本章我們來了解一下二叉樹這一概念。
目錄
1.樹概念及結構
1.1樹的概念???????
1.2 樹的特點:
1.3?樹的相關概念
1.4?樹的表示???????
1.5?樹在實際中的運用(表示文件系統的目錄樹結構)
2.二叉樹概念及結構
2.1概念
2.3 特殊的二叉樹:
????????1. 滿二叉樹:
????????2. 完全二叉樹:
2.4 二叉樹的性質
2.5 二叉樹的存儲結構
1. 順序存儲
2. 鏈式存儲
3.二叉樹的順序結構及實現
3.1 二叉樹的順序結構
3.2 堆的概念及結構
3.3 堆的實現
3.2.1 堆向下調整算法
3.2.2堆的創建
3.2.3 建堆時間復雜度???????
3.2.4?堆中元素調整
3.2.5?堆的插入
3.2.6?堆中元素的判斷
3.2.7?堆中元素的刪除
3.2.8?堆的摧毀
3.2.9?堆的頭文件
3.3?堆的應用
3.3.1 堆排序
3.3.2 TOP-K問題
4.二叉樹鏈式結構的實現
4.1 前置說明
4.2二叉樹的遍歷
4.2.1 前序、中序以及后序遍歷
4.2.2 層序遍歷
4.3 節點個數以及高度等
4.4?二叉樹的創建和銷毀
4.5?二叉樹頭文件
1.樹概念及結構
1.1樹的概念
樹是一種非線性的數據結構,它是由n(?n>=0)個有限結點組成一個具有層次關系的集合。 ?把它叫做樹是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。? ???????
1.2 樹的特點:
? ? ? ? ? (1) 所有樹都有一個特殊的結點,稱為根結點,它是整個樹的起點,其他節點都是從根節點開始向下延伸的。
? ? ? ? ? (2) 每個節點在樹中都是唯一的,每個節點都可以通過其在樹中的位置被唯一地標識。
? ????????(3)?樹形結構的節點之間呈現出層級關系,每個節點可以有多個子節點,但只能有一個父節點,除了根節點沒有父節點。
? ? ? ? ? (4)在數據結構樹中,每個節點都不能構成環,即不存在一個節點的子孫節點中存在其祖先節點的情況。
? ????????(5)?樹是遞歸定義的。
注意:樹形結構中,子樹之間不能有交集,否則就不是樹形結構。
1.3?樹的相關概念
1.節點的度?:一個節點含有的子樹的個數稱為該節點的度; ??如上圖: ?A的為6。
2.葉節點或終端節點:度為0的節點稱為葉節點; ??如上圖: ?B、C、?H、?I...等節點為葉節點。
3.非終端節點或分支節點:度不為0的節點; ??如上圖: ?D、?E、?F、G...等節點為分支節點。
4.雙親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點; ??如上圖: ?A是B的父節點。
5.孩子節點或子節點?:一個節點含有的子樹的根節點稱為該節點的子節點; ??如上圖: ?B是A的孩子節點。
6.兄弟節點:具有相同父節點的節點互稱為兄弟節點; ??如上圖: ?B、C是兄弟節點。
7.樹的度?:一棵樹中,最大的節點的度稱為樹的度; ??如上圖:樹的度為6。
8.節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
9.樹的高度或深度:樹中節點的最大層次; ??如上圖:樹的高度為4。
10.堂兄弟節點:雙親在同一層的節點互為堂兄弟;如上圖:??H、?I互為兄弟節點。
11.節點的祖先:從根到該節點所經分支上的所有節點;如上圖: ?A是所有節點的祖先。
12.子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。如上圖:所有節點都是A的子孫。
13.森林:由?m(?m>0)棵互不相交的樹的集合稱為森林;
1.4?樹的表示
typedef int DataType;
struct Node
{struct Node* _firstChild1; // 第一個孩子結點struct Node* _pNextBrother; // 指向其下一個兄弟結點DataType _data; // 結點中的數據域
};
1.5?樹在實際中的運用(表示文件系統的目錄樹結構)

2.二叉樹概念及結構
2.1概念


2.3 特殊的二叉樹:
????????1. 滿二叉樹:
????????2. 完全二叉樹:

