C# 數據結構 之 二叉樹


二叉樹的定義

二叉樹(Binary Tree)是n(n>=0)個有限元素的集合,該集合或者為空,或者由一個稱為根(root)的元素及兩個不相交的,被稱為左子樹和右子樹的二叉樹組成。當集合為空時,稱該二叉樹為空二叉樹,在二叉樹中,一個元素也稱為一個結點。

二叉樹是有序的,即若將其左右子樹顛倒,就稱為另一顆不同的二叉樹。

二叉樹的相關術語

結點的度:結點所擁有的子樹的個數稱為該結點的度。

葉結點:度為0的結點稱為葉結點,或者稱為終端結點。

分支結點:度不為0的結點為分支結點,一棵樹的結點除葉結點外就是分支結點。

孩子:一個結點子樹的根結點就是這個結點的孩子。

雙親:一個結點的上層結點稱為這個結點的雙親。

兄弟:同一雙親的孩子稱為兄弟。

路徑:如果一棵樹的一串結點n1,n2,...,nk有如下關系:結點ni是ni+1 的父結點,就把n1,n2,...,nk稱為一條從n1到nk的路徑,這條路徑的長度是k-1.

樹的深度:樹中所有結點的最大層數稱為樹的深度。

樹的度:樹中個結點度的最大值稱為該樹的度。

滿二叉樹

在一顆二叉樹中,如果所有分支結點都存在左子樹和右子樹,並且所有葉子結點都在同一層上,這樣的一顆二叉樹稱為滿二叉樹。

完全二叉樹

一顆深度為k的有n個結點的二叉樹,對樹中的結點按從上到下,從左到右的順序進行編號,如果編號為i(1=<i=<n)的結點與滿二叉樹中編號為i的結點在二叉樹中的位置相同,則這棵樹稱為完全二叉樹。 完全二叉樹的特點:葉子結點智能出現在最下層和次下層,且最下層的葉子結點集中在樹的左部。

顯然,一顆滿二叉樹必定是一顆完全二叉樹,而完全二叉樹未必是滿二叉樹。

二叉樹的主要性質

1.一顆非空二叉樹的第i層上最多有2i-1個結點(i>=1)。

2.一顆深度為k的二叉樹中,最多具有2k-1個結點。

3.對於一顆非空二叉樹,如果葉子結點樹為n0,度數為2的二叉樹結點為n2,則有n0=n2+1;

4.具有n個結點的完全二叉樹的深度k為[log2n]+1。

二叉樹的存儲結構有三種:順序存儲結構 二叉鏈表存儲結構 三叉鏈表存儲結構

二叉鏈表存儲結構:

每個結點有三個域組成,除了數據域外,還有兩個指針域,分別用來給出該結點左孩子和右孩子所在的鏈節點的存儲地址。iChild | data | rChild

三叉鏈表存儲結構:

每個結點由4個域組成,iChild | data | rChild |parent

parent域用來指向該結點雙親結點的指針,這種存儲結構便於查找孩子結點 雙親結點,但是增加了空間開銷。

