二叉樹學習:從零基礎到程式碼實現
阿新 • • 發佈:2019-02-15
二叉樹的定義:
二叉樹是節點的有限集合,該集合或者為空集,或者是由一個根和兩顆互不相交的、稱為該根的左子樹和右子樹的二叉樹組成。
二叉樹的性質:
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、建立二叉樹的類
2、構建二叉樹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(); } }
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