2.4 二叉樹的性質
1. 若規定根節點的層數為 1 ,則一棵非空二叉樹的 第 i 層上最多有個結點。
2. 若規定根節點的層數為 1 ,則 深度為 h 的二叉樹的最大結點數是。
3. 對任何一棵二叉樹 , 如果度為 0 其葉結點個數為, 度為 2 的分支結點個數為
,則有
=
+1
4. 若規定根節點的層數為 1 ,具有 n 個結點的滿二叉樹的深度 ,。(ps:
是log 以 2為底,n+1 為對數 )
5. 對于具有 n 個結點的完全二叉樹,如果按照從上至下從左至右的數組順序對所有節點從 0 開始編號,則對于序號為i 的結點有:????????1. 若 i>0 , i 位置節點的雙親序號: (i-1)/2 ; i=0 , i 為根節點編號,無雙親節點。????????2. 若 2i+1<n ,左孩子序號: 2i+1 , 2i+1>=n 否則無左孩子。????????3. 若 2i+2<n ,右孩子序號: 2i+2 , 2i+2>=n 否則無右孩子。
2.5 二叉樹的存儲結構
二叉樹一般可以使用兩種結構存儲,一種順序結構,一種鏈式結構。
1. 順序存儲

2. 鏈式存儲

typedef int BTDataType;
// 二叉鏈
struct BinaryTreeNode
{struct BinTreeNode* _pLeft; // 指向當前節點左孩子struct BinTreeNode* _pRight; // 指向當前節點右孩子BTDataType _data; // 當前節點值域
}
// 三叉鏈
struct BinaryTreeNode
{struct BinTreeNode* _pParent; // 指向當前節點的雙親struct BinTreeNode* _pLeft; // 指向當前節點左孩子struct BinTreeNode* _pRight; // 指向當前節點右孩子BTDataType _data; // 當前節點值域
};
3.二叉樹的順序結構及實現
3.1 二叉樹的順序結構

3.2 堆的概念及結構
3.3 堆的實現
3.2.1 堆向下調整算法
int array[] = {27,15,19,18,28,34,65,49,25,37};
3.2.2堆的創建
int a[] = {1,5,3,8,7,6};
// 小堆
//堆初始化函數
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}
3.2.3 建堆時間復雜度

3.2.4?堆中元素調整
為了便于插入元素的同時滿足堆的特性,我們需要寫一個調整堆中元素的函數:
//交換函數
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向上調整法
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//while (parent >= 0)while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;//child = (child - 1) / 2;//parent = (parent - 1) / 2;}else{break;}}
}//向下調整法
void AdjustDown(HPDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){// 假設左孩子小,如果解設錯了,更新一下if (child+1 < size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
???????
3.2.5?堆的插入

// O(logN)
//堆的插入函數
void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}
3.2.6?堆中元素的判斷
為了方便對堆中元素的狀況的判斷,我們還需要寫一下這些函數:
//獲取堆頂元素
HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}//獲取堆中元素的個數
size_t HeapSize(HP* php)
{assert(php);return php->size;
}//判斷堆是否為空
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}
3.2.7?堆中元素的刪除

//堆中元素的刪除
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}
3.2.8?堆的摧毀
在堆使用完畢后,我們需要摧毀已經建立的隊,釋放系統資源,釋放之前向系統申請的堆區域的空間。
//摧毀堆
void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}
3.2.9?堆的頭文件
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<time.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);void HeapDestroy(HP* php);void HeapPush(HP* php, HPDataType x);
// 規定刪除堆頂(根節點)
void HeapPop(HP* php);HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);void Swap(HPDataType* p1, HPDataType* p2);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int size, int parent);
3.3?堆的應用
3.3.1 堆排序

// 升序堆排
void HeapSort(int* a, int n)
{// 建大堆// O(N*logN)/*for (int i = 1; i < n; i++){AdjustUp(a, i);}*/// O(N)for (int i = (n-1-1)/2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}
3.3.2 TOP-K問題
//創建文件存入10000000個隨機數
void CreateNDate()
{// 造數據int n = 10000000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = (rand()+i) % 10000000;fprintf(fin, "%d\n", x);}fclose(fin);
}//堆排獲取文件中最大的k個數
void PrintTopK(const char* file, int k)
{FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");return;}// 建一個k個數小堆int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc error");return;}// 讀取前k個,建小堆for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);AdjustUp(minheap, i);}int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minheap[0]){minheap[0] = x;AdjustDown(minheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}printf("\n");free(minheap);fclose(fout);
}
4.二叉樹鏈式結構的實現
4.1 前置說明
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