Binary Tree
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataStructure
{
    /// <summary>
    /// 二叉鏈表結點類
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public  class TreeNode<T>
    {
        private T data;               //數據域
        private TreeNode<T> lChild;   //左孩子   樹中一個結點的子樹的根結點稱為這個結點的孩子
        private TreeNode<T> rChild;   //右孩子

        public TreeNode(T val, TreeNode<T> lp, TreeNode<T> rp)
        {
            data = val;
            lChild = lp;
            rChild = rp;
        }

        public TreeNode(TreeNode<T> lp, TreeNode<T> rp)
        {
            data = default(T);
            lChild = lp;
            rChild = rp;
        }

        public TreeNode(T val)
        {
            data = val;
            lChild = null;
            rChild = null;
        }

        public TreeNode()
        {
            data = default(T);
            lChild = null;
            rChild = null;
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }

        public TreeNode<T> LChild
        {
            get { return lChild; }
            set { lChild = value; }
        }

        public TreeNode<T> RChild
        {
            get { return rChild; }
            set { rChild = value; }
        }

    }

    /// <summary>
    /// 定義索引文件結點的數據類型
    /// </summary>
    public struct indexnode
    {
        int key;         //
        int offset;      //位置
        public indexnode(int key, int offset)
        {
            this.key = key;
            this.offset = offset;
        }

        //鍵屬性
        public int Key
        {
            get { return key; }
            set { key = value; }
        }
        //位置屬性
        public int Offset
        {
            get { return offset; }
            set { offset = value; }
        }


    }


    public class LinkBinaryTree<T>
    {
        private TreeNode<T> head;       //頭引用

        public TreeNode<T> Head
        {
            get { return head; }
            set { head = value; }
        }

        public LinkBinaryTree()
        {
            head = null;
        }

        public LinkBinaryTree(T val)
        {
            TreeNode<T> p = new TreeNode<T>(val);
            head = p;
        }

        public LinkBinaryTree(T val, TreeNode<T> lp, TreeNode<T> rp)
        {
            TreeNode<T> p = new TreeNode<T>(val, lp, rp);
            head = p;
        }

        //判斷是否是空二叉樹
        public bool IsEmpty()
        {
            if (head == null)
                return true;
            else
                return false;
        }

        //獲取根結點
        public TreeNode<T> Root()
        {
            return head;
        }

        //獲取結點的左孩子結點
        public TreeNode<T> GetLChild(TreeNode<T> p)
        {
            return p.LChild;
        }

        public TreeNode<T> GetRChild(TreeNode<T> p)
        {
            return p.RChild;
        }

        //將結點p的左子樹插入值為val的新結點,原來的左子樹稱為新結點的左子樹
        public void InsertL(T val, TreeNode<T> p)
        {
            TreeNode<T> tmp = new TreeNode<T>(val);
            tmp.LChild = p.LChild;
            p.LChild = tmp;
        }

        //將結點p的右子樹插入值為val的新結點,原來的右子樹稱為新節點的右子樹
        public void InsertR(T val, TreeNode<T> p)
        {
            TreeNode<T> tmp = new TreeNode<T>(val);
            tmp.RChild = p.RChild;
            p.RChild = tmp;
        }

        //若p非空 刪除p的左子樹
        public TreeNode<T> DeleteL(TreeNode<T> p)
        {
            if ((p == null) || (p.LChild == null))
                return null;
            TreeNode<T> tmp = p.LChild;
            p.LChild = null;
            return tmp;
        }

        //若p非空 刪除p的右子樹
        public TreeNode<T> DeleteR(TreeNode<T> p)
        {
            if ((p == null) || (p.RChild == null))
                return null;
            TreeNode<T> tmp = p.RChild;
            p.RChild = null;
            return tmp;
        }

        //編寫算法 在二叉樹中查找值為value的結點

        public TreeNode<T> Search(TreeNode<T> root, T value)
        {
            TreeNode<T> p = root;
            if (p == null)
                return null;
            if (!p.Data.Equals(value))
                return p;
            if (p.LChild != null)
            {
                return Search(p.LChild, value);
            }
            if (p.RChild != null)
            {
                return Search(p.RChild, value);
            }
            return null;
        }

        //判斷是否是葉子結點
        public bool IsLeaf(TreeNode<T> p)
        {
            if ((p != null) && (p.RChild == null) && (p.LChild == null))
                return true;
            else
                return false;
        }


        //中序遍歷
        //遍歷根結點的左子樹->根結點->遍歷根結點的右子樹 
        public void inorder(TreeNode<T> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                inorder(ptr.LChild);
                Console.WriteLine(ptr.Data + " ");
                inorder(ptr.RChild);
            }
        }


        //先序遍歷
        //根結點->遍歷根結點的左子樹->遍歷根結點的右子樹 
        public void preorder(TreeNode<T> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                Console.WriteLine(ptr.Data + " ");
                preorder(ptr.LChild);
                preorder(ptr.RChild);
            }
        }


        //后序遍歷
        //遍歷根結點的左子樹->遍歷根結點的右子樹->根結點
        public void postorder(TreeNode<T> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                postorder(ptr.LChild);
                postorder(ptr.RChild);
                Console.WriteLine(ptr.Data + "");
            }
        }


        //層次遍歷
        //引入隊列 
        public void LevelOrder(TreeNode<T> root)
        {
            if (root == null)
            {
                return;
            }
            CSeqQueue<TreeNode<T>> sq = new CSeqQueue<TreeNode<T>>(50);
            sq.EnQueue(root);
            while (!sq.IsEmpty())
            {
                //結點出隊
                TreeNode<T> tmp = sq.DeQueue();
                //處理當前結點
                Console.WriteLine("{0}", tmp);
                //將當前結點的左孩子結點入隊
                if (tmp.LChild != null)
                {
                    sq.EnQueue(tmp.LChild);
                }
                if (tmp.RChild != null)
                {
                    sq.EnQueue(tmp.RChild);
                }
            }
        }
    }

    /// <summary>
    /// 二叉搜索樹:結點的左子節點的值永遠小於該結點的值,而右子結點的值永遠大於該結點的值 稱為二叉搜索樹
    /// </summary>
    public class LinkBinarySearchTree : LinkBinaryTree<indexnode>
    {
        //定義增加結點的方法
        public void insert(indexnode element)
        {
            TreeNode<indexnode> tmp, parent = null, currentNode = null;
            //調用FIND方法
            find(element, ref parent, ref currentNode);
            if (currentNode != null)
            {
                Console.WriteLine("Duplicates words not allowed");
                return;
            }
            else
            {
                //創建結點
                tmp = new TreeNode<indexnode>(element);
                if (parent == null)
                    Head = tmp;
                else
                {
                    if (element.Key < parent.Data.Key)
                        parent.LChild = tmp;
                    else
                        parent.RChild = tmp;
                }
            }
        }

        //定義父結點
        public void find(indexnode element, ref TreeNode<indexnode> parent, ref TreeNode<indexnode> currentNode)
        {
            currentNode = Head;
            parent = null;
            while ((currentNode != null) && (currentNode.Data.Key.ToString() != element.Key.ToString()) && (currentNode.Data.Offset .ToString() != element.Offset .ToString()))//
            {
                parent = currentNode;
                if (element.Key < currentNode.Data.Key)
                    currentNode = currentNode.LChild;
                else
                    currentNode = currentNode.RChild;
            }
        }

        //定位結點
        public void find(int key)
        {
            TreeNode<indexnode> currentNode = Head;
            while ((currentNode != null) && (currentNode.Data.Key.ToString () != key.ToString ()))
            {
                Console.WriteLine(currentNode.Data.Offset.ToString());
                if (key < currentNode.Data.Key)
                    currentNode = currentNode.LChild;
                else
                    currentNode = currentNode.RChild;
            }
        }

        //中序遍歷
        //遍歷根結點的左子樹->根結點->遍歷根結點的右子樹 
        public void inorderS(TreeNode<indexnode> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                inorderS(ptr.LChild);
                Console.WriteLine(ptr.Data.Key  + " ");
                inorderS(ptr.RChild);
            }
        }


        //先序遍歷
        //根結點->遍歷根結點的左子樹->遍歷根結點的右子樹 
        public void preorderS(TreeNode<indexnode> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                Console.WriteLine(ptr.Data.Key  + " ");
                preorderS(ptr.LChild);
                preorderS(ptr.RChild);
            }
        }


        //后序遍歷
        //遍歷根結點的左子樹->遍歷根結點的右子樹->根結點
        public void postorderS(TreeNode<indexnode> ptr)
        {
            if (IsEmpty())
            {
                Console.WriteLine("Tree is Empty !");
                return;
            }
            if (ptr != null)
            {
                postorderS(ptr.LChild);
                postorderS(ptr.RChild);
                Console.WriteLine(ptr.Data.Key + "");
            }
        }
    }


    /// <summary>
    /// 循環順序隊列
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class CSeqQueue<T>
    {
        private int maxsize;       //循環順序隊列的容量
        private T[] data;          //數組,用於存儲循環順序隊列中的數據元素
        private int front;         //指示最近一個已經離開隊列的元素所占有的位置 循環順序隊列的對頭
        private int rear;          //指示最近一個進入隊列的元素的位置           循環順序隊列的隊尾

        public T this[int index]
        {
            get { return data[index]; }
            set { data[index] = value; }
        }

        //容量屬性
        public int Maxsize
        {
            get { return maxsize; }
            set { maxsize = value; }
        }

        //對頭指示器屬性
        public int Front
        {
            get { return front; }
            set { front = value; }
        }

        //隊尾指示器屬性
        public int Rear
        {
            get { return rear; }
            set { rear = value; }
        }

        public CSeqQueue()
        {

        }

        public CSeqQueue(int size)
        {
            data = new T[size];
            maxsize = size;
            front = rear = -1;
        }

        //判斷循環順序隊列是否為滿
        public bool IsFull()
        {
            if ((front == -1 && rear == maxsize - 1) || (rear + 1) % maxsize == front)
                return true;
            else
                return false;
        }

        //清空循環順序列表
        public void Clear()
        {
            front = rear = -1;
        }

        //判斷循環順序隊列是否為空
        public bool IsEmpty()
        {
            if (front == rear)
                return true;
            else
                return false;
        }

        //入隊操作
        public void EnQueue(T elem)
        {
            if (IsFull())
            {
                Console.WriteLine("Queue is Full !");
                return;
            }
            rear = (rear + 1) % maxsize;
            data[rear] = elem;
        }

        //出隊操作
        public T DeQueue()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is Empty !");
                return default(T);
            }
            front = (front + 1) % maxsize;
            return data[front];
        }

        //獲取對頭數據元素
        public T GetFront()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is Empty !");
                return default(T);
            }
            return data[(front + 1) % maxsize];//front從-1開始
        }

        //求循環順序隊列的長度
        public int GetLength()
        {
            return (rear - front + maxsize) % maxsize;
        }
    }


    /// <summary>
    /// 哈夫曼樹結點類
    /// </summary>
    public class HNode
    {
        private int weight;          //結點權值
        private int lChild;          //左孩子結點
        private int rChild;          //右孩子結點
        private int parent;          //父結點

        public int Weight
        {
            get { return weight; }
            set { weight = value; }
        }

        public int LChild
        {
            get { return lChild; }
            set { lChild = value; }
        }

        public int RChild
        {
            get { return rChild; }
            set { rChild = value; }
        }

        public int Parent
        {
            get { return parent; }
            set { parent = value; }
        }

        public HNode()
        {
            weight = 0;
            lChild = -1;
            rChild = -1;
            parent = -1;
        }
    }
