1. 二叉樹的實(shí)現(xiàn)分析
問題思考:如何存儲(chǔ)一棵完全二叉樹夯膀?
結(jié)果:使用一個(gè)數(shù)組,按照二叉樹中每個(gè)節(jié)點(diǎn)的位置進(jìn)行存儲(chǔ)即可
如果不是一顆完全二叉樹則在使用數(shù)組存儲(chǔ)的時(shí)候就要將沒有節(jié)點(diǎn)的位置置空舒萎,如下圖所示,當(dāng)一棵樹是斜樹的時(shí)候就會(huì)浪費(fèi)很多存儲(chǔ)空間,所以如果是斜樹使用鏈?zhǔn)酱鎯?chǔ)會(huì)更好一些莲祸,如果是一顆完全二叉樹則使用數(shù)組存儲(chǔ)是非常完美的。
2. 二叉樹的順序存儲(chǔ)實(shí)現(xiàn)
2.1 準(zhǔn)備條件
我們在實(shí)現(xiàn)順序存儲(chǔ)的二叉樹的時(shí)候椭迎,只需要一個(gè)數(shù)組即可锐帜,所以我們設(shè)計(jì)一個(gè)數(shù)組來存儲(chǔ)二叉樹。
代碼實(shí)現(xiàn):
#include <stdio.h>
#include "stdlib.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存儲(chǔ)空間初始分配量 */
#define MAX_TREE_SIZE 100 /* 二叉樹的最大結(jié)點(diǎn)數(shù) */
typedef int Status; /* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼畜号,如OK等 */
typedef int CElemType; /* 樹結(jié)點(diǎn)的數(shù)據(jù)類型缴阎,目前暫定為整型 */
typedef CElemType SqBiTree[MAX_TREE_SIZE]; /* 0號(hào)單元存儲(chǔ)根結(jié)點(diǎn) */
CElemType Nil = 0; /*設(shè)整型以0為空 或者以 INT_MAX(65535)*/
// 節(jié)點(diǎn)的層和每層中的位置
typedef struct {
int level; //結(jié)點(diǎn)層
int order; //本層的序號(hào)(按照滿二叉樹給定序號(hào)規(guī)則)
} Position;
2.2 初始化并創(chuàng)建二叉樹
初始化一個(gè)二叉樹則需要將存儲(chǔ)這個(gè)二叉樹的所有節(jié)點(diǎn)置空即可,與清空一致简软。
代碼實(shí)現(xiàn):
/// 初始化一個(gè)二叉樹
/// @param T 樹
Status InitBiTree(SqBiTree T) {
// 將所有節(jié)點(diǎn)置空
for (int i = 0; i<MAX_TREE_SIZE; i++) {
T[i] = Nil;
}
return OK;
}
// 在順序存儲(chǔ)中清空和初始化的操作是一樣的
#define ClearBiTree InitBiTree
我們這里的創(chuàng)建一個(gè)二叉樹就是能夠自動(dòng)給樹創(chuàng)建一些節(jié)點(diǎn)蛮拔,方便后續(xù)的測試,這里默認(rèn)添加10個(gè)節(jié)點(diǎn)痹升,在添加的時(shí)候做了一個(gè)判斷建炫,當(dāng)前節(jié)點(diǎn)不為空,但其父節(jié)點(diǎn)為空則直接報(bào)錯(cuò)(根節(jié)點(diǎn)除外)疼蛾,其實(shí)我們這里判斷這個(gè)有點(diǎn)多余肛跌,我們是按照完全二叉樹的樣子去創(chuàng)建的,但是如果在我們手動(dòng)輸入的時(shí)候難免會(huì)有錯(cuò)察郁,所以多加些判斷衍慎。
代碼實(shí)現(xiàn):
/// 創(chuàng)建一個(gè)樹(按照完全二叉樹初始化)
/// @param T 樹
Status CreateBiTree(SqBiTree T) {
int i = 0;
while (i<10) {// 添加10個(gè)節(jié)點(diǎn)
T[i] = i+1;
// 如果該節(jié)點(diǎn)不是根節(jié)點(diǎn),但有值皮钠,而且其父節(jié)點(diǎn)沒值稳捆,則報(bào)錯(cuò)(防止手動(dòng)插入的時(shí)候犯錯(cuò))
if (i!=0 && T[i] != Nil && T[(i+1)/2-1] == Nil) {
printf("出現(xiàn)無雙親的非根結(jié)點(diǎn)%d\n",T[i]);
exit(0);
}
i++;
}
// 將其余節(jié)點(diǎn)置空
while (i<MAX_TREE_SIZE) {
T[i] = Nil;
i++;
}
return OK;
}
2.3 判斷二叉樹是否為空樹
樹最重要的就是根,沒有根就沒有樹鳞芙,所以二叉樹的根節(jié)點(diǎn)為空則二叉樹就是空樹眷柔。
代碼實(shí)現(xiàn):
// 判斷樹是否為空,只需要判斷根節(jié)點(diǎn)即可原朝,根節(jié)點(diǎn)都沒有指定為空
Status BiTreeIsEmpty(SqBiTree T) {
if (T[0] == Nil) {
return TRUE;
} else {
return FALSE;
}
}
2.4 計(jì)算二叉樹的深度
要想計(jì)算二叉樹的深度就要找到最后一個(gè)節(jié)點(diǎn)驯嘱,所以我們在順序存儲(chǔ)結(jié)構(gòu)中通過從后往前遍歷的方法找到的第一個(gè)有值的節(jié)點(diǎn)就是該二叉樹的最后一個(gè)節(jié)點(diǎn)。
根據(jù)二叉樹的節(jié)點(diǎn)規(guī)則喳坠,當(dāng)2的當(dāng)前深度的次冪超過剛才找到的位置時(shí)就找到了該二叉樹的深度鞠评。
代碼實(shí)現(xiàn):
/// 二叉樹的深度
/// @param T 樹
int BiTreeDepth(SqBiTree T){
int j = -1;
// 如果樹為空則返回-1
if (BiTreeIsEmpty(T)) { return j; }
int i;
for (i = MAX_TREE_SIZE-1; i>=0; i--) {
if (T[i] != Nil) {
break;
}
}
do {
j++;
} while (pow(2, j) <= i); //計(jì)算2的次冪
return 0;
}
2.5 給定位置賦值
賦值操作首先要根據(jù)層號(hào)和序號(hào)找到需要賦值的位置,然后需要判斷該位置是否合法壕鹉,合法后給該位置賦值剃幌。
代碼實(shí)現(xiàn):
/// 給特定位置的節(jié)點(diǎn)賦值
/// @param T 樹
/// @param p 位置
/// @param e 值
Status setValue(SqBiTree T, Position p, CElemType e) {
// 找到位置
int i = (int)pow(2, p.level-1) + p.order - 2;
// 判斷要賦值的位置的父節(jié)點(diǎn)是否為空
if (T[(i+1)/2-1] == Nil && e != Nil) {
return ERROR;
}
// 判斷要賦值的位置的子節(jié)點(diǎn)是否有值
if ((T[i*2+1] != Nil || T[i*2+1] != Nil) && e !=Nil) {
return ERROR;
}
// 賦值
T[i] = e;
return OK;
}
2.6 獲取根節(jié)點(diǎn)的值和指定位置的值
- 根節(jié)點(diǎn)
代碼實(shí)現(xiàn):
/// 獲取跟節(jié)點(diǎn)的值
/// @param T 樹
/// @param e 值存儲(chǔ)
Status Root(SqBiTree T,CElemType *e){
if (BiTreeIsEmpty(T)) {
return ERROR;
}
*e = T[0];
return OK;
}
- 其他節(jié)點(diǎn)
根據(jù)傳入的層號(hào)和該層序號(hào)找到此值的位置聋涨,返回即可
代碼實(shí)現(xiàn):
/// 獲取給定位置的值
/// @param T 樹
/// @param p 位置
CElemType getValue(SqBiTree T, Position p) {
// 打印層
printf("要獲取的層號(hào)是:%d\n", (int)pow(2, p.level-1));
// 打印序號(hào)
printf("該層的序號(hào)是 %d\n", p.order);
// 返回
return T[(int)pow(2, p.level-1)+p.order-2];
}
2.7 獲取某節(jié)點(diǎn)的父節(jié)點(diǎn)
獲取雙親節(jié)點(diǎn)只需找到該節(jié)點(diǎn),按照二叉樹節(jié)點(diǎn)的規(guī)則负乡,返回其雙親節(jié)點(diǎn)即可
/// 獲取某個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)
/// @param T 樹
/// @param e 節(jié)點(diǎn)值
CElemType GetParent(SqBiTree T, CElemType e){
// 判斷樹是否為空
if (T[0] == Nil) { return Nil; }
// 遍歷找到值所對(duì)應(yīng)的節(jié)點(diǎn)牍白,根節(jié)點(diǎn)沒有雙親節(jié)點(diǎn),所以從1開始
for (int i = 1; i<MAXSIZE; i++) {
if (T[i] == e) {
return T[(i+1)/2 - 1];
}
}
return Nil;
}
2.8 獲取某節(jié)點(diǎn)的孩子節(jié)點(diǎn)(左右)
很簡單抖棘,只需找到相應(yīng)節(jié)點(diǎn)茂腥,然后根據(jù)規(guī)則查找左右孩子即可。
代碼實(shí)現(xiàn):
/// 獲取某個(gè)節(jié)點(diǎn)的左孩子
/// @param T 樹
/// @param e 節(jié)點(diǎn)值
CElemType getLeftChild(SqBiTree T, CElemType e) {
// 樹空判斷
if (T[0]==Nil) { return Nil; }
// 找到e的位置
for (int i=0; i<MAXSIZE-1; i++) {
if (T[i]==e) {
return T[i*2+1];
}
}
return Nil;
}
/// 獲取某個(gè)節(jié)點(diǎn)的右孩子
/// @param T 樹
/// @param e 節(jié)點(diǎn)值
CElemType getRightChild(SqBiTree T, CElemType e) {
// 樹空判斷
if (T[0]==Nil) { return Nil; }
// 找到e的位置
for (int i=0; i<MAXSIZE-1; i++) {
if (T[i]==e) {
return T[i*2+2];
}
}
return Nil;
}
2.9 獲取某節(jié)點(diǎn)的兄弟節(jié)點(diǎn)(左右)
首先根據(jù)節(jié)點(diǎn)值找到節(jié)點(diǎn)切省,如果是找做兄弟則該節(jié)點(diǎn)必須是個(gè)右節(jié)點(diǎn)最岗,反之也是。
/// 獲取某個(gè)節(jié)點(diǎn)的左兄弟
/// @param T 樹
/// @param e 節(jié)點(diǎn)值
CElemType GetLeftSibling(SqBiTree T, CElemType e){
// 樹空判斷
if (T[0]==Nil) { return Nil; }
// 找到e的位置
for (int i=1; i<MAXSIZE-1; i++) {
// 值相等朝捆,并且找到的位置是右孩子般渡,不然沒有左兄弟
if (T[i]==e && i%2==0) {
return T[i-1];
}
}
return Nil;
}
/// 獲取某個(gè)節(jié)點(diǎn)的右兄弟
/// @param T 樹
/// @param e 節(jié)點(diǎn)值
CElemType GetRightSibling(SqBiTree T, CElemType e){
// 樹空判斷
if (T[0]==Nil) { return Nil; }
// 找到e的位置
for (int i=1; i<MAXSIZE-1; i++) {
// 值相等,并且找到的位置是左孩子芙盘,不然沒有右兄弟
if (T[i]==e && i%2==1) {
return T[i+1];
}
}
return Nil;
}
3. 二叉樹的遍歷
3.1 前序遍歷
前序遍歷(DLR/NLR)驯用,是二叉樹遍歷的一種,也叫做先序遍歷儒老。前序遍歷首先訪問根結(jié)點(diǎn)然后遍歷左子樹晨汹,最后遍歷右子樹。
遍歷結(jié)果為:ABCDGHCEIF
實(shí)現(xiàn)代碼:
/// 前序遍歷
/// @param T 樹
/// @param e 節(jié)點(diǎn)
void PreTraverse(SqBiTree T, int e) {
// 打印節(jié)點(diǎn)
printf("%d ",T[e]);
// 前序遍歷左子樹
if (T[2*e+1] != Nil) {
PreTraverse(T, 2*e+1);
}
// 前序遍歷右子樹
if (T[2*e+2] != Nil) {
PreTraverse(T, 2*e+2);
}
}
/// 前序遍歷封裝
/// @param T 樹
Status PreOrderTraverse(SqBiTree T){
//樹不為空
if (!BiTreeIsEmpty(T)) {
PreTraverse(T, 0);
}
printf("\n");
return OK;
}
3.2 中序遍歷
中序遍歷(LDR/LNR)是二叉樹遍歷的一種贷盲。在二叉樹中,中序遍歷首先遍歷左子樹剥扣,然后訪問根結(jié)點(diǎn)巩剖,最后遍歷右子樹。
遍歷結(jié)果為:GDHBAEICF
實(shí)現(xiàn)代碼:
/// 中序遍歷
/// @param T 樹
/// @param e 節(jié)點(diǎn)
void InTraverse(SqBiTree T, int e){
// 如果傳入節(jié)點(diǎn)的左子樹不為空就一直尋找
if (T[2*e+1] != Nil) {
InTraverse(T, 2*e+1);
}
// 打印節(jié)點(diǎn)
printf("%d ",T[e]);
// 如果右子樹也不為空則從該節(jié)點(diǎn)的右子樹繼續(xù)向下遍歷
if (T[2*e+2] != Nil) {
InTraverse(T, 2*e+2);
}
}
/// 中序遍歷的包裝
/// @param T 樹
Status InOrderTraverse(SqBiTree T){
// 樹不為空則開始遍歷
if (!BiTreeIsEmpty(T)) {
InTraverse(T, 0);
}
printf("\n");
return OK;
}
3.3后序遍歷
后序遍歷(LRD/LRN)二叉樹遍歷的一種钠怯。在二叉樹中佳魔,先左后右再根,即首先遍歷左子樹晦炊,然后遍歷右子樹鞠鲜,最后訪問根結(jié)點(diǎn)。
遍歷結(jié)果為:GHDBIEFCA
實(shí)現(xiàn)代碼:
/// 后序遍歷
/// @param T 樹
/// @param e 節(jié)點(diǎn)
void PostTraverse(SqBiTree T, int e){
// 如果傳入節(jié)點(diǎn)的左子樹不為空就一直尋找
if (T[2*e+1] != Nil) {
PostTraverse(T, 2*e+1);
}
// 如果右子樹也不為空則從該節(jié)點(diǎn)的右子樹繼續(xù)向下遍歷
if (T[2*e+2] != Nil) {
PostTraverse(T, 2*e+2);
}
// 打印節(jié)點(diǎn)
printf("%d ",T[e]);
}
/// 后續(xù)遍歷的包裝
/// @param T 樹
Status PostOrderTraverse(SqBiTree T)
{
if(!BiTreeIsEmpty(T)){
PostTraverse(T,0);
}
printf("\n");
return OK;
}
3.4 層序遍歷
層序遍歷二叉樹遍歷的一種断国。即從根節(jié)點(diǎn)開始從左至右一層一層的遍歷每個(gè)節(jié)點(diǎn)贤姆。
遍歷結(jié)果為:ABCDEFGHI
實(shí)現(xiàn)代碼:
/// 層序遍歷
/// @param T ??
void LevelOrderTraverse(SqBiTree T) {
// 初始化一個(gè)遍歷存儲(chǔ)值
int i = MAX_TREE_SIZE-1;
// 找到最后一個(gè)有值的
while (T[i] == Nil) { i--; }
// 遍歷打印
for (int j = 0; j<=i; j++) {
if (T[j] != Nil) {
printf("%d ", T[j]);
}
}
printf("\n");
}
4 二叉樹的鏈?zhǔn)酱鎯?chǔ)實(shí)現(xiàn)
詳細(xì)細(xì)節(jié)見注釋,主要通過插入的時(shí)候進(jìn)行前序方法進(jìn)行設(shè)計(jì)稳衬,使用'#'作為判斷到達(dá)葉子節(jié)點(diǎn)的條件霞捡,來實(shí)現(xiàn)葉子節(jié)點(diǎn)的置空操作。
代碼實(shí)現(xiàn):
#include <stdio.h>
#include "string.h"
#include "stdlib.h"
#include "math.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
/* 存儲(chǔ)空間初始分配量 */
#define MAXSIZE 100
/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼薄疚,如OK等 */
typedef int Status;
char *chars;
int indexs = 0;
typedef char CElemType;
CElemType Nil=' '; /* 字符型以空格符為空 */
typedef struct BiTNode /* 結(jié)點(diǎn)結(jié)構(gòu) */
{
CElemType data; /* 結(jié)點(diǎn)數(shù)據(jù) */
struct BiTNode *lchild,*rchild; /* 左右孩子指針 */
}BiTNode,*BiTree;
/// 初始化
/// @param T 樹
Status InitBiTree(BiTree *T) {
*T = NULL;
return OK;
}
/// 創(chuàng)建一棵鏈?zhǔn)綐?/// @param T 樹
void CreateBiTree(BiTree *T){
// 使用一個(gè)全局?jǐn)?shù)組來實(shí)現(xiàn)初始化的插入
CElemType c = chars[indexs++];
printf("%c ", c);
/*
1.如果第一個(gè)節(jié)點(diǎn)就是占位的則樹就是空的
2.由于葉子節(jié)點(diǎn)沒有孩子碧信,為了占有其左右孩子指針赊琳,通過#來判斷,
如遇#其孩子指針為空
3.通過#號(hào)來結(jié)束插入砰碴,初始化構(gòu)建的時(shí)候需要設(shè)計(jì)好順序
*/
if (c == '#') {
*T = NULL;
} else {
*T = (BiTree)malloc(sizeof(BiTNode));
// 如果沒創(chuàng)建成功就報(bào)錯(cuò)
if (!*T) { exit(OVERFLOW); }
// 賦值
(*T)->data = c;
// 構(gòu)造左子樹
CreateBiTree(&(*T)->lchild);
// 構(gòu)造右子樹
CreateBiTree(&(*T)->rchild);
}
}
/// 判斷是否是空樹
/// @param T 樹
Status BiTreeIsEmpty(BiTree T)
{
if (T == NULL) {
return TRUE;
} else {
return FALSE;
}
}
/// 獲取根節(jié)點(diǎn)的值
/// @param T 樹
CElemType GetRoot(BiTree T) {
if (BiTreeIsEmpty(T)) {
return Nil;
}
return T->data;
}
/// 銷毀二叉樹
/// @param T 樹
void DestroyBiTree(BiTree *T){
// 空就不銷毀了
if (*T) {
// 銷毀左孩子
if ((*T)->lchild) {
DestroyBiTree(&(*T)->lchild);
}
// 銷毀右孩子
if ((*T)->lchild) {
DestroyBiTree(&(*T)->rchild);
}
// 釋放根節(jié)點(diǎn)
free(*T);
// 置空
*T = NULL;
}
}
/// 二叉樹深度
/// @param T 樹
int BiTreeDepth(BiTree T){
/*
由于存在左子樹和右子樹躏筏,那個(gè)最深需要比較一下
*/
int i,j;
// 計(jì)算左孩子的深度
if (T->lchild) {
i = BiTreeDepth(T->lchild);
} else { i=0; }//沒有左子樹i為0
// 計(jì)算有子樹的深度
if (T->rchild) {
j = BiTreeDepth(T->rchild);
} else { j=0; }//沒有左子樹i為0
return i>j?i+1:j+1;
}
/// 獲取某個(gè)節(jié)點(diǎn)的值
/// @param p 節(jié)點(diǎn)
CElemType GetValue(BiTree p) {
return p->data;
}
/// 設(shè)置某個(gè)節(jié)點(diǎn)的值
/// @param p 節(jié)點(diǎn)
/// @param value 值
void SetValue(BiTree p,CElemType value){
p->data = value;
}
/// 前序遍歷
/// @param T 二叉樹
void PreOrderTraverse(BiTree T){
// 非空判斷
if (T==NULL) { return; }
// 打印
printf("%c ", T->data);
// 遍歷左子樹
PreOrderTraverse(T->lchild);
// 遍歷右子樹
PreOrderTraverse(T->rchild);
}
/// 中序遍歷
/// @param T 二叉樹
void InOrderTraverse(BiTree T){
// 非空判斷
if (T==NULL) { return; }
// 遍歷左子樹
InOrderTraverse(T->lchild);
// 打印
printf("%c ", T->data);
// 遍歷右子樹
InOrderTraverse(T->rchild);
}
/// 后序遍歷
/// @param T 二叉樹
void PostOrderTraverse(BiTree T){
// 非空判斷
if (T==NULL) { return; }
// 遍歷左子樹
PostOrderTraverse(T->lchild);
// 遍歷右子樹
PostOrderTraverse(T->rchild);
// 打印
printf("%c ", T->data);
}
int main(int argc, const char * argv[]) {
// insert code here...
BiTree T;
InitBiTree(&T);
chars = "ABDH#K###E##CFI###G#J##";
CreateBiTree(&T);
printf("二叉樹是否為空%d\n", BiTreeIsEmpty(T));
printf("樹的深度是%d\n", BiTreeDepth(T));
printf("樹根的值是%c\n", GetRoot(T));
printf("\n前序遍歷二叉樹:");
PreOrderTraverse(T);
printf("\n中序遍歷二叉樹:");
InOrderTraverse(T);
printf("\n后序遍歷二叉樹:");
PostOrderTraverse(T);
printf("\n");
return 0;
}