您的位置:首页 > 编程语言

二叉树学习:从零基础到代码实现

2017-07-28 17:54 369 查看
二叉树的定义:

二叉树是节点的有限集合,该集合或者为空集,或者是由一个根和两颗互不相交的、称为该根的左子树和右子树的二叉树组成。

二叉树的性质:

1、二叉树第i(i>=1)层上至多有2^(i-1)个结点。

2、高度为h的二叉树至多有2^(h)-1个结点。

3、包含n个元素的二叉树的高度至少为log2(n+1)向上取整。

4、任意一颗二叉树中,若叶节点的个数为n0,度为2的结点的个数为n2,则必有n0=n2+1.

5、高度为h的二叉树恰好有2^(h)-1个结点时称为满二叉树。满二叉树是完全二叉树,也是扩充二叉树。

6、一颗二叉树中,只有最下面两层结点的度可以小于2,并且最下面一层的叶节点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。

7、扩充二叉树也称2-树,扩充二叉树中除叶子结点外,其余结点都必须有两个孩子。

8、具有n个结点的完全二叉树的高度为log2(n+1)向上取整。

9、假定对一颗有n个结点的完全二叉树中的结点,按从上到下,从左到右的顺序,从0到n-1编号,设树中某个结点的序号为i,0<=i<n,则有以下关系成立:(下面建二叉树的时候会用到)

(1)当i=0时,该节点为二叉树的根。

(2)若i>0,则该结点的双亲的序号为(i-1)/2向下取整。

(3)若2i+1<n,则该结点的左孩子的序号为2i+1,否则该结点无左孩子。

(4)若2i+2<n,则该结点的右孩子的序号为2i+2,否则该结点无右孩子。

C#构建二叉树

1、建立二叉树的类

class Node
{
private object _data;//本节点数据
private Node _left;//左孩子
private Node _right;//右孩子
public object Data
{
get { return _left; }
set { _data = value; }
}
public Node Left
{
get { return _left; }
set { _left = value; }
}
public Node Right
{
get { return _right; }
set { _right = value; }
}
public Node(object data)
{
this._data = data;
}
public override string ToString()
{
return _data.ToString();
}
}2、构建二叉树
class BinaryTreeFull
{
private Node _head;//头节点,是整个二叉树的入口
private string cStr;//将传入的初始化字符串转换为全局变量
public Node Head //调用Head就相当于拿到了整棵树
{
get { return _head; }
set { _head = value; }
}
public BinaryTreeFull()
{
_head = null;
}
public BinaryTreeFull(string constructStr)//默认以程序遍历构造二叉树
{
cStr = constructStr;//赋值给全局变量
if (cStr[0] == '#')//#表示空节点
{
_head = null;
return;//根节点为空
}
_head = new Node(cStr[0]);//把第一个字符加入二叉树,作为根结点
Add(_head, 0);
}
private void Add(Node parent, int index)
{
int leftIndex = 2 * index + 1;
if (leftIndex < cStr.Length)//存在左孩子
{
if (cStr[leftIndex] != '#')//不为空
{
parent.Left = new Node(cStr[leftIndex]);//加入该结点的左孩子
Add(parent.Left, leftIndex);//继续查找
}
}
int rightIndex = 2 * index + 2;
if (rightIndex < cStr.Length)//存在右孩子
{
if (cStr[rightIndex] != '#')//不为空
{
parent.Right = new Node(cStr[rightIndex]);//加入该结点的右孩子
Add(parent.Right, rightIndex);//继续查找
}
}
}
}递归的方法遍历二叉树:
1、先序遍历