class BinaryTree
    {

        static void Main(string[] args)
        {
            LinkBinarySearchTree bs = new LinkBinarySearchTree();
            while (true)
            {
                //菜單
                Console.WriteLine("\nMenu");
                Console.WriteLine("1.創建二叉搜索樹");
                Console.WriteLine("2.執行中序遍歷");
                Console.WriteLine("3.執行先序遍歷");
                Console.WriteLine("4.執行后序遍歷");
                Console.WriteLine("5.顯示搜索一個結點的路徑");
                Console.WriteLine("6.exit");

                Console.WriteLine("\n輸入你的選擇(1-5)");
                char ch = Convert.ToChar(Console.ReadLine());
                Console.WriteLine();

                switch (ch)
                {
                    case '1':
                        {
                            int key, offset;
                            string flag;
                            do
                            {
                                Console.WriteLine("請輸入鍵:");
                                key = Convert.ToInt32(Console.ReadLine());
                                Console.WriteLine("請輸入位置:");
                                offset = Convert.ToInt32(Console.ReadLine());
                                indexnode element = new indexnode(key, offset);
                                bs.insert(element);
                                Console.WriteLine("繼續添加新的結點(Y/N)?");
                                flag = Console.ReadLine();
                            } while (flag == "Y" || flag == "y");
                        }
                        break;
                    case '2':
                        {
                            bs.inorderS(bs.Head);
                        }
                        break;
                    case '3':
                        {
                            bs.preorderS(bs.Head);
                        }
                        break;
                    case '4':
                        {
                            bs.postorderS(bs.Head);
                        }
                        break;
                    case '5':
                        {
                            int key;
                            Console.WriteLine("請輸入鍵");
                            key = Convert.ToInt32(Console.ReadLine());
                            bs.find(key);
                        }
                        break;
                    case '6':
                        return;
                    default:
                        {
                            Console.WriteLine("Invalid option");
                            break;
                        }
                }
            }
        }
}

 最優二叉樹 哈夫曼樹

