數據結構和算法系列14 二叉樹

歇了幾天了,沒有寫博客。從今天開始要總結樹和二叉樹了。那麼什麼是樹呢?node

1,樹的定義:算法

1)有且僅有一個特定的稱爲根Root的結點。數組

2)當n>1時,其他結點可分爲m(m>0)個互不相交的有限集,其中每一個集合自己又是一個棵樹,並稱爲根的子樹。函數

2,樹的表示方法:spa

最多見的是樹形表示法和廣義表表示法,下面是樹形表示法,如圖所示。3d

ds43

上圖的廣義表表示法爲:(A(B(D,E),C(F,G)))指針

3,常見的術語:code

1) 父節點,孩子節點,兄弟節點。以上圖爲例,A是B和C的父節點,B和C是A的孩子節點,B和C之間就是兄弟節點了。blog

2) 結點的度和樹的度。結點的度即結點有幾個分支,好比節點A有兩個分支B和C,那麼結點A的度就是2,樹的度即爲一棵樹中結點的最大度數,因此樹的度也是2。遞歸

3) 有序樹和無序樹。若是將樹中結點的子樹當作是從左至右依次有序且不能交換,則稱該樹爲有序樹,不然稱爲無序樹。

4) 森林。在上圖中,若是將根節點A拿掉,那麼B和C子樹合併就是森林了。

5) 二叉樹。二叉樹是一種特殊的樹。它的每一個結點至多隻有兩棵子樹。

4,二叉樹的常見性質:

性質1 在二叉樹的第i層上至多有2i-1個結點(i>=1)

性質2 深度爲k的二叉樹至多有2k-1個結點(k>=1)

性質3 滿二叉樹,在一棵深度爲k且有2k-1個結點。徹底二叉樹,若一棵深度爲k的二叉樹,其前k-1層是一個棵滿二叉樹,而最下面一層(即第k層)上的結點都集中在該層最左邊的若干位置上。

滿二叉樹必定是徹底二叉樹,可是徹底二叉樹則不必定是滿二叉樹。

下面是滿二叉樹和徹底二叉樹圖示。

ds43

5,二叉樹的兩種存儲結構

1) 順序存儲

對於徹底二叉樹而言,可使用順序存儲結構。可是對於通常的二叉樹來講,使用存儲結構會有兩個缺點,一,若是不是徹底二叉樹,則必須將其轉化爲徹底二叉樹,二是增長了不少虛節點,浪費資源空間。

ds43

2) 鏈式存儲

這是最經常使用的一種二叉樹存儲結構。每一個結點設置三個域,即值域,左指針域和右指針域,用data表示值域,lchild和rchild分別表示指向左右子樹的指針域。如圖所示。

ds43

6,二叉樹的常見操做

1) 插入節點

思路:首先找到要插入節點的父節點,而後肯定插到父節點的左邊仍是右邊,最後將節點插入。

2) 查找節點

思路:運用遞歸查找。

3) 計算樹的深度

思路:分別遞歸左子樹和右子樹,取長度較大的那一個做爲整個樹的深度。

4) 遍歷之先序遍歷

思路:先訪問根,而後遍歷左子樹,再遍歷右子樹

5) 遍歷之中序遍歷

思路:先遍歷左子樹,再訪問根,最後遍歷右子樹

6) 遍歷以後序遍歷

思路:先遍歷左子樹,再遍歷右子樹,最後訪問根

7) 遍歷之層次遍歷

思路:從上到小,從左到右遍歷

 

下面是算法實現代碼。

C#版:

namespace DS.BLL
{
    /// <summary>
    /// 描述:二叉樹操做類
    /// 做者:魯寧
    /// 時間:2013/9/5 11:36:43
    /// </summary>
    public class BinTreeBLL
    {
        //按層遍歷的存儲空間長度
        public static int Length { get; set; }

        /// <summary>
        /// 生成根節點
        /// </summary>
        /// <returns></returns>
        public static BinTree<string> CreateRoot()
        {
            BinTree<string> root = new BinTree<string>();
            Console.WriteLine("請輸入根節點,以便生成樹");
            root.Data = Console.ReadLine();
            Console.WriteLine("根節點生成成功");
            return root;
        }