4.2二叉樹的遍歷
4.2.1 前序、中序以及后序遍歷
按照規則,二叉樹的遍歷有: 前序 / 中序 / 后序的遞歸結構遍歷 :????????1. 前序遍歷 (Preorder Traversal 亦稱先序遍歷 )—— 訪問根結點的操作發生在遍歷其左右子樹之前。????????2. 中序遍歷 (Inorder Traversal)—— 訪問根結點的操作發生在遍歷其左右子樹之中(間)。????????3. 后序遍歷 (Postorder Traversal)—— 訪問根結點的操作發生在遍歷其左右子樹之后。????????由于被訪問的結點必是某子樹的根,所以 N(Node )、 L(Left subtree )和 R(Right subtree )又可解釋為 根、根的左子樹和根的右子樹 。 NLR 、 LNR 和 LRN 分別又稱為先根遍歷、中根遍歷和后根遍歷。
// 二叉樹前序遍歷
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){return;}printf("%c ", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){return;}BinaryTreeInOrder(root->left);printf("%c ", root->data);BinaryTreeInOrder(root->right);
}// 二叉樹后序遍歷
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);printf("%c ", root->data);
}


前序遍歷結果: 1 2 3 4 5 6
中序遍歷結果: 3 2 1 5 4 6
后序遍歷結果: 3 2 5 6 4 1
4.2.2 層序遍歷
// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root)
{Queue q; //這里創建了一個隊列,借助隊列先進先出的特性實現了層序QueueInit(&q); //隊列初始化if (root){QueuePush(&q, root); //元素入隊列}int levelsize = 1;while (!QueueEmpty(&q)) //隊列為空停止訓話{while (levelsize--) {BTNode* front = QueueFront(&q); //獲取隊頭元素QueuePop(&q); //刪除隊頭printf("%c ", front->data);if (front->left){QueuePush(&q, front->left); //元素入隊列}if (front->right){QueuePush(&q, front->right);}}printf("\n");levelsize = QueueSize(&q); //獲取隊列元素個數}printf("\n");QueueDestroy(&q); //摧毀隊列}
4.3 節點個數以及高度等
// 二叉樹節點個數
int BinaryTreeSize(BTNode* root)
{return root == NULL ? 0 : 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}// 二叉樹葉子節點個數
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k)
{assert(k > 0);if (root == NULL)return 0;if (k == 1)return 1;return TreeLevelK(root->left, k - 1)+ TreeLevelK(root->right, k - 1);
}// 二叉樹查找值為x的節點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data==x){return root;}BTNode* left = BinaryTreeFind(root->left,x);if (left){return left;}BTNode* right = BinaryTreeFind(root->right,x);if (right){return right;}return NULL;
}// 判斷二叉樹是否是完全二叉樹
int BinaryTreeComplete(BTNode* root)
{Queue q; //同樣借助隊列進行QueueInit(&q);QueuePush(&q, root);while(!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}QueuePush(&q, front->left);QueuePush(&q, front->right);}//如果節點為空,但隊列此時不為零while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){QueueDestroy(&q);return 0;}}QueueDestroy(&q);return 1;}
4.4?二叉樹的創建和銷毀
//創建二叉樹
BTNode *BinaryTreeCreate(BTDataType * src, int n, int* pi)
{if (*pi >= n || src[*pi] == '#'){(*pi)++;return NULL;}BTNode * cur = (BTNode *)malloc(sizeof(BTNode));cur->_data = src[*pi];(*pi)++;cur->left = BinaryTreeCreate(src, n, pi);cur->right = BinaryTreeCreate(src, n, pi);return cur;
}//摧毀二叉樹void BinaryTreeDestory(BTNode** root)
{if (*root){BinaryTreeDestory(&(*root)->left);BinaryTreeDestory(&(*root)->right);free(*root);*root = NULL;}
}
4.5?二叉樹頭文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;// 通過前序遍歷的數組"ABD##E#H##CF##G##"構建二叉樹
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉樹銷毀
void BinaryTreeDestory(BTNode** root);
// 二叉樹節點個數
int BinaryTreeSize(BTNode* root);
// 二叉樹葉子節點個數
int BinaryTreeLeafSize(BTNode* root);
// 二叉樹第k層節點個數
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉樹查找值為x的節點
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉樹前序遍歷
void BinaryTreePrevOrder(BTNode* root);
// 二叉樹中序遍歷
void BinaryTreeInOrder(BTNode* root);
// 二叉樹后序遍歷
void BinaryTreePostOrder(BTNode* root);
// 層序遍歷
void BinaryTreeLevelOrder(BTNode* root);
// 判斷二叉樹是否是完全二叉樹
int BinaryTreeComplete(BTNode* root);
本章結束!?