最優二叉樹 也稱哈夫曼樹(HUffman) 是指對於一組帶有確定權值的葉結點,構造的具有最小帶權路徑長度的二叉樹。

若將樹中結點賦給一個有着特殊含義的數值,則這個數值稱為該結點的權值

設二叉樹具有n個帶權值的葉結點,那么從根結點到各結點的路徑長度與相應葉結點權值的乘積叫做二叉樹的帶權路徑長度。記為:WPL = ΣK=1N WK*LK.

根據哈夫曼樹的定義要使其WPL值最小,必須使其權值大的葉結點越靠近根結點,而權值小的葉結點越遠離根結點。

建立哈夫曼樹的基本思想:

1.由給定的n個權值{W1,W2,W3,W4,...,Wn}構造n顆只有一個葉結點的二叉樹,從而得到一個二叉樹集合F={T1,T2,T3,...,Tn};

2.在F中選取根結點的權值最小的和次小的兩顆二叉樹作為左 右子樹構造一顆新的二叉樹,這顆二叉樹根結點的權值為其左右子樹根結點權值之和。

3.在F中刪除作為左右子樹的兩顆二叉樹,並將新建立的二叉樹加入到集合F中。

4 重復 2 。3 兩步,當F中只剩一顆二叉樹時,這顆二叉樹表示要建立的哈夫曼樹。