public void PreOrder(Node node)
{
if (node != null)//只要结点不为空,就输出结点值,然后查找左结点和右结点
{
Console.Write(node);//我在开始
PreOrder(node.Left);
PreOrder(node.Right);
}
}2、中序遍历
public void InOrder(Node node)
{
if (node != null)
{
InOrder(node.Left);
Console.Write(node);//我在中间
InOrder(node.Right);
}
}3、后续遍历
public void AfterOrder(Node node)
{
if (node != null)
{
AfterOrder(node.Left);
AfterOrder(node.Right);
Console.Write(node);//我在最后
}
}4、计算叶子结点个数(先序遍历)
public void CountLeaf(Node node, ref int count)//count用于返回结点个数
{
if (node != null)
{
if ((node.Left == null) && (node.Right == null))
{
count++;
}
CountLeaf(node.Left, ref count);
CountLeaf(node.Right, ref count);
}
}
5、计算节点数
public int Count(Node root)
{
if (root == null) return 0;
return Count(root.Right) + Count(root.Left) + 1;
}6、计算二叉树的高度
public int Height(Node root)
{
int a, b;
if (root == null) return 0;
a = Height(root.Left);
b = Height(root.Right);
if (a > b)
{
return a + 1;
}
else
{
return b + 1;
}
}7、复制二叉树
public Node CopyTree(Node root)
{
Node newroot;
if (root == null)
{
newroot = null;
}
else
{
CopyTree(root.Left);
CopyTree(root.Right);
newroot = root;
}
return newroot;
}8、先序遍历建立二叉树
public static BinaryTreeFull CreateByPre(string s)
{
BinaryTreeFull tree = new BinaryTreeFull(s);//先以层序初始化一个树,再调整
int _count = 0;
Node node = tree.Head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
node.Data = s[_count++];
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
node = stack.Pop();
node = node.Right;
}
}
return tree;
}9、中序遍历建立二叉树
public static BinaryTreeFull CreateByIn(string s)
{
BinaryTreeFull tree = new BinaryTreeFull(s);
int _count = 0;
Node node = tree.Head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
node = stack.Pop();
node.Data = s[_count++];
node = node.Right;
}
}
return tree;
}10、后序遍历建立二叉树
public static BinaryTreeFull CreateByAfter(string s)
{
BinaryTreeFull tree = new BinaryTreeFull(s);
int _count = 0;
Node node = tree.Head;
Node pre = tree.Head;
//pre指针指向“之前出栈节点”,如果为null有问题,因为后序遍历中头结点肯定值最后被访问的
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
Node temp = stack.Peek().Right;
if (temp == null || temp == pre)
{
node = stack.Pop();
node.Data = s[_count++];
pre = node;
node = null;
}
else
{
node = temp;
}
}
}
return tree;
}非递归方法遍历二叉树:
1、先序遍历

public void PreStackOrder()
{
Node node = _head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
Console.Write(node);
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
node = stack.Pop();
node = node.Right;//如果出栈节点没有右孩子的话则继续出栈操作
}
}
}2、中序遍历
public void InStackOrder()
{
Node node = _head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
node = stack.Pop();
Console.Write(node);
node = node.Right;
}
}
}3、后续遍历(比较难,先存起来)
     3.1利用两个栈
public void AfterStackOrder()
{
Stack<Node> lstack = new Stack<Node>();//用于存放父节点
Stack<Node> rstack = new Stack<Node>();//用于存放右孩子
Node node = _head, right;//right用于存放右栈出栈的节点
do
{
while (node != null)
{
right = node.Right;
lstack.Push(node);
rstack.Push(right);
node = node.Left;//沿左孩子的方向继续循环
}
node = lstack.Pop();
right = rstack.Pop();
if (right == null)//如果右出栈的元素为空则访问左边出栈的元素
{
Console.Write(node);
}
else
{
lstack.Push(node);//左边出栈元素退回栈
rstack.Push(null);//右边补充一个空元素
}
node = right;//如果右边出栈的部位空,则以上面的规则访问这个右孩子节点
}
while (lstack.Count > 0 || rstack.Count > 0);
}    3.2利用一个栈
public void AfterStackOrder2()//性能更优的单栈非递归算法
{
Node node = _head, pre = _head;
//pre指针指向“之前出栈节点”,如果为null有问题,这里指向头结点,因为后续遍历中头结点肯定值最后被访问的。
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
Node temp = stack.Peek().Right;//获取栈顶元素的右孩子
if (temp == null || temp == pre)//满足规则1
{
node = stack.Pop();
Console.Write(node);
pre = node;
node = null;
}
else
{
node = temp;//将栈顶节点的右孩子入栈
}
}
}
}

最后,感谢这位博主的文章,博主主页:http://www.cnblogs.com/zhanjindong
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: