1. 程式人生 > >二叉樹學習:從零基礎到程式碼實現

二叉樹學習:從零基礎到程式碼實現

二叉樹的定義:

二叉樹是節點的有限集合,該集合或者為空集,或者是由一個根和兩顆互不相交的、稱為該根的左子樹和右子樹的二叉樹組成。

二叉樹的性質:

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