        /// <summary>
        /// 插入節點
        /// </summary>
        /// <param name="tree">待操做的二叉樹</param>
        /// <returns>插入節點後的二叉樹</returns>
        public static BinTree<string> Insert(BinTree<string> tree)
        {
            while (true)
            { 
                //建立要插入的節點
                BinTree<string> node = new BinTree<string>();
                Console.WriteLine("請輸入待插入節點的數據");
                node.Data = Console.ReadLine();

                //獲取父節點數據
                Console.WriteLine("請輸入待查找的父節點數據");
                var parentNodeData = Console.ReadLine();

                //肯定插入方向
                Console.WriteLine("請肯定要插入到父節點的:1 左側, 2 右側");
                Direction direction = (Direction)Enum.Parse(typeof(Direction), Console.ReadLine());

                //插入節點
                tree = InsertNode(tree,node,parentNodeData,direction);

                //todo:沒有找到父節點沒有提示???
                if (tree == null)
                {
                    Console.WriteLine("未找到父節點,請從新輸入!");
                    continue;
                }
                Console.WriteLine("插入成功,是否繼續? 1 繼續,2 退出");

                if (int.Parse(Console.ReadLine()) == 1) continue;
                else break; //退出循環
            }
            return tree;
        }

        public static BinTree<T> InsertNode<T>(BinTree<T> tree, BinTree<T> node, T parentNodeData, Direction direction)
        {
            if (tree == null) return null;

            //找到父節點
            if (tree.Data.Equals(parentNodeData))
            {
                switch (direction)
                { 
                    case Direction.Left:
                        if (tree.Left != null) throw new Exception("左節點已存在,不能插入!");
                        else tree.Left = node;
                        break;
                    case Direction.Right:
                        if (tree.Right != null) throw new Exception("右節點已存在,不能插入!");
                        else tree.Right = node;
                        break;
                }
            }

            //向左子樹查找父節點(遞歸)
            InsertNode(tree.Left,node,parentNodeData,direction);

            //向右子樹查找父節點(遞歸)
            InsertNode(tree.Right,node,parentNodeData,direction);

            return tree;
        }

        /// <summary>
        /// 查找節點
        /// </summary>
        /// <typeparam name="T">節點數據類型</typeparam>
        /// <param name="tree">二叉樹</param>
        /// <param name="data">要查找的節點數據</param>
        /// <returns>要查找的節點</returns>
        public static bool GetNode<T>(BinTree<T> tree, T data)
        {
            if (tree == null) return false;

            //查找成功
            if (tree.Data.Equals(data)) return true;

            //遞歸查找
            return GetNode(tree.Left,data); //這裏有問題???
        }

        /// <summary>
        /// 獲取二叉樹的深度
        /// 思路:分別遞歸左子樹和右子樹,而後得出較大的一個即爲二叉樹的深度
        /// </summary>
        /// <typeparam name="T">二叉樹的數據類型</typeparam>
        /// <param name="tree">待操做的二叉樹</param>
        /// <returns>二叉樹的深度</returns>
        public static int GetLength<T>(BinTree<T> tree)
        {
            if (tree == null) return 0;
            int leftLength, rightLength;
            
            //遞歸左子樹的深度
            leftLength = GetLength(tree.Left);

            //遞歸右子樹的深度
            rightLength = GetLength(tree.Right);

            if (leftLength > rightLength) return leftLength + 1;
            else return rightLength + 1;
        }

        /// <summary>
        /// 先序遍歷
        /// 思路:輸出根節點--->遍歷左子樹--->遍歷右子樹
        /// </summary>
        /// <typeparam name="T">二叉樹的數據類型</typeparam>
        /// <param name="tree">待操做的二叉樹</param>
        public static void Traversal_DLR<T>(BinTree<T> tree)
        {
            if (tree == null) return;

            //輸出節點的值
            Console.Write(tree.Data+"\t");

            //遞歸遍歷左子樹
            Traversal_DLR(tree.Left);

            //遞歸遍歷右子樹
            Traversal_DLR(tree.Right);
        }

        /// <summary>
        /// 中序遍歷
        /// 思路:遍歷左子樹--->輸出根節點--->遍歷右子樹
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tree"></param>
        public static void Traversal_LDR<T>(BinTree<T> tree)
        {
            if (tree == null) return;

            //遍歷左子樹
            Traversal_LDR(tree.Left);

            //輸出節點的值
            Console.Write(tree.Data + "\t");

            //遍歷右子樹
            Traversal_LDR(tree.Right);
        }

        /// <summary>
        /// 後序遍歷
        /// 思路:遍歷左子樹--->遍歷右子樹--->輸出根節點
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tree"></param>
        public static void Traversal_LRD<T>(BinTree<T> tree)
        {
            if (tree == null) return;

            //遍歷左子樹
            Traversal_LRD(tree.Left);

            //遍歷右子樹
            Traversal_LRD(tree.Right);

            //輸出節點的值
            Console.Write(tree.Data + "\t");
        }

