二叉树
介绍
- 每个节点最多只能有两个子节点
- 子节点分为左子节点和右子节点
遍历
[注]:可以把打印当前节点信息的位置当作一种方法来记忆, 例如先打印当前节点信息然后遍历左右子节点即为前序遍历
结点代码
class Node
{
public int ID { get; set; }
public string Name { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(int id, string name)
{
this.ID = id;
this.Name = name;
}
public override string ToString()
{
return $"Node: [ID = {this.ID}, Name = {this.Name}]";
}
}
前序遍历
static void PreOrder(Node root)
{
if (root == null)
return;
Console.WriteLine(root);
PreOrder(root.Left);
PreOrder(root.Right);
}
中序遍历
static void InfixOrder(Node root)
{
if (root == null)
return;
InfixOrder(root.Left);
Console.WriteLine(root);
InfixOrder(root.Right);
}
后序遍历
static void PostOrder(Node root)
{
if (root == null)
return;
PostOrder(root.Left);
PostOrder(root.Right);
Console.WriteLine(root);
}
查找
查找分为通过前序遍历查找,中序遍历查找,后序遍历查找,思路大致相同
[注]:此代码写在Node的类体中
- 判断当前节点是否是带查找节点
- 如果是,则返回当前节点
- 如果不是,则按前序遍历思路遍历下一节点,继续比对
前序
public Node PreOrderSearch(int id)
{
if (this.ID == id)
return this;
Node res = null;
if (this.Left != null)
res = this.Left.PreOrderSearch(id);
//说明在左子树找到
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PreOrderSearch(id);
return res;
}
中序
public Node InfixOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.InfixOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
if (this.Right != null)
res = this.Right.InfixOrderSearch(id);
return res;
}
后序
public Node PostOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.PostOrderSearch(id);
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PostOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
return null;
}
删除
规定:
- 如果删除的节点是叶子节点,则删除该节点
- 如果删除的节点是非叶子节点,则删除该子树
思路:
- 判断是否为空树,或只有一个root节点
- 二叉树是单向的,故实际要找的是待删除节点的父亲节点
- 如果当前节点的左子节点是待删除结点,则 this.Left = null
- 如果当前节点的右子节点是待删除结点,则 this.Right = null
- 如果3,4两步未删除节点,递归左子树 递归右子树
代码
public void Delete(int id)
{
if (this.Left != null && this.Left.ID == id)
{
this.Left = null;
return;
}
if (this.Right != null && this.Right.ID == id)
{
this.Right = null;
return;
}
if (this.Left != null)
this.Left.Delete(id);
if (this.Right != null)
this.Right.Delete(id);
}
顺序存储二叉树
介绍
- 顺序二叉树通常只考虑完全二叉树
- 第n个元素的左子节点为 2n+1
- 第n个元素的右子节点为 2n+2
- 第n个元素的父亲节点是 (n-1)/2
代码
class ArrayBinaryTree
{
private int[] arr; //存储节点的数组
public ArrayBinaryTree(int[] arr)
{
this.arr = arr;
}
/// <summary>
///
/// </summary>
/// <param name="index">数组的下标</param>
public void PreOrder(int index = 0)
{
//如果数组为空或者arr.length == 0
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
Console.WriteLine(arr[index]);
//向左递归
if (index * 2 + 1 < arr.Length)
{
PreOrder(2 * index + 1);
}
//向右递归
if (index * 2 + 2 < arr.Length)
{
PreOrder(index * 2 + 2);
}
}
public void InfixOrder(int index = 0)
{
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
if (2 * index + 1 < arr.Length)
{
InfixOrder(2 * index + 1);
}
Console.WriteLine(arr[index]);
if (2 * index + 2 < arr.Length)
{
InfixOrder(2 * index + 2);
}
}
public void PostOrder(int index = 0)
{
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
if (index * 2 + 1 < arr.Length)
{
PostOrder(index * 2 + 1);
}
if (index * 2 + 2 < arr.Length)
{
PostOrder(index * 2 + 2);
}
Console.WriteLine(arr[index]);
}
}
线索化二叉树
介绍
- n个节点的二叉链表中含有 n+1 个空指针域。利用二叉链表的空指针域,存放指向该节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针称为“线索”)
[注]: 二叉树中有n个节点,则有 2n 个指针域。除了根节点外每个节点占一个指针域,故含有 2n-(n-1) = n+1 个空指针域 - 这种加上了线索的二叉链表称为线索链表,对应的二叉树称为线索二叉树(Threaded Binarytree) 根据线索性质的不同,线索二叉树可以分为前序线索二叉树,中序线索二叉树和后序线索二叉树三种。
当线索化二叉树后,node节点的属性Left 和Right 有如下情况
- Left指向的是左子树,也可能是指向的前驱节点
- Right指向的是右子树,也可能是指向后继节点
代码
此时需向Node中添加两个属性 LeftType 和 RightType
//1.如果leftType是0 表示指向的是左子树,如果是1则表示指向前驱节点
//2.如果rightType是0 表示指向的是右子树,如果是1则表示指向后继节点
class ThreadedBinaryTree
{
private Node root;
//为了实现线索化,需要创建指向当前结点的前驱结点的指针
private Node pre;
public Node Root
{
set { root = value; }
}
public void ThreadNodes()
{
this.ThreadNodes(root);
}
//遍历线索化二叉树
public void TraverseThreadedTree()
{
Node node = root;
while (node != null)
{
//循环找到leftType == 1 的结点
while (node.LeftType == 0)
{
node = node.Left;
}
//打印当前节点
Console.WriteLine(node);
//如果当前结点的右指针指向后继结点,就一直输出
while (node.RightType == 1)
{
node = node.Right;
Console.WriteLine(node);
}
//替换遍历的结点
node = node.Right;
}
}
/// <summary>
///
/// </summary>
/// <param name="node">线索化当前结点</param>
public void ThreadNodes(Node node)
{
if (node == null)
return;
//(1) 线索化左子树
ThreadNodes(node.Left);
//(2) 线索化当前结点
//处理当前结点的前驱
if (node.Left == null)
{
//让当前结点的左指针指向前驱结点,并修改leftType
node.Left = pre;
node.LeftType = 1;
}
//处理后继结点
if (pre != null && pre.Right == null)
{
pre.Right = node;
pre.RightType = 1;
}
//后移pre
pre = node;
//(3) 线索化右子树
ThreadNodes(node.Right);
}
}
补充全部代码
二叉树
using System;
namespace DeleteMethod
{
class Program
{
static void Main(string[] args)
{
BinaryTree binaryTree = new BinaryTree();
Node node1 = new Node(1, "Roy");
Node node2 = new Node(2, "Summy");
Node node3 = new Node(3, "Shane");
Node node4 = new Node(4, "mezz");
node1.Left = node2;
node1.Right = node3;
node3.Right = node4;
binaryTree.Root = node1;
Console.WriteLine("删除前");
binaryTree.PreOrder();
Console.WriteLine("\n\n");
Console.WriteLine("删除后");
binaryTree.Delete(4);
binaryTree.PreOrder();
}
}
class Node
{
public int ID { get; set; }
public string Name { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(int id, string name)
{
this.ID = id;
this.Name = name;
}
public void PreOrder()
{
Console.WriteLine(this);
if (this.Left != null)
this.Left.PreOrder();
if (this.Right != null)
this.Right.PreOrder();
}
public void InfixOrder()
{
if (this.Left != null)
this.Left.InfixOrder();
Console.WriteLine(this);
if (this.Right != null)
this.Right.InfixOrder();
}
public void PostOrder()
{
if (this.Left != null)
this.Left.PostOrder();
if (this.Right != null)
this.Right.PostOrder();
Console.WriteLine(this);
}
public Node PreOrderSearch(int id)
{
if (this.ID == id)
return this;
Node res = null;
if (this.Left != null)
res = this.Left.PreOrderSearch(id);
//说明在左子树找到
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PreOrderSearch(id);
return res;
}
public Node InfixOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.InfixOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
if (this.Right != null)
res = this.Right.InfixOrderSearch(id);
return res;
}
public Node PostOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.PostOrderSearch(id);
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PostOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
return null;
}
public void Delete(int id)
{
if (this.Left != null && this.Left.ID == id)
{
this.Left = null;
return;
}
if (this.Right != null && this.Right.ID == id)
{
this.Right = null;
return;
}
if (this.Left != null)
this.Left.Delete(id);
if (this.Right != null)
this.Right.Delete(id);
}
public override string ToString()
{
return $"Node [ID = {ID}, Name = {Name}].";
}
}
class BinaryTree
{
private Node root;
public Node Root
{
set { root = value; }
}
public void PreOrder()
{
if (this.root != null)
this.root.PreOrder();
else
Console.WriteLine("二叉树为空");
}
public void InfixOrder()
{
if (this.root != null)
this.root.InfixOrder();
else
Console.WriteLine("二叉树为空");
}
public void PostOrder()
{
if (this.root != null)
this.root.PostOrder();
else
Console.WriteLine("二叉树为空");
}
public Node PreOrderSearch(int id)
{
if (root != null)
return root.PreOrderSearch(id);
return null;
}
public Node InfixOrderSearch(int id)
{
if (root != null)
return root.InfixOrderSearch(id);
return null;
}
public Node PostOrderSearch(int id)
{
if (root != null)
return root.PostOrderSearch(id);
return null;
}
public void Delete(int id)
{
if (root != null)
{
if (root.ID == id)
root = null;
else
root.Delete(id);
}
else
Console.WriteLine("树为空,无法删除");
}
}
}
顺序存储二叉树
using System;
namespace ArrBinaryTree
{
class Program
{
static void Main(string[] args)
{
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree(arr);
arrayBinaryTree.PreOrder();
Console.WriteLine();
arrayBinaryTree.InfixOrder();
Console.WriteLine();
arrayBinaryTree.PostOrder();
}
}
class ArrayBinaryTree
{
private int[] arr; //存储节点的数组
public ArrayBinaryTree(int[] arr)
{
this.arr = arr;
}
/// <summary>
///
/// </summary>
/// <param name="index">数组的下标</param>
public void PreOrder(int index = 0)
{
//如果数组为空或者arr.length == 0
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
Console.WriteLine(arr[index]);
//向左递归
if (index * 2 + 1 < arr.Length)
{
PreOrder(2 * index + 1);
}
//向右递归
if (index * 2 + 2 < arr.Length)
{
PreOrder(index * 2 + 2);
}
}
public void InfixOrder(int index = 0)
{
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
if (2 * index + 1 < arr.Length)
{
InfixOrder(2 * index + 1);
}
Console.WriteLine(arr[index]);
if (2 * index + 2 < arr.Length)
{
InfixOrder(2 * index + 2);
}
}
public void PostOrder(int index = 0)
{
if (arr == null || arr.Length == 0)
{
Console.WriteLine("数组为空");
return;
}
if (index * 2 + 1 < arr.Length)
{
PostOrder(index * 2 + 1);
}
if (index * 2 + 2 < arr.Length)
{
PostOrder(index * 2 + 2);
}
Console.WriteLine(arr[index]);
}
}
}
线索二叉树
using System;
namespace ThreadedBinaryTreeDemo
{
class Program
{
static void Main(string[] args)
{
Node root = new Node(1, "Roy");
Node node2 = new Node(3, "Jack");
Node node3 = new Node(6, "Smith");
Node node4 = new Node(8, "Mary");
Node node5 = new Node(10, "Tim");
Node node6 = new Node(14, "Summy");
root.Left = node2;
root.Right = node3;
node2.Left = node4;
node2.Right = node5;
node3.Left = node6;
ThreadedBinaryTree tree = new ThreadedBinaryTree();
tree.Root = root;
tree.ThreadNodes();
tree.TraverseThreadedTree();
}
}
class Node
{
public int ID { get; set; }
public string Name { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
//说明:
//1.如果leftType是0 表示指向的是左子树,如果是1则表示指向前驱节点
//2.如果rightType是0 表示指向的是右子树,如果是1则表示指向后继节点
public int LeftType { get; set; }
public int RightType { get; set; }
public Node(int id, string name)
{
this.ID = id;
this.Name = name;
}
public void PreOrder()
{
Console.WriteLine(this);
if (this.Left != null)
this.Left.PreOrder();
if (this.Right != null)
this.Right.PreOrder();
}
public void InfixOrder()
{
if (this.Left != null)
this.Left.InfixOrder();
Console.WriteLine(this);
if (this.Right != null)
this.Right.InfixOrder();
}
public void PostOrder()
{
if (this.Left != null)
this.Left.PostOrder();
if (this.Right != null)
this.Right.PostOrder();
Console.WriteLine(this);
}
public Node PreOrderSearch(int id)
{
if (this.ID == id)
return this;
Node res = null;
if (this.Left != null)
res = this.Left.PreOrderSearch(id);
//说明在左子树找到
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PreOrderSearch(id);
return res;
}
public Node InfixOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.InfixOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
if (this.Right != null)
res = this.Right.InfixOrderSearch(id);
return res;
}
public Node PostOrderSearch(int id)
{
Node res = null;
if (this.Left != null)
res = this.Left.PostOrderSearch(id);
if (res != null)
return res;
if (this.Right != null)
res = this.Right.PostOrderSearch(id);
if (res != null)
return res;
if (this.ID == id)
return this;
return null;
}
public void Delete(int id)
{
if (this.Left != null && this.Left.ID == id)
{
this.Left = null;
return;
}
if (this.Right != null && this.Right.ID == id)
{
this.Right = null;
return;
}
if (this.Left != null)
this.Left.Delete(id);
if (this.Right != null)
this.Right.Delete(id);
}
public override string ToString()
{
return $"Node [ID = {ID}, Name = {Name}].";
}
}
class ThreadedBinaryTree
{
private Node root;
//为了实现线索化,需要创建指向当前结点的前驱结点的指针
private Node pre;
public Node Root
{
set { root = value; }
}
public void ThreadNodes()
{
this.ThreadNodes(root);
}
//遍历线索化二叉树
public void TraverseThreadedTree()
{
Node node = root;
while (node != null)
{
//循环找到leftType == 1 的结点
while (node.LeftType == 0)
{
node = node.Left;
}
//打印当前节点
Console.WriteLine(node);
//如果当前结点的右指针指向后继结点,就一直输出
while (node.RightType == 1)
{
node = node.Right;
Console.WriteLine(node);
}
//替换遍历的结点
node = node.Right;
}
}
/// <summary>
///
/// </summary>
/// <param name="node">线索化当前结点</param>
public void ThreadNodes(Node node)
{
if (node == null)
return;
//(1) 线索化左子树
ThreadNodes(node.Left);
//(2) 线索化当前结点
//处理当前结点的前驱
if (node.Left == null)
{
//让当前结点的左指针指向前驱结点,并修改leftType
node.Left = pre;
node.LeftType = 1;
}
//处理后继结点
if (pre != null && pre.Right == null)
{
pre.Right = node;
pre.RightType = 1;
}
//后移pre
pre = node;
//(3) 线索化右子树
ThreadNodes(node.Right);
}
public void PreOrder()
{
if (this.root != null)
this.root.PreOrder();
else
Console.WriteLine("二叉树为空");
}
public void InfixOrder()
{
if (this.root != null)
this.root.InfixOrder();
else
Console.WriteLine("二叉树为空");
}
public void PostOrder()
{
if (this.root != null)
this.root.PostOrder();
else
Console.WriteLine("二叉树为空");
}
public Node PreOrderSearch(int id)
{
if (root != null)
return root.PreOrderSearch(id);
return null;
}
public Node InfixOrderSearch(int id)
{
if (root != null)
return root.InfixOrderSearch(id);
return null;
}
public Node PostOrderSearch(int id)
{
if (root != null)
return root.PostOrderSearch(id);
return null;
}
public void Delete(int id)
{
if (root != null)
{
if (root.ID == id)
root = null;
else
root.Delete(id);
}
else
Console.WriteLine("树为空,无法删除");
}
}
}