數據結構系列 二叉樹的遍歷(順序存儲)

https://blog.csdn.net/lin20044140410/article/details/89436835 二叉樹鏈式存儲

數據結構-樹

樹是一種一對多的數據結構,是n(n>=0)個結點的有限集。N=0時爲空樹。在任意一棵非空樹中:1,有且只有一個特定的稱爲根root的結點,2,當n>1時,其餘結點可分爲m (m>0)個互不相交的有限集T1,T2,,,,Tm,其中每一個集合本身又是一棵樹,並且稱爲根的子樹。

 

一些概念:

  1. 結點擁有的子樹數稱爲階段的度,度爲0的結點稱爲葉結點Leaf,度不爲0的結點稱爲分支結點。樹的度是樹內個結點的度的最大值。
  2. 結點的層次,從根開始,根爲第一層,根的孩子爲第二層,樹中結點的最大層次稱爲樹的深度,或者高度。

 

二叉樹

二叉樹是一種特殊結構的樹,是n個結點的有限集合,或者爲空樹,或者由一個根結點和兩棵互不相交的、分別稱爲左子樹和右子樹的二叉樹組成。

二叉樹的特點:

  1. 每個結點最多兩棵子樹,
  2. 左子樹、右子樹是有順序的,次序不能顛倒,
  3. 即使樹中某結點只有一棵子樹,也要區分它是左子樹還是右子樹。

 

特殊的二叉樹

斜樹,所有的結點都只有左子樹的二叉樹叫左斜樹,所有結點都只有右子樹的二叉樹叫右斜樹。斜樹,其實就是線性表結構。

 

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

滿二叉樹特點:

  1. 葉子只能出現在最下一層,
  2. 非葉子結點的度一定是2
  3. 在同樣深度的二叉樹中,滿二叉樹的結點個數最多,葉子最多。

 

完全二叉樹

對一棵具有n個結點的二叉樹按層序編號,如果編號爲i(1<=i<=n)的結點與同樣深度的滿二叉樹中編號爲i的結點在二叉樹中位置完全相同,則這棵二叉樹稱爲完全二叉樹。

滿二叉樹一定是一棵完全二叉樹,但是完全二叉樹不一定是滿的。

完全二叉樹的所有結點與同樣深度的滿二叉樹,它們按層序編號相同的結點,是一一對應的。

上圖是一棵完全二叉樹。

圖中樹1,樹2,樹3因爲編號不連續,都不是完全二叉樹,

完全二叉樹特點:

  1. 葉子結點只能出現在最下兩層。
  2. 最下層葉子結點一定集中在左部連續位置。
  3. 倒數第二層,如有葉子結點,一定都在右部連續位置。
  4. 如果結點度爲1,則該節點只有左孩子,不存在只有右子樹的情況。
  5. 同樣結點數的二叉樹,完全二叉樹深度最小。

 

判斷一棵二叉樹是不是完全二叉樹,就是給每個結點按照滿二叉樹的結構逐層編號,如果編號出現空擋,就不是完全二叉樹。

 

二叉樹的性質

  1. 在二叉樹的第i層,至多有2(i-1) (i>=1)
  2. 深度是k的二叉樹,最多有2k -1 個結點 k>=1.

以上兩點都可以通過數據歸納法來得出結論。

  1. 任意一棵二叉樹,如果終端結點數爲n0,度爲2的結點數爲n2,則n0 = n2 +1,
  2. 具有n個結點的完全二叉樹的深度爲log2n +1
  3. 如果對一棵有n個結點的完全二叉樹(深度爲log2n +1)的結點,按層序編號(從第1層到第log2n +1 層,每層從左到右),對於任一結點i(1<=I <=n):

如果i = 1,結點i就是二叉樹的根,無雙親,如果i>1,則其雙親是結點 i/2 (向下取整)。

如果2i >n,則結點i無左孩子,結點i爲葉子結點;否則其左孩子是結點2i。

如果2i + 1 > n,則結點i無右孩子,否則其右孩子是結點2i + 1。

 

依據以上性質,來定義二叉樹的存儲結構,先說順序存儲。

二叉樹的順序存儲結構就是用一維數組存儲二叉樹中的結點,結點的存儲位置,就是數組的下標,相應位置不存在的結點設置爲null。這種存儲結構,適用於完全二叉樹,因爲對於極端的右斜樹,它有k個結點,卻要分配2k -1個存儲空間,這是對存儲空間的浪費。

 

二叉樹的鏈式存儲結構,每個結點最多兩個孩子,所以這種存儲結構,一個數據域,兩個指針域,兩個指針域存放指向左孩子和右孩子的指針,稱這樣的存儲結構爲二叉鏈表。

 

二叉樹的遍歷

對二叉樹的遍歷,就是把樹中的結點變成某種意義的線性序列。

遍歷算法就是遞歸。

  1. 前序遍歷,若二叉樹爲null,則空操作返回,否則先訪問根結點,然後前序遍歷左子樹,在前序遍歷右子樹。
  2. 中序遍歷,若樹爲null,則空操作返回,否則從根結點開始,中序遍歷左子樹,然後訪問根結點,最後中序遍歷右子樹。
  3. 後序遍歷,若樹爲null,則空操作返回,否則從左到右,先葉子後結點的方式,先遍歷訪問左子樹,再同樣從左到右,先葉子後結點的方式,遍歷右子樹,最後訪問根結點。
  4. 層序遍歷,若樹爲null,則空操作返回,否則從樹的第一層根結點開始訪問,從上到下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問。

 

從遍歷操作可以得出,

已知前序遍歷序列和中序遍歷序列,可以唯一確定一棵二叉樹;

已知後序遍歷序列和中序遍歷序列,可以唯一確定一棵二叉樹;

這兩種情況都可以確定根結點,並且可以確定左子樹或者右子樹。

但是,已知前序和後序序列,是不能確定一棵二叉樹的,因爲雖然根結點可以確定,但是無法確定那個結點是左子樹,那個結點是右子樹。

二叉樹順序存儲代碼實現:

#ifndef DATA_STRUCTURE_BINARY_TREE_CLASS_H
#define DATA_STRUCTURE_BINARY_TREE_CLASS_H 

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 //存儲空間初始分配量
#define MAX_TREE_SIZE 100 //二叉樹最大結點樹

typedef int Status; //表示函數結果的狀態碼
typedef int TElemType; //樹節點的數據類型,暫定int
typedef TElemType SqBiTree[MAX_TREE_SIZE]; //順序存儲結構數組

typedef struct {
    int level;//節點的層
    int order;//本層的序號,按滿二叉樹計算
}Position;

TElemType Nil = 0; //表示空元素

#endif

 

#include "binaryTree.h"

#include "iostream"
#include "cstdlib"
#include "cmath"
using namespace std;
Status visit(TElemType c) {
   cout << "node:" << c <<endl;
}

//構造空二叉樹
Status InitBiTree(SqBiTree T) {
	int i;
	for (i = 0; i< MAX_TREE_SIZE; i++) {
		T[i] = Nil;//空值
	}

	return OK;
}

Status CreateBiTree(SqBiTree T) {
	int i=0;
	cout<< "按層序輸入結點的值,整形數,0表示空結點,結點數 <=" <<
		MAX_TREE_SIZE <<endl;
	//對前10個結點賦值
	while (i < 10) {
		T[i] = i+1;
		cout << "結點:" << i <<",值:"<<    T[i] << endl;
		//對於非根結點,一定有雙親,如果沒有就出錯了
		if (i != 0 && T[(i+1)/2 -1] == Nil && T[i] != Nil) {
			cout << "無雙親的非根結點:" <<         T[i] << endl;
			exit(ERROR);
		}
		i++;
	}

	//其餘結點賦值空
	while(i < MAX_TREE_SIZE) {
		T[i] = Nil;
		i++;
	}	
	return OK;
}

//判空樹
Status BiTreeEmpty(SqBiTree T) {
    //如果根結點爲空,即空樹
	if (T[0] == Nil) {
		return TRUE;
	} else {
		return FALSE;
	}
}

//獲取樹的深度
int BiTreeDepth(SqBiTree T) {
	int i,j=-1;
	for (i = MAX_TREE_SIZE-1; i>=0; i--) {
		if (T[i] != Nil) {//找到最後一個不爲空的結點
			break;
		}
	}
	//因爲在找到最後一個非空結點後,for又做了一次i--,所以要在加回去
	i++;
	//根據前面的性質,深度爲k二叉樹,最多有2k(2的k次冪) -1 個結點,
	//現在知道結點數,反推深度
	do{
	    j++;
	}while (i>=powl(2,j));

	return j;
}

//獲取根結點
Status Root(SqBiTree T, TElemType *e) {
	if (BiTreeEmpty(T)) {
		return ERROR;
	} else {
		*e = T[0];
		return OK;
	}
}

//獲取指定位置的結點,參數e包含結點所在的層,及本層的序號。
TElemType Value(SqBiTree T, Position e) {
	return T[(int)powl(2, e.level -1) + e.order -2];
}

//給指定位置的結點賦值
Status Assign(SqBiTree T, Position e, TElemType value) {
	//獲取數組的序號
	int i = (int)powl(2, e.level -1) + e.order -2;
	if (value != Nil && T[(i+1)/2-1] == Nil) {
		//非根結點,無雙親
		return ERROR;
	} else if (value == Nil && (T[i*2+1] !=Nil || T[i*2+2] !=Nil)) {
		//左孩子或者右孩子不爲空,就不能給其雙親賦值空
		return ERROR;
	}
	T[i] = value;
	return OK;
}

//返回結點的雙親
TElemType Parent(SqBiTree T, TElemType e) {
	int i;
	if (T[0] == Nil) {
		return Nil;
	}
	for (i = 1; i<MAX_TREE_SIZE-1; i++) {
		if (T[i] == e) {
			return T[(i+1)/2-1];
		}
	}
	return Nil;
}

//返回左孩子
TElemType LeftChild(SqBiTree T, TElemType e) {
	int i;
	if (T[0] == Nil) {
		return Nil;
	}
	for (i =0; i<= MAX_TREE_SIZE -1; i++) {
		if (T[i] == e) {
			return T[i*2 +1];
		}
	}
	return Nil;
}

//返回右孩子
TElemType RightChild(SqBiTree T, TElemType e) {
	int i;
	if (T[0] == Nil) {
		return Nil;
	}
	for (i =0; i<= MAX_TREE_SIZE -1; i++) {
		if (T[i] == e) {
			return T[i*2 +2];
		}
	}
	return Nil;
}

//前序遍歷
void PreTraverse(SqBiTree T, int e) {
	visit(T[e]);
	if (T[2*e +1] != Nil) {
		PreTraverse(T, 2*e+1);
	}
	if (T[2*e +2] != Nil) {
		PreTraverse(T, 2*e+2);
	}
}

Status PreOrderTraverse(SqBiTree T) {
	cout << "前序遍歷。。。" << endl;
	if (!BiTreeEmpty(T)) {
		PreTraverse(T, 0);
	}
	return OK;
}

//中序遍歷
void InTraverse(SqBiTree T, int e) {
	if (T[2*e +1] != Nil) {
		InTraverse(T, 2*e+1);
	}
	visit(T[e]);
	if (T[2*e +2] != Nil) {
		InTraverse(T, 2*e+2);
	}
}

Status InOrderTraverse(SqBiTree T) {
	cout << "中序遍歷。。。" << endl;
	if (!BiTreeEmpty(T)) {
		InTraverse(T, 0);
	}
	return OK;
}

//後序遍歷
void PostTraverse(SqBiTree T, int e) {
	if (T[2*e +1] != Nil) {
		PostTraverse(T, 2*e+1);
	}
	if (T[2*e +2] != Nil) {
		PostTraverse(T, 2*e+2);
	}
	visit(T[e]);	
}

Status PostOrderTraverse(SqBiTree T) {
	cout << "後序遍歷。。。" << endl;
	if (!BiTreeEmpty(T)) {
		PostTraverse(T, 0);
	}
	return OK;
}

//層序遍歷
void LevelOrderTraverse(SqBiTree T) {
	int i = MAX_TREE_SIZE -1;
	cout << "層序遍歷。。。" << endl;
	while (T[i] == Nil) {
		i--;//查找最後一個非空結點
	}
	cout << "最後一個非空結點(數組小標從0開始):" << i << endl;
	for (int j=0; j<=i; j++) {
		if(T[j] != Nil) {
			visit(T[j]);
		}
	}
	cout << endl;
}

//按層輸出結點
void LevelOrderVisist(SqBiTree T) {
	int j,k;
	Position p;
	TElemType e;
	cout << "按層輸出結點。。。" << endl;
	for (j=1;j<=BiTreeDepth(T);j++) {
		cout <<"第 "<<j <<"層:" <<endl;
		for (k=1;k<=powl(2,j-1);k++) {
			p.level = j;
			p.order =k;
			e= Value(T,p);
			if (e != Nil) {
				cout << k << ":" << e << endl;
			}
		}
		cout <<endl;
	}
}

int main() {
	Status i;
	Position p;
	TElemType e;
	SqBiTree T;
	InitBiTree(T);
	CreateBiTree(T);
	cout << "新建的二叉樹,深度:" << BiTreeDepth(T) << endl;

	i = Root(T, &e);
	cout << "二叉樹的根:" << e << endl;
	LevelOrderVisist(T);
	LevelOrderTraverse(T);
	PreOrderTraverse(T);
	InOrderTraverse(T);
	PostOrderTraverse(T);
	
	p.level = 3;
	p.order = 2;
	e = Value(T, p);
	cout << "修改第3層第2個結點的值,原值:"<< e << "新值爲50." << endl;
	e = 50;
	Assign(T, p, e);
	e = Value(T, p);
	cout << "修改後的第3層第2個結點的值:"<< e  << endl;
	PreOrderTraverse(T);

	return 0;
}

/**output/

$ g++ -g BinaryTree.cpp -o BiTree
$ ./BiTree

按層序輸入結點的值,整形數,0表示空結點,結點數 <=100
結點:0,值:1
結點:1,值:2
結點:2,值:3
結點:3,值:4
結點:4,值:5
結點:5,值:6
結點:6,值:7
結點:7,值:8
結點:8,值:9
結點:9,值:10
新建的二叉樹,深度:4
二叉樹的根:1
按層輸出結點。。。
第 1層:
1:1

第 2層:
1:2
2:3

第 3層:
1:4
2:5
3:6
4:7

第 4層:
1:8
2:9
3:10

層序遍歷。。。
最後一個非空結點(數組小標從0開始):9
node:1
node:2
node:3
node:4
node:5
node:6
node:7
node:8
node:9
node:10

前序遍歷。。。
node:1
node:2
node:4
node:8
node:9
node:5
node:10
node:3
node:6
node:7
中序遍歷。。。
node:8
node:4
node:9
node:2
node:10
node:5
node:1
node:6
node:3
node:7
後序遍歷。。。
node:8
node:9
node:4
node:10
node:5
node:2
node:6
node:7
node:3
node:1
修改第3層第2個結點的值,原值:5新值爲50.
修改後的第3層第2個結點的值:50
前序遍歷。。。
node:1
node:2
node:4
node:8
node:9
node:50
node:10
node:3
node:6
node:7

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章