        /// <summary>
        /// 按層遍歷
        /// 思路:從上到下,從左到右遍歷節點
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tree"></param>
        public static void Traversal_Level<T>(BinTree<T> tree)
        {
            if (tree == null) return;
            int head=0;
            int tail=0;

            //申請保存空間
            BinTree<T>[] treeList=new BinTree<T>[Length];

            //將當前二叉樹保存到數組中
            treeList[tail] = tree;

            //計算tail的位置
            tail = (tail + 1) % Length; //除留餘數法

            while (head != tail)
            { 
                var tempNode=treeList[head];

                //計算head的位置
                head = (head + 1) % Length;

                //輸出節點的值
                Console.Write(tempNode.Data+"\t");

                //若是左子樹不爲空,則將左子樹保存到數組的tail位置
                if (tempNode.Left != null)
                {
                    treeList[tail] = tempNode.Left;

                    //從新計算tail的位置
                    tail = (tail + 1) % Length;
                }

                //若是右子樹不爲空,則將右子樹保存到數組的tail位置
                if (tempNode.Right != null)
                {
                    treeList[tail] = tempNode.Right;

                    //從新計算tail的位置
                    tail = (tail + 1) % Length;
                }
            }
        }

        /// <summary>
        /// 清空
        /// 思路:使用遞歸釋放當前節點的數據值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tree"></param>
        public static void Clear<T>(BinTree<T> tree)
        {
            if (tree == null) return;

            //遞歸左子樹
            Clear(tree.Left);

            //遞歸右子樹
            Clear(tree.Right);

            //釋放節點數據
            tree = null;
        }

    }

    /// <summary>
    /// 二叉樹(二叉鏈表)存儲結構
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BinTree<T>
    {
        public T Data { get;set;} //數據域

        public BinTree<T> Left { get; set; } //左孩子

        public BinTree<T> Right { get; set; } //右孩子
    }

    /// <summary>
    /// 插入方向(左節點仍是右節點)
    /// </summary>
    public enum Direction
    {
        Left=1,
        Right=2
    }
}

namespace BinTree.CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***************二叉樹(二叉鏈表)********************");

            //建立根節點
            BinTree<string> tree = BinTreeBLL.CreateRoot();

            //插入節點
            Console.WriteLine("\n*********插入節點***********");
            BinTreeBLL.Insert(tree);

            //先序遍歷
            Console.WriteLine("\n先序遍歷結果:");
            BinTreeBLL.Traversal_DLR(tree);

            //中序遍歷
            Console.WriteLine("\n中序遍歷結果:");
            BinTreeBLL.Traversal_LDR(tree);

            //後序遍歷
            Console.WriteLine("\n後序遍歷結果:");
            BinTreeBLL.Traversal_LRD(tree);

            //層次遍歷
            Console.WriteLine("\n層次遍歷結果:");
            BinTreeBLL.Length = 50;
            BinTreeBLL.Traversal_Level(tree);

            //獲取樹的深度
            Console.WriteLine("\n當前樹的深度爲:{0}",BinTreeBLL.GetLength(tree));

            Console.ReadKey();
        }
    }
}

程序輸出結果如圖:

ds44

 

C語言版:

#include "string.h"
#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存儲空間初始分配量 */

typedef int Status;        /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */

/**********定義二叉樹的存儲結構************/
typedef char TElemType;
TElemType Nil=' '; /* 字符型以空格符爲空 */

typedef struct BinTreeNode  /* 結點結構 */
{
    TElemType data;        /* 結點數據 */
    struct BinTreeNode *lchild,*rchild; /* 左右孩子指針 */
}BinTreeNode,*BinTree;
/*****************************************/

/***********用於構造二叉樹************** */
int index=1;
typedef char charArray[24]; /*聲明一個char類型數組,charArray是數組名也是一個指針*/
charArray str;

Status AssignStr(charArray T,char *chars)
{ 
    int i;
    if(strlen(chars)>MAXSIZE) return ERROR; /*輸入字符的長度超過存儲空間最大值*/
    else
    {
        T[0]=strlen(chars); /*0號單元存放字符串長度*/
        for(i=1;i<=T[0];i++)
        {
            T[i]=*(chars+i-1); /*???*/
        }
        return OK;
    }
}
/* ************************************* */

/*構造二叉樹*/
void CreateBinTree(BinTree *T)
{ 
    TElemType ch;
    ch=str[index++];

    if(ch=='#') 
        *T=NULL;
    else
    {
        *T=(BinTree)malloc(sizeof(BinTreeNode));
        if(!*T)
            exit(OVERFLOW);
        (*T)->data=ch; /* 生成根結點 */
        CreateBinTree(&(*T)->lchild); /* 構造左子樹 */
        CreateBinTree(&(*T)->rchild); /* 構造右子樹 */
    }
}

/* 構造空二叉樹*/
Status InitBinTree(BinTree *T)
{ 
    *T=NULL;
    return OK;
}

/*清空二叉樹*/
void ClearBinTree(BinTree *T)
{ 
    if(*T) 
    {
        if((*T)->lchild) /* 有左孩子 */
            ClearBinTree(&(*T)->lchild); /* 銷燬左孩子子樹 */
        if((*T)->rchild) /* 有右孩子 */
            ClearBinTree(&(*T)->rchild); /* 銷燬右孩子子樹 */
        free(*T); /* 釋放根結點 */
        *T=NULL; /* 空指針賦0 */
    }
}

/*判斷二叉樹是否爲空*/
Status IsBinTreeEmpty(BinTree T)
{ 
    if(T)
        return FALSE;
    else
        return TRUE;
}

/*計算二叉樹的深度*/
int GetDepth(BinTree T)
{
    int i,j;
    if(!T)
        return 0;
    if(T->lchild)
        i=GetDepth(T->lchild);
    else
        i=0;
    if(T->rchild)
        j=GetDepth(T->rchild);
    else
        j=0;
    return i>j?i+1:j+1;
}

/*獲取二叉樹的根節點*/
TElemType GetRoot(BinTree T)
{ 
    if(IsBinTreeEmpty(T))
        return Nil; /*Nil表示空字符*/
    else
        return T->data;
}

/*前序遍歷
思路:訪問根節點--->前序遍歷左子樹--->前序遍歷右子樹
*/
void PreOrderTraverse(BinTree T)
{ 
    if(T==NULL) return;

    printf("%c ",T->data);/* 顯示結點數據,能夠更改成其它對結點操做 */
    PreOrderTraverse(T->lchild); /* 再先序遍歷左子樹 */
    PreOrderTraverse(T->rchild); /* 最後先序遍歷右子樹 */
}

/*中序遍歷
思路:中序遍歷左子樹--->訪問根節點--->中序遍歷右子樹
*/
void InOrderTraverse(BinTree T)
{ 
    if(T==NULL) return;

    InOrderTraverse(T->lchild); /* 中序遍歷左子樹 */
    printf("%c ",T->data);/* 顯示結點數據,能夠更改成其它對結點操做 */
    InOrderTraverse(T->rchild); /* 最後中序遍歷右子樹 */
}

/*後序遍歷
思路:後序遍歷左子樹--->後序遍歷右子樹--->訪問根節點
*/
void PostOrderTraverse(BinTree T)
{
    if(T==NULL) return;

    PostOrderTraverse(T->lchild); /* 前後序遍歷左子樹  */
    PostOrderTraverse(T->rchild); /* 再後序遍歷右子樹  */
    printf("%c ",T->data);/* 顯示結點數據,能夠更改成其它對結點操做 */
}


int main()
{
    int i;
    BinTree T;
    TElemType e1;

    /*初始化二叉樹爲一棵空樹*/
    InitBinTree(&T);

    /*設置字符數組用於構造二叉樹*/
    AssignStr(str,"ABDH#K###E##CFI###G#J##");

    /*建立二叉樹*/
    CreateBinTree(&T);
    printf("建立二叉樹後,樹空否?%d(1:是 0:否) 樹的深度爲:%d\n",IsBinTreeEmpty(T),GetDepth(T));

    /*獲取二叉樹的根節點*/
    e1=GetRoot(T);
    printf("\n二叉樹的根爲: %c\n",e1);

    /*前序遍歷*/
    printf("\n前序遍歷二叉樹:");
    PreOrderTraverse(T);

    /*中序遍歷*/
    printf("\n中序遍歷二叉樹:");
    InOrderTraverse(T);

    /*後序遍歷*/
    printf("\n後序遍歷二叉樹:");
    PostOrderTraverse(T);

    /*清空二叉樹*/
    ClearBinTree(&T);
    printf("\n\n清除二叉樹後,樹空否?%d(1:是 0:否) 樹的深度爲:%d\n",IsBinTreeEmpty(T),GetDepth(T));
    i=GetRoot(T);
    if(!i) printf("樹空,無根\n");

    getchar();
}
程序輸出結果如圖:

ds45

相關文章
相關標籤/搜索