树 Quiz 简单测验
二叉查找树 BinarySearchTree
AVL 树 是一种平衡二叉查找树,简称平衡二叉树
红黑树 RedBlackTree
二叉堆 binary heap
第五章 树及变体 ///
/ 1.1 Quiz 简单测验 树节点为问题或答案。叶子节点为答案。
Do you have experience in developing applications?
No
Have you completed the university?
No
Will you find some time during the semester?
Yes
Apply for our long-time internship program!
/* Main 程序 */
using System;
using System.Collections.Generic;
namespace Quiz
{
class Program
{
static void Main(string[] args)
{
BinaryTree<QuizItem> tree = GetTree();
BinaryTreeNode<QuizItem> node = tree.Root;
while (node != null)
{ //判断是问题还是答案
if (node.Left != null || node.Right != null)//问题
{
Console.WriteLine(node.Data.Text);//输出问题
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.Y:
WriteAnswer(" Yes");//选择左侧子节点
node = node.Left;
break;
case ConsoleKey.N:
WriteAnswer(" No");//选择右侧子节点
node = node.Right;
break;
}
}
else//答案
{
WriteAnswer(node.Data.Text); //输出答案
node = null;//跳出循环
}
}
Console.ReadLine();
}
//构建quiz树
private static BinaryTree<QuizItem> GetTree()
{
BinaryTree<QuizItem> tree = new BinaryTree<QuizItem>();//二叉树实例
tree.Root = new BinaryTreeNode<QuizItem>() //根节点
{
Data = new QuizItem("Do you have experience in developing applications?"),//根节点数据
//孩子节点
Children = new List<TreeNode<QuizItem>>()
{
new BinaryTreeNode<QuizItem>() //左侧孩子节点
{
Data = new QuizItem("Have you worked as a developer for more than 5 years?"),
Children = new List<TreeNode<QuizItem>>()
{
new BinaryTreeNode<QuizItem>()//左侧孩子节点
{
Data = new QuizItem("Apply as a senior developer!")//答案
},
new BinaryTreeNode<QuizItem>()//右侧孩子节点
{
Data = new QuizItem("Apply as a middle developer!")//答案
}
}
},
new BinaryTreeNode<QuizItem>()//右侧孩子节点
{
Data = new QuizItem("Have you completed the university?"),
Children = new List<TreeNode<QuizItem>>()
{
new BinaryTreeNode<QuizItem>()//左侧孩子节点
{
Data = new QuizItem("Apply for a junior developer!")//答案
},
new BinaryTreeNode<QuizItem>()//右侧孩子节点
{
Data = new QuizItem("Will you find some time during the semester?"),
Children = new List<TreeNode<QuizItem>>()
{
new BinaryTreeNode<QuizItem>()//左侧孩子节点
{
Data = new QuizItem("Apply for our long-time internship program!")//答案
},
new BinaryTreeNode<QuizItem>()//右侧孩子节点
{
Data = new QuizItem("Apply for summer internship program!")//答案
}
}
}
}
}
}
};
tree.Count = 9;//节点总数
return tree;
}
//输出文本
private static void WriteAnswer(string text)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(text);
Console.ForegroundColor = ConsoleColor.Gray;
}
}
}
/* QuizItem.cs */
public class QuizItem //节点
{
public string Text { get; set; } //问题或答案
public QuizItem(string text) => Text = text;//构造函数
}
/* 树的遍历方法 TraversalEnum.cs */
public enum TraversalEnum //遍历树的三种方式
{
PREORDER,
INORDER,
POSTORDER
}
/* TreeNode.cs 树节点 */
using System;
using System.Collections.Generic;
namespace Quiz
{
public class TreeNode<T> //树节点
{
public T Data { get; set; } //数据:可以是类的实例 例如QuizItem
public TreeNode<T> Parent { get; set; } //父节点
public List<TreeNode<T>> Children { get; set; } //孩子节点
public int GetHeight()//获取节点的高度
{
int height = 1;
TreeNode<T> current = this;
while (current.Parent != null)
{
height++;
current = current.Parent;
}
return height;
}
}
}
/* 二叉树节点 BinaryTreeNode.cs */
using System;
using System.Collections.Generic;
namespace Quiz
{
public class BinaryTreeNode<T> : TreeNode<T> //二叉树节点
{
public BinaryTreeNode() => Children = new List<TreeNode<T>>() { null, null };//构造函数:初始化左右孩子节点为null
public BinaryTreeNode<T> Left//左侧子节点
{
get { return (BinaryTreeNode<T>)Children[0] ?? null; }
set { Children[0] = value; }
}
public BinaryTreeNode<T> Right //右侧子节点
{
get { return (BinaryTreeNode<T>)Children[1] ?? null; }
set { Children[1] = value; }
}
}
}
/* 二叉树 BinaryTree.cs */
using System;
using System.Collections.Generic;
namespace Quiz
{
public class BinaryTree<T> //二叉树
{
public BinaryTreeNode<T> Root { get; set; } //根节点
public int Count { get; set; }//节点总数
//遍历树: 遍历方式
public List<BinaryTreeNode<T>> Traverse(TraversalEnum mode)
{
List<BinaryTreeNode<T>> nodes = new List<BinaryTreeNode<T>>();//
switch (mode)
{
case TraversalEnum.PREORDER:
TraversePreOrder(Root, nodes);
break;
case TraversalEnum.INORDER:
TraverseInOrder(Root, nodes);
break;
case TraversalEnum.POSTORDER:
TraversePostOrder(Root, nodes);
break;
}
return nodes;
}
private void TraversePreOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
result.Add(node);
TraversePreOrder(node.Left, result);
TraversePreOrder(node.Right, result);
}
}
private void TraverseInOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
TraverseInOrder(node.Left, result);
result.Add(node);
TraverseInOrder(node.Right, result);
}
}
private void TraversePostOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
TraversePostOrder(node.Left, result);
TraversePostOrder(node.Right, result);
result.Add(node);
}
}
//树高
public int GetHeight()
{
int height = 0;
foreach (BinaryTreeNode<T> node in Traverse(TraversalEnum.PREORDER))
{
height = Math.Max(height, node.GetHeight());
}
return height;
}
}
}
// 2.1 二叉查找树 BinarySearchTree.cs
3
The BST with three nodes (50, 100, 150):
100
┌----+----┐
50 150
5
The BST after adding two nodes (75, 125):
100
┌---------+---------┐
50 150
+----┐ ┌----+
75 125
10
The BST after adding five nodes (25, 175, 90, 110, 135):
100
┌-------------------+-------------------┐
50 150
┌---------+---------┐ ┌---------+---------┐
25 75 125 175
+----┐ ┌----+----┐
90 110 135
9
The BST after removing the node 25:
100
┌-------------------+-------------------┐
50 150
+---------┐ ┌---------+---------┐
75 125 175
+----┐ ┌----+----┐
90 110 135
8
The BST after removing the node 50:
100
┌-------------------+-------------------┐
75 150
+---------┐ ┌---------+---------┐
90 125 175
┌----+----┐
110 135
7
The BST after removing the node 100:
110
┌-------------------+-------------------┐
75 150
+---------┐ ┌---------+---------┐
90 125 175
+----┐
135
Pre-order traversal: 110, 75, 90, 150, 125, 135, 175
In-order traversal: 75, 90, 110, 125, 135, 150, 175
Post-order traversal: 90, 75, 135, 125, 175, 150, 1106
---
110
┌-------------------+-------------------┐
75 150
┌---------+---------┐
125 175
+----┐
135
5
---
110
+-------------------┐
150
┌---------+---------┐
125 175
+----┐
135
4
---
110
+---------┐
150
┌----+----┐
135 175
3
---
150
┌----+----┐
135 175
2
---
150
┌----+
135
1
---
150
0
---
/* TreeNodes.cs 树节点类*/
public class TreeNode<T>
{
public T Data { get; set; } //数据
public List<TreeNode<T>> Children { get; set; } //孩子节点的引用
}
/* Tree 树 */
public class Tree<T>
{
public TreeNode<T> Root { get; set; }
}
/* 二叉树节点类 */
{ //二叉树节点
public class BinaryTreeNode<T> : TreeNode<T>
{ //构造函数:初始化左右孩子节点为null
public BinaryTreeNode() => Children = new List<TreeNode<T>>() { null, null };
public BinaryTreeNode<T> Parent { get; set; }//父节点
public BinaryTreeNode<T> Left //左侧子节点
{
get { return (BinaryTreeNode<T>)Children[0]; }
set { Children[0] = value; }
}
public BinaryTreeNode<T> Right//右侧子节点
{
get { return (BinaryTreeNode<T>)Children[1]; }
set { Children[1] = value; }
}
public int GetHeight()//获取高度
{
int height = 1;
BinaryTreeNode<T> current = this;
while (current.Parent != null)
{
height++;
current = current.Parent;
}
return height;
}
}
}
/* BinaryTree.cs 二叉树类*/
using System;
using System.Collections.Generic;
namespace Trees
{
public class BinaryTree<T>
{
public BinaryTreeNode<T> Root { get; set; }//二叉树根节点
public int Count { get; set; }
//遍历二叉树,返回 二叉树节点列表
public List<BinaryTreeNode<T>> Traverse(TraversalEnum mode)
{
List<BinaryTreeNode<T>> nodes = new List<BinaryTreeNode<T>>();//
switch (mode)
{
case TraversalEnum.PREORDER:
TraversePreOrder(Root, nodes);
break;
case TraversalEnum.INORDER:
TraverseInOrder(Root, nodes);
break;
case TraversalEnum.POSTORDER:
TraversePostOrder(Root, nodes);
break;
}
return nodes;
}
//先当前,后左,再右
private void TraversePreOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
result.Add(node);
TraversePreOrder(node.Left, result);
TraversePreOrder(node.Right, result);
}
}
//先左,后当前,再右
private void TraverseInOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
TraverseInOrder(node.Left, result);
result.Add(node);
TraverseInOrder(node.Right, result);
}
}
//先左,后右,再当前
private void TraversePostOrder(BinaryTreeNode<T> node, List<BinaryTreeNode<T>> result)
{
if (node != null)
{
TraversePostOrder(node.Left, result);
TraversePostOrder(node.Right, result);
result.Add(node);
}
}
//所有节点最大高度
public int GetHeight()
{
int height = 0;
foreach (BinaryTreeNode<T> node in Traverse(TraversalEnum.PREORDER))
{
height = Math.Max(height, node.GetHeight());
}
return height;
}
}
}
/* BinarySearchTree.cs 二叉查找树 */
using System;
namespace Trees
{
public class BinarySearchTree<T> : BinaryTree<T>
where T : IComparable //存储在节点的数据类型可以被比较
{
//查找元素
public bool Contains(T data)
{
BinaryTreeNode<T> node = Root;//从根节点开始
while (node != null)
{
int result = data.CompareTo(node.Data);
if (result == 0)
{
return true;//找到
}
else if (result < 0)//搜索值小于当前节点值
{
node = node.Left;//取当前节点的左侧子节点
}
else//右侧
{
node = node.Right;
}
}
return false;
}
//插入节点
public void Add(T data)
{
BinaryTreeNode<T> parent = GetParentForNewNode(data); //获取父节点
BinaryTreeNode<T> node = new BinaryTreeNode<T>() { Data = data, Parent = parent };//创建新节点
if (parent == null)
{
Root = node;
}
else if (data.CompareTo(parent.Data) < 0) //新节点的值比父节点小
{
parent.Left = node;//作为父节点的左侧子节点
}
else
{
parent.Right = node;
}
Count++;//树节点总数++
}
//为新节点寻找父节点
private BinaryTreeNode<T> GetParentForNewNode(T data)
{
BinaryTreeNode<T> current = Root;//从根节点开始
BinaryTreeNode<T> parent = null;
while (current != null)
{
parent = current;//初始化父节点为当前节点
int result = data.CompareTo(current.Data);//比较新节点与当前父节点的值
if (result == 0)
{ //已经存在节点值
throw new ArgumentException($"The node {data} already exists.");
}
else if (result < 0)
{
current = current.Left;//新节点值比当前节点值小,选择当前节点的子节点作为新的当前节点。 如果当前节点为空,则跳出循环。结果为上一当前节点。
}
else
{
current = current.Right;
}
}
return parent;
}
//移除数据: 可能有重复数据。需要递归
public void Remove(T data)
{
Remove(Root, data);
}
//以node为根节点 查找存储data数据的节点并删除
private void Remove(BinaryTreeNode<T> node, T data)
{
if (data.CompareTo(node.Data) < 0)
{
Remove(node.Left, data);//在左侧子节点递归
}
else if (data.CompareTo(node.Data) > 0)
{
Remove(node.Right, data);//在右侧子节点递归
}
else//找到存储改数据的节点
{
if (node.Left == null && node.Right == null) //无子节点,将该节点替换为null
{
ReplaceInParent(node, null);
Count--;//更新树的节点数
}
else if (node.Right == null)//无右侧子节点,将该节点替换为左侧子节点
{
ReplaceInParent(node, node.Left);
Count--;
}
else if (node.Left == null)//无左侧子节点,将该节点替换为右侧子节点
{
ReplaceInParent(node, node.Right);
Count--;
}
else//该节点包含左右子节点
{
BinaryTreeNode<T> successor = FindMinimumInSubtree(node.Right);//找到右侧子树的最小节点
node.Data = successor.Data;//更新子树根节点的数据为 右侧子树的最小值
Remove(successor, successor.Data);//移除右侧子树的最小节点
}
}
}
//节点替换
private void ReplaceInParent(BinaryTreeNode<T> node, BinaryTreeNode<T> newNode)
{
if (node.Parent != null)
{
if (node.Parent.Left == node)//要替换的节点是其父节点左侧子节点
{
node.Parent.Left = newNode;//替换为新节点
}
else
{
node.Parent.Right = newNode;
}
}
else
{
Root = newNode;
}
if (newNode != null)
{
newNode.Parent = node.Parent; //更新新节点的父节点
}
}
//找到子树最小节点
private BinaryTreeNode<T> FindMinimumInSubtree(BinaryTreeNode<T> node)
{
while (node.Left != null)
{
node = node.Left;
}
return node;
}
}
}
/* 二叉查找树示例 及其可视化 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Trees
{
class Program
{
private const int COLUMN_WIDTH = 5;//数据最大长度
public static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
BinarySearchTree<int> tree = new BinarySearchTree<int>();//创建一个 二叉查找树
tree.Root = new BinaryTreeNode<int>() { Data = 100 }; //根节点 100
tree.Root.Left = new BinaryTreeNode<int>() { Data = 50, Parent = tree.Root };//左侧节点 50
tree.Root.Right = new BinaryTreeNode<int>() { Data = 150, Parent = tree.Root };//右侧节点150
tree.Count = 3;
VisualizeTree(tree, "The BST with three nodes (50, 100, 150):");
tree.Add(75);
tree.Add(125);
VisualizeTree(tree, "The BST after adding two nodes (75, 125):");
tree.Add(25);
tree.Add(175);
tree.Add(90);
tree.Add(110);
tree.Add(135);
VisualizeTree(tree, "The BST after adding five nodes (25, 175, 90, 110, 135):");
tree.Remove(25);
VisualizeTree(tree, "The BST after removing the node 25:");
tree.Remove(50);
VisualizeTree(tree, "The BST after removing the node 50:");
tree.Remove(100);
VisualizeTree(tree, "The BST after removing the node 100:");
//三种遍历方法遍历二叉查找树
Console.Write("Pre-order traversal:\t");
Console.Write(string.Join(", ", tree.Traverse(TraversalEnum.PREORDER).Select(n => n.Data)));
Console.Write("\nIn-order traversal:\t");
Console.Write(string.Join(", ", tree.Traverse(TraversalEnum.INORDER).Select(n => n.Data)));
Console.Write("\nPost-order traversal:\t");
Console.Write(string.Join(", ", tree.Traverse(TraversalEnum.POSTORDER).Select(n => n.Data)));
tree.Remove(90);
VisualizeTree(tree, "---");
tree.Remove(75);
VisualizeTree(tree, "---");
tree.Remove(125);
VisualizeTree(tree, "---");
tree.Remove(110);
VisualizeTree(tree, "---");
tree.Remove(175);
VisualizeTree(tree, "---");
tree.Remove(135);
VisualizeTree(tree, "---");
tree.Remove(150);
VisualizeTree(tree, "---");
Console.ReadLine();
}
//可视化树
private static void VisualizeTree(BinarySearchTree<int> tree, string caption)
{
Console.WriteLine(tree.Count);
char[][] console = InitializeVisualization(tree, out int width);
VisualizeNode(tree.Root, 0, width / 2, console, width);
Console.WriteLine(caption);
foreach (char[] row in console)
{
Console.WriteLine(row);
}
}
//初始化可视化
private static char[][] InitializeVisualization(BinarySearchTree<int> tree, out int width)
{
int height = tree.GetHeight();//树的高度 几层
width = (int)Math.Pow(2, height) - 1;//树宽度
char[][] console = new char[height * 2][];//2倍的高度
for (int i = 0; i < height * 2; i++)//
{
console[i] = new char[COLUMN_WIDTH * width];
}
return console;
}
//可视化节点 tree.Root, 0, width / 2, console, width
private static void VisualizeNode(BinaryTreeNode<int> node, int row, int column, char[][] console, int width)
{
if (node != null)
{
char[] chars = node.Data.ToString().ToCharArray();//数据字符串
int margin = (COLUMN_WIDTH - chars.Length) / 2;
for (int i = 0; i < chars.Length; i++)
{
console[row][COLUMN_WIDTH * column + i + margin] = chars[i];
}
int columnDelta = (width + 1) / (int)Math.Pow(2, node.GetHeight() + 1);//下一层 子树节点位置相对父节点位置列数改变量
VisualizeNode(node.Left, row + 2, column - columnDelta, console, width);//左侧节点可视化
VisualizeNode(node.Right, row + 2, column + columnDelta, console, width);//右侧节点可视化
DrawLineLeft(node, row, column, console, columnDelta);//左侧分支线条
DrawLineRight(node, row, column, console, columnDelta);//右侧分支线条
}
}
//绘制右侧线条 node:父节点
private static void DrawLineRight(BinaryTreeNode<int> node, int row, int column, char[][] console, int columnDelta)
{
if (node.Right != null)
{
int startColumnIndex = COLUMN_WIDTH * column + 2;
int endColumnIndex = COLUMN_WIDTH * (column + columnDelta) + 2;
for (int x = startColumnIndex + 1; x < endColumnIndex; x++)
{
console[row + 1][x] = '-';//
}
console[row + 1][startColumnIndex] = '+';//中间 + 号
console[row + 1][endColumnIndex] = '\u2510'; //右侧终止线条
}
}
//绘制左侧线条
private static void DrawLineLeft(BinaryTreeNode<int> node, int row, int column, char[][] console, int columnDelta)
{
if (node.Left != null)
{
int startColumnIndex = COLUMN_WIDTH * (column - columnDelta) + 2;
int endColumnIndex = COLUMN_WIDTH * column + 2;
for (int x = startColumnIndex + 1; x < endColumnIndex; x++)
{
console[row + 1][x] = '-';
}
console[row + 1][startColumnIndex] = '\u250c';
console[row + 1][endColumnIndex] = '+';
}
}
}
}
3.1 AVL 树 是一种平衡二叉查找树,简称平衡二叉树。查找速度快
In-order: 1, 2, 3, 4, 5, 6, 7, 8, 9
Post-order: 1, 3, 2, 5, 7, 9, 8, 6, 4
Breadth-first: 4, 2, 6, 1, 3, 5, 8, 7, 9
Children of node 8 (height = 2): 7 and 9.
/*程序 需要引用Adjunct nuget包*/
using System;
using System.Collections.Generic;
using System.DataStructures;
using System.Text;
using System.Threading.Tasks;
namespace AVL
{
class Program
{
static void Main(string[] args)
{
AvlTree<int> tree = new AvlTree<int>();
for (int i = 1; i < 10; i++)
{
tree.Add(i);
}
Console.WriteLine("In-order: " + string.Join(", ", tree.GetInorderEnumerator()));
Console.WriteLine("Post-order: " + string.Join(", ", tree.GetPostorderEnumerator()));
Console.WriteLine("Breadth-first: " + string.Join(", ", tree.GetBreadthFirstEnumerator()));
AvlTreeNode<int> node = tree.FindNode(8);
Console.WriteLine($"Children of node {node.Value} (height = {node.Height}): {node.Left.Value} and {node.Right.Value}.");
Console.ReadLine();
}
}
}
/// 4.1 Red-black trees 红黑树 RedBlackTree 比BSTs更好。
Does value exist? yes
9 elements in the range 1-10
Values: 1, 2, 3, 4, 5, 6, 7, 8, 10
Values: 1 2 3 4 5 6 7 8 10
/* Main 程序 添加nuget包 TreeLib */
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TreeLib;
namespace RedBlackTree
{
class Program
{
static void Main(string[] args)
{
RedBlackTreeList<int> tree = new RedBlackTreeList<int>();//红黑树:int节点
for (int i = 1; i <= 10; i++)
{
tree.Add(i);
}
tree.Remove(9);
bool contains = tree.ContainsKey(5); //判断树是否包含键
Console.WriteLine("Does value exist? " + (contains ? "yes" : "no"));
uint count = tree.Count; //树的节点数
tree.Greatest(out int greatest);//获取树的最大值
tree.Least(out int least);//获取树的最小值
Console.WriteLine($"{count} elements in the range {least}-{greatest}");
Console.WriteLine("Values: " + string.Join(", ", tree.GetEnumerable()));
Console.Write("Values: ");
foreach (EntryList<int> node in tree)//遍历节点
{
Console.Write(node + " ");
}
Console.ReadLine();
Console.ReadLine();
}
}
}
5.1 二叉堆 添加 PommaLabs.Hippie nuget库
Unsorted: 50, 33, 78, -23, 90, 41
Sorted: -23, 33, 41, 50, 78, 90
/* Main 程序 */
//using DIBRIS.Hippie;
using Heap;
using PommaLabs.Hippie;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Heap
{
class Program
{
static void Main(string[] args)
{
List<int> unsorted = new List<int>() { 50, 33, 78, -23, 90, 41 }; // int无序列表
MultiHeap<int> heap = HeapFactory.NewBinaryHeap<int>();//二叉堆实例
unsorted.ForEach(i => heap.Add(i)); //将int列表中每一项添加到堆
Console.WriteLine("Unsorted: " + string.Join(", ", unsorted)); //无序列表
List<int> sorted = new List<int>(heap.Count);//初始化有序列表
while (heap.Count > 0)
{
sorted.Add(heap.RemoveMin()); //将堆中最小值依次添加到有序列表
}
Console.WriteLine("Sorted: " + string.Join(", ", sorted));
Console.ReadLine();
}
}
}