Huffman Tree
    /// <summary>
    /// 哈夫曼樹結點類
    /// </summary>
    public class HNode
    {
        private int weight;          //結點權值
        private int lChild;          //左孩子結點
        private int rChild;          //右孩子結點
        private int parent;          //父結點

        public int Weight
        {
            get { return weight; }
            set { weight = value; }
        }

        public int LChild
        {
            get { return lChild; }
            set { lChild = value; }
        }

        public int RChild
        {
            get { return rChild; }
            set { rChild = value; }
        }

        public int Parent
        {
            get { return parent; }
            set { parent = value; }
        }

        public HNode()
        {
            weight = 0;
            lChild = -1;
            rChild = -1;
            parent = -1;
        }
    }


    public class HuffmanTree
    {
        private HNode[] data;       //結點數組
        private int leafNum;        //葉子結點數目

        public HNode this[int index]
        {
            get { return data[index]; }
            set { data[index] = value; }
        }

        public int LeafNum
        {
            get { return leafNum; }
            set { leafNum = value; }
        }

        public HuffmanTree(int n)
        {
            //由哈夫曼樹的構造思想,用一個數組存放原來的n個葉子結點和構造過程中產生
            //臨時生成的結點,數組的大小為2n-1
            data = new HNode[2 * n - 1];
            for (int i = 0; i < 2 * n - 1; i++)
            {
                data[i] = new HNode();
            }
            leafNum = n;
        }


        //創建哈夫曼樹
        public void Create()
        {
            int m1, m2, x1, x2;
            //輸入n個葉子結點的權值
            for (int i = 0; i < this.leafNum; ++i)
            {
                data[i].Weight = Convert.ToInt32(Console.ReadLine());
            }
            //處理n個葉子結點,建立哈夫曼樹
            for (int i = 0; i < this.leafNum - 1; ++i)
            {
                m1 = m2 = Int32.MaxValue;
                x1 = x2 = 0;
                for (int j = 0; j < this.leafNum + i; ++j)//2*n-1
                {
                    if ((data[j].Weight < m1) && (data[j].Parent == -1))
                    {
                        m2 = m1;
                        x2 = x1;
                        m1 = data[j].Weight;
                        x1 = j;
                    }
                    else if ((data[j].Weight < m2) && (data[j].Parent == -1))
                    {
                        m2 = data[j].Weight;
                        x2 = j;
                    }
                }
                data[x1].Parent = this.leafNum + i;
                data[x2].Parent = this.leafNum + i;
                data[this.leafNum + i].Weight = data[x1].Weight + data[x2].Weight;
                data[this.leafNum + i].LChild = x1;
                data[this.leafNum + 1].RChild = x2;
            }
        }


    }
 static void Main(string[] arg)
        {
            HuffmanTree ht;
            Console.WriteLine("請輸入葉節點的個數:");
            int leafNum = Convert.ToInt32(Console.ReadLine());
            ht = new HuffmanTree(leafNum);
            ht.Create();
            Console.WriteLine("位置\t權值\t父結點\t左孩子結點\t右孩子結點");
            for (int i = 0; i < 2 * leafNum - 1; i++)
            {
                Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", i, ht[i].Weight, ht[i].Parent, ht[i].LChild, ht[i].RChild);
            }
            Console.ReadLine();
        }

注:本文整理自《數據結構(C#語言版)》!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM