歇了幾天了,沒有寫博客。從今天開始要總結樹和二叉樹了。那麼什麼是樹呢?node
1,樹的定義:算法
1)有且僅有一個特定的稱爲根Root的結點。數組
2)當n>1時,其他結點可分爲m(m>0)個互不相交的有限集,其中每一個集合自己又是一個棵樹,並稱爲根的子樹。函數
2,樹的表示方法:spa
最多見的是樹形表示法和廣義表表示法,下面是樹形表示法,如圖所示。3d
上圖的廣義表表示法爲:(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層)上的結點都集中在該層最左邊的若干位置上。
滿二叉樹必定是徹底二叉樹,可是徹底二叉樹則不必定是滿二叉樹。
下面是滿二叉樹和徹底二叉樹圖示。
5,二叉樹的兩種存儲結構
1) 順序存儲
對於徹底二叉樹而言,可使用順序存儲結構。可是對於通常的二叉樹來講,使用存儲結構會有兩個缺點,一,若是不是徹底二叉樹,則必須將其轉化爲徹底二叉樹,二是增長了不少虛節點,浪費資源空間。
2) 鏈式存儲
這是最經常使用的一種二叉樹存儲結構。每一個結點設置三個域,即值域,左指針域和右指針域,用data表示值域,lchild和rchild分別表示指向左右子樹的指針域。如圖所示。
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(); } } }
程序輸出結果如圖:
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(); }