1. ??樹數據結構的基本定義和屬性??
樹是一種重要的非線性數據結構,用于表示層次關系。
- ??基本定義??:樹是由 n(n ≥ 0)個結點組成的有限集合。當 n = 0 時,稱為空樹;當 n > 0 時,樹必須滿足兩個條件:
- 有且僅有一個特定的根結點(root node)。
- 當 n > 1 時,其余結點可劃分為 m(m > 0)個互不相交的子集 T1, T2, ..., Tm,每個子集本身又是一棵樹,稱為子樹(subtree)。
- ??結點屬性??:
- ??結點的度(degree)??:指一個結點擁有的子樹個數。例如,一個結點有 3 個子樹,則其度為 3。
- ??葉結點(leaf node)??:度為 0 的結點,即沒有子樹的結點。
- ??分支結點(branch node)??:度不為 0 的結點,即至少有一個子樹的結點。
- ??樹的整體屬性??:
- ??樹的度數??:指樹中所有結點的度的最大值。例如,如果所有結點度的最大值為 3,則樹的度數為 3。
- ??樹的深度或高度??:從根結點開始計算,根所在層為第 1 層,其子結點為第 2 層,依此類推。深度反映了樹的層級結構。
- ??樹的性質強調??:樹的結構強調“互不相交”的子集,確保每個結點只屬于一個父結點,避免循環或重復。
2. ??樹的存儲結構??
- ??順序結構??:將樹結點存儲在連續的內存位置(如數組),通過索引表示父子關系。優點是訪問快速,但插入/刪除操作可能低效。
- ??鏈式結構??:使用指針或引用來鏈接結點(如鏈表),每個結點包含數據域和指向子樹的指針。優點是靈活,適用于動態樹結構。
3. ??二叉樹的定義和類型??
二叉樹是樹的特例,具有嚴格的子樹順序規則。
- ??基本定義??:二叉樹是 n 個結點的有限集合,它要么為空樹,要么由一個根結點和兩棵互不相交的左子樹與右子樹組成。關鍵特點包括:
- 每個結點最多有兩個子樹(左子樹和右子樹)。
- 左子樹和右子樹有固定順序,不能顛倒(例如,交換左右子樹會改變樹結構)。
- 如果結點只有一個子樹,必須明確指定是左子樹或右子樹。
- ??特殊二叉樹類型??:
- ??斜樹(skewed tree)??:所有結點都只有左子樹(左斜樹)或只有右子樹(右斜樹)。結構類似線性鏈表。
- ??滿二叉樹(full binary tree)??:所有分支結點都有左右子樹,且所有葉結點都在同一層上。結點總數達到最大值(2^k - 1,k 為深度)。
- ??完全二叉樹(complete binary tree)??:對樹按層序編號(根為 1),每個結點的編號與同深度的滿二叉樹中對應結點位置相同。不完全二叉樹在編號上會有空缺。
4. ??二叉樹的數學特性和遍歷方法??
- ??數學特性??:
- 第 i 層(i ≥ 1)最多有 2^(i-1) 個結點。
- 深度為 k 的二叉樹(k ≥ 1)最多有 2^k - 1 個結點。
- 關鍵公式:對于任意二叉樹,葉子結點數(n0)和度為 2 的結點數(n2)滿足 n0 = n2 + 1。
- 對于有 n 個結點的完全二叉樹,深度為 (log?(n)) + 1。
- ??遍歷方法??:遍歷是訪問樹中所有結點的過程,三種主要順序:
- ??前序遍歷(preorder traversal)??:順序為“根-左-右”。先訪問根結點,然后遞歸訪問左子樹,最后右子樹。
- ??中序遍歷(inorder traversal)??:順序為“左-根-右”。從根開始但不先訪問根;先遞歸訪問左子樹,再訪問根,最后右子樹。常用于二叉搜索樹(BST)。
- ??后序遍歷(postorder traversal)??:順序為“左-右-根”。從根開始但不先訪問根;先遞歸訪問左子樹,然后右子樹,最后根。
- ??層序遍歷(level order traversal)??:按層從上到下、從左到右訪問結點。
遍歷方法強調遞歸實現,適用于樹結構的遞歸特性。
5. ??GDB調試工具的常規使用步驟??
GDB(GNU Debugger)的實用指南,用于調試C/C++程序,特別是處理段錯誤和一般調試:
- ??調試段錯誤(segmentation fault)??:
- 編譯時添加調試選項:使用
gcc -g *.c
生成帶調試信息的可執行文件。 - 啟動GDB:運行
gdb a.out
。 - 運行程序:在GDB中輸入
r
(run)命令執行程序。 - 重現錯誤:讓程序運行到崩潰點。
- 定位錯誤:輸入
where
或bt
命令顯示調用棧,找出段錯誤發生的具體位置(如文件和行號)。
- 編譯時添加調試選項:使用
- ??一般調試流程??:
- 設置斷點:使用
b
命令(break),例如b fun.c:36
在文件 fun.c 的第 36 行設置斷點,或b myfun
在函數 myfun 入口處設斷點。 - 運行程序:輸入
r
開始執行,程序會在斷點處暫停。 - 單步執行:
n
(next):步過,執行下一行代碼(如果遇到函數,不進入函數內部)。s
(step):步入,執行下一行代碼(如果遇到函數,進入函數內部)。
- 查看變量:使用
p
(print)命令,例如p a
顯示變量 a 的值,或p *ptr
顯示指針 ptr 指向的數據。display a
可持續顯示變量變化。 - 控制執行:
c
(continue):從當前斷點繼續運行,直到下一個斷點或程序結束。return
:強制從當前函數返回調用處。
- 輔助命令:
set print elements 300
設置字符串顯示長度(避免截斷)。
- 設置斷點:使用
- ??關鍵優勢??:GDB 能通過 where 命令追蹤函數調用棧,幫助快速定位內存錯誤或邏輯問題。
6. ??希爾排序算法??
- ??算法原理??:希爾排序通過分組比較和插入來優化排序。它使用一個“間隙”(gap)序列,初始 gap 為數組長度的一半,逐步減小 gap 至 1(此時等價于插入排序)。核心是減少逆序對,提升效率。
- ??代碼實現??:
void shell_sort(int a[], int len) {for (int gap = len / 2; gap > 0; gap /= 2) { // gap 從 len/2 開始,每次減半for (int i = gap; i < len; ++i) // 從 gap 位置開始遍歷{ int temp = a[i]; // 保存當前元素int j = i;while (j >= gap && a[j - gap] > temp) { // 比較并移動元素a[j] = a[j - gap]; // 將較大元素后移j -= gap; // 跳轉到前一個 gap 位置}a[j] = temp; // 插入 temp 到正確位置}} }
- ??關鍵步驟??:
- ??外層循環??:控制 gap 值(gap = len/2, gap/2, ..., 1)。
- ??內層循環??:對每個 gap 組進行插入排序。從索引 gap 開始,將元素與同組前驅比較,移動元素以保持有序。
- ??效率??:希爾排序平均時間復雜度為 O(n log n),優于簡單插入排序 O(n2),適用于中等規模數據。
- ??應用場景??:常用于嵌入式系統或內存受限環境,因代碼簡潔高效。
代碼實現:
1.創建數
2.三種遍歷方法
3.以隊列形式遍歷
4..摧毀樹
頭文件
#ifndef _TREE_H_
#define TREE_H_typedef char DATATYPE;
typedef struct BiTNode /* 結點結構 */
{DATATYPE data; /* 結點數據 */struct BiTNode *lchild, *rchild; /* 左右孩子指針 */
} BiTNode;//定義隊列節點結構
typedef struct QueueNode
{// 指向樹節點的指針BiTNode *treeNode;// 指向下一個隊列節點struct QueueNode *next;
} QueueNode;//定義隊列結構
typedef struct Queue
{// 隊列頭部QueueNode *head;// 隊列尾部QueueNode *tail;
} Queue;extern void CreateTree(BiTNode **root);
extern void PreOrderTraverse(BiTNode *root);
extern void InOrderTraverse(BiTNode *root);
extern void PostOrderTraverse(BiTNode *root);
extern void DestroyTree(BiTNode *root); extern int isQueueEmpty(Queue *queue);
extern void enqueue(Queue *queue, BiTNode *treeNode);
extern BiTNode* dequeue(Queue *queue);
extern void each_of_tree(BiTNode *root);
extern void initQueue(Queue *queue);
#endif
函數
#include <stdio.h>
#include "tree.h"
#include <stdlib.h>char data[] = "Abd#g###ce#h##fi###";
int ind = 0;void CreateTree(BiTNode **root)
{char c = data[ind++];if ('#' == c){*root = NULL;return;}else{*root = malloc(sizeof(BiTNode));if (NULL == *root) /*判斷申請是否成功*/{printf("malloc error\n");*root = NULL;return;} (*root)->data = c;CreateTree(&(*root)->lchild); /*左*/CreateTree(&(*root)->rchild); /*右*/}return;
}
//根左右(前序)
void PreOrderTraverse(BiTNode *root)
{if (NULL == root){return;}else{printf("%c", root->data);PreOrderTraverse(root->lchild);PreOrderTraverse(root->rchild);}return;
}
//左根右(中序)
void InOrderTraverse(BiTNode *root)
{if (NULL == root){return;}InOrderTraverse(root->lchild);printf("%c", root->data);InOrderTraverse(root->rchild);return;
}
//左右根(后序)
void PostOrderTraverse(BiTNode *root)
{if (NULL == root){return;}PostOrderTraverse(root->lchild);PostOrderTraverse(root->rchild);printf("%c", root->data);return;
}
void DestroyTree(BiTNode *root)
{if (NULL == root){return;}DestroyTree(root->lchild);DestroyTree(root->rchild);free(root);root = NULL;return;
}// 初始化隊列
void initQueue(Queue *queue)
{queue->head = queue->tail = NULL;
}// 判斷隊列是否為空
int isQueueEmpty(Queue *queue)
{return queue->head == NULL;
}// 入隊
void enqueue(Queue *queue, BiTNode *treeNode)
{QueueNode *newNode = malloc(sizeof(QueueNode));newNode->treeNode = treeNode;newNode->next = NULL;if (isQueueEmpty(queue)) {queue->head = queue->tail = newNode;} else {queue->tail->next = newNode;queue->tail = newNode;}
}// 出隊
BiTNode* dequeue(Queue *queue)
{if (isQueueEmpty(queue)) {return NULL;}QueueNode *temp = queue->head;BiTNode *treeNode = temp->treeNode;queue->head = queue->head->next;if (queue->head == NULL) {queue->tail = NULL;}free(temp);return treeNode;
}// 層序遍歷
void each_of_tree(BiTNode *root)
{if (root == NULL) {return;}//定義對聯Queue queue;// 初始化隊列initQueue(&queue);// 根節點入隊enqueue(&queue, root);while (!isQueueEmpty(&queue)) {// 出隊BiTNode *current = dequeue(&queue);//訪問節點printf("%c \n", current->data);// 左子節點入隊if (current->lchild != NULL) {enqueue(&queue, current->lchild);}// 右子節點入隊if (current->rchild != NULL) {enqueue(&queue, current->rchild);}}
}
主函數
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tree.h"int main(int argc, char **argv)
{BiTNode *root;CreateTree(&root);PreOrderTraverse(root);printf("\n");InOrderTraverse(root);printf("\n");PostOrderTraverse(root);printf("\n");DestroyTree(root);root = NULL;each_of_tree(root);return 0;
}
7. ??整體總結??
- ??數據結構核心??:樹和二叉樹的定義、屬性、存儲及遍歷是重點,強調樹的結構特性(如度、深度)和二叉樹的具體規則(如左右子樹順序)。數學特性(如結點數量公式)和遍歷方法(前序、中序、后序)為算法實現奠定基礎。
- ??實用工具??:GDB 調試部分提供 step-by-step 指南,解決段錯誤和一般調試問題,突出命令如
where
、b
、p
的重要性。 - ??算法應用??:希爾排序作為高效排序算法,以代碼示例展示其 gap 策略和插入優化。