【資料結構】高效雙向連結串列list、樹tree(二叉樹)
阿新 • • 發佈:2019-01-27
vi正常模式下:
"shift + g" 跳到最後一行
"gg" 跳到第一行
<效率更高的雙向連結串列結構程式碼>
【樹】
如果單向線性鏈式物理結構中每個節點可以向後找到多個其他節點,就成為了一棵"樹"
樹裡的節點可以分成幾層,"不同層之間符合線性規律"(任何兩層之間都有前後順序)
樹的最上面一層只有一個節點,這個節點叫做"樹的根節點"
可以"用根節點代表整棵樹"
如果樹中兩個節點之間有直接的聯絡,就說明他們之間存在"父子關係"
靠近根節點的叫"父節點",遠離根節點的叫"子節點"
任何節點"最多隻能有一個父節點",但是"可以有多個子節點"
【二叉樹】
如果每個節點"最多隻有兩個"子節點,就把這種樹叫做"二叉樹"
二叉樹是最簡單的樹(所有的樹都可以轉化為二叉樹)
二叉樹裡"用左右區分"一個節點的兩個不同子節點
二叉樹裡任何一個節點都可以看作是一個二叉樹的根節點
如果二叉樹中一個節點叫 A ,它的左子節點叫 B 右子節點叫 C,則B所代表的二叉樹叫做A的"左子樹",C所代表的二叉樹叫做A的"右子樹"
樹的遍歷指的是:"依次處理樹裡的每一個節點"
樹的大多數操作都是通過遍歷實現的
通常採用"遞迴方式解決樹的遍歷"問題
在對樹進行遍歷的時候,一定"先遍歷左"子樹,然"後遍歷右"子樹
如果最先處理根節點叫做"前序遍歷"
如果中間處理根節點叫做"中序遍歷"
如果最後處理根節點叫做"後序遍歷"
【練習】
編寫函式計算一棵樹的高度
"shift + g" 跳到最後一行
"gg" 跳到第一行
<效率更高的雙向連結串列結構程式碼>
/*程式碼*/ 01link.c #include <stdlib.h> #include "01link.h" //連結串列初始化 void link_init(Link *p_link) { p_link->head.p_next = &(p_link->tail); p_link->tail.p_prev = &(p_link->head); p_link->tail.p_next = NULL; p_link->head.p_prev = NULL; p_link->p_cur = NULL; } //連結串列清理 void link_deinit(Link *p_link) { while(p_link->head.p_next != &(p_link->tail)) { Node *p_first = &(p_link->head); Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; p_first->p_next = p_last; p_last->p_prev = p_first; free(p_mid); p_mid = NULL; } p_link->p_cur = NULL; } //統計有效節點資料個數 int link_size(const Link *p_link) { int cnt = 0; const Node *p_node = NULL; for(p_node = &(p_link->head); p_node != &(p_link->tail); p_node = p_node->p_next) { const Node *p_first = p_node; const Node *p_mid = p_first->p_next; const Node *p_last = p_mid->p_next; cnt++; } return cnt - 1; } //把新數字加入到最前面,num代表要加入的數字 void link_add_head(Link *p_link, int num) { Node *p_first = NULL, *p_mid = NULL, *p_last = NULL; Node *p_node = (Node *)malloc(sizeof(Node)); if(!p_node) { return ; } p_node->num = num; p_node->p_next = NULL; p_node->p_prev = NULL; p_first = &(p_link->head); p_mid = p_first->p_next; p_last = p_mid->p_next; p_first->p_next = p_node; p_node->p_prev = p_first; p_node->p_next = p_mid; p_mid->p_prev = p_node; p_link->p_cur = NULL; } //把新數字加入到最後面(追加),num代表要加入的數字 void link_append(Link *p_link, int num) { Node *p_tmp = NULL; //迴圈指標變數 Node *p_node = (Node *)malloc(sizeof(Node)); if(!p_node) { return ; } p_node->num = num; p_node->p_next = NULL; p_node->p_prev = NULL; for(p_tmp = &(p_link->head); p_tmp != &(p_link->tail); p_tmp = p_tmp->p_next) { Node *p_first = p_tmp; Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; if(p_mid == &(p_link->tail)) { p_first->p_next = p_node; p_node->p_prev = p_first; p_node->p_next = p_mid; p_mid->p_prev = p_node; break; } } p_link->p_cur = NULL; } //按順序加入新數字 void link_insert_in_order(Link *p_link, int num) { Node *p_tmp = NULL; Node *p_node = (Node *)malloc(sizeof(Node)); if(!p_node) { return ; } p_node->num = num; p_node->p_next = NULL; p_node->p_prev = NULL; for(p_tmp = &(p_link->head); p_tmp != &(p_link->tail); p_tmp = p_tmp->p_next) { Node *p_first = p_tmp; Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; if(p_mid == &(p_link->tail) || p_mid->num > num) { p_first->p_next = p_node; p_node->p_prev = p_first; p_node->p_next = p_mid; p_mid->p_prev = p_node; break; } } p_link->p_cur = NULL; } //刪除最前面數字的函式 void link_remove_head(Link *p_link) { Node *p_first = &(p_link->head); Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; if(p_mid == &(p_link->tail)) { return ; } p_first->p_next = p_last; p_last->p_prev = p_first; free(p_mid); p_mid = NULL; p_link->p_cur = NULL; } //刪除最後一個數字的函式 void link_remove_tail(Link *p_link) { Node *p_node = NULL; for(p_node = &(p_link->head); p_node != &(p_link->tail); p_node = p_node->p_next) { Node *p_first = p_node; Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; if(p_last == &(p_link->tail)) { p_first->p_next = p_last; p_last->p_prev = p_first; free(p_mid); p_mid = NULL; break; } } p_link->p_cur = NULL; } //刪除某個數字的函式 void link_remove(Link *p_link, int num) { Node *p_node = NULL; for(p_node = &(p_link->head); p_node != &(p_link->tail); p_node = p_node->p_next) { Node *p_first = p_node; Node *p_mid = p_first->p_next; Node *p_last = p_mid->p_next; if(p_mid != &(p_link->tail) && p_mid->num == num) { p_first->p_next = p_last; p_last->p_prev = p_first; free(p_mid); p_mid = NULL; break; } } p_link->p_cur = NULL; } //獲得第一個數字的函式 int link_get_head(const Link *p_link, int *p_num) { if(p_link->head.p_next == &(p_link->tail)) { return 0; } else { *p_num = p_link->head.p_next->num; return 1; } } //獲得最後一個數字的函式 int link_get_tail(const Link *p_link, int *p_num) { const Node *p_node = NULL; if(p_link->head.p_next == &(p_link->tail)) { return 0; } for(p_node = &(p_link->head); p_node != &(p_link->tail); p_node = p_node->p_next) { const Node *p_first = p_node; const Node *p_mid = p_first->p_next; const Node *p_last = p_mid->p_next; if(p_last == &(p_link->tail)) { *p_num = p_mid->num; return 1; } } } //獲得某個編號的數字的函式,num表示編號,*p_num獲得的數字 int link_get(const Link *p_link, int num, int *p_num) { int cnt = 0; const Node *p_node = NULL; for(p_node = &(p_link->head); p_node != &(p_link->tail); p_node = p_node->p_next) { const Node *p_first = p_node; const Node *p_mid = p_first->p_next; const Node *p_last = p_mid->p_next; if(p_mid != &(p_link->tail) && cnt == num) { *p_num = p_mid->num; return 1; } cnt++; } return 0; } //開始從前向後遍歷(準備開始呼叫link_add_head函式) void link_begin(Link *p_link) { p_link->p_cur = &(p_link->head); } //在從前向後的遍歷過程中獲得下一個數字 int link_next(Link *p_link, int *p_num) { //如果指標是空就表示沒有資料 if(!(p_link->p_cur)) { return 0; } p_link->p_cur = p_link->p_cur->p_next; //指標向後移動一步 if(p_link->p_cur == &(p_link->tail)) { //指標和尾節點捆綁 p_link->p_cur = NULL; //指標恢復到開始之前的樣子 return 0; } else { *p_num = p_link->p_cur->num; //指標有效節點捆綁 return 1; } } //開始從後向前遍歷 void link_rbegin(Link *p_link) { p_link->p_cur = &(p_link->tail); } //在從後向前的遍歷過程中獲得下一個數字 int link_prev(Link *p_link, int *p_num) { if(!(p_link->p_cur)) { return 0; } p_link->p_cur = p_link->p_cur->p_prev; //指標向前移動一步 if(p_link->p_cur == &(p_link->head)) { p_link->p_cur = NULL; return 0; } else { *p_num = p_link->p_cur->num; return 1; } } /*程式碼*/ 01link.h #ifndef __01LINK_H__ #define __01LINK_H__ #include <stdlib.h> typedef struct Node{ int num; struct Node *p_next; struct Node *p_prev; //prev:上一個 } Node; typedef struct { Node head, tail; Node *p_cur; //隨著函式呼叫,該指標依次和每個數字捆綁 } Link; void link_init(Link *); //連結串列初始化 void link_deinit(Link *); //連結串列清理 int link_size(const Link *); //統計有效節點資料個數 //把新數字加入到最前面,num代表要加入的數字 void link_add_head(Link *, int); //把新數字加入到最後面(追加),num代表要加入的數字 void link_append(Link *, int); //按順序加入新數字 void link_insert_in_order(Link *, int); //刪除最前面數字的函式 void link_remove_head(Link *); //刪除最後一個數字的函式 void link_remove_tail(Link *); //刪除某個數字的函式 void link_remove(Link *, int); //獲得第一個數字的函式 int link_get_head(const Link *, int *); //獲得最後一個數字的函式 int link_get_tail(const Link *, int *); //獲得某個編號的數字的函式,num表示編號,*p_num獲得的數字 int link_get(const Link *, int, int *); //開始從前向後遍歷的函式 void link_begin(Link *); //在從前向後的遍歷中獲得下一個數字的函式 int link_next(Link *, int *); //在從後向前遍歷的函式 void link_rbegin(Link *); //在從後向前的遍歷過程中獲得下一個數字 int link_prev(Link *, int *); #endif //__01LINK_H__ /*程式碼*/ 01main.c #include <stdio.h> #include "01link.h" int main() { int num = 0; Link lnk = {0}; link_init(&lnk); link_append(&lnk, 3); link_append(&lnk, 5); link_append(&lnk, 7); link_append(&lnk, 9); link_append(&lnk, 11); link_begin(&lnk); while(1) { if(!link_next(&lnk, &num)) { //如果沒拿到數字 break; } printf("%d ", num); } printf("\n"); link_rbegin(&lnk); while(1) { if(!link_prev(&lnk, &num)) { break; } printf("%d ", num); } printf("\n"); link_deinit(&lnk); return 0; }
【樹】
如果單向線性鏈式物理結構中每個節點可以向後找到多個其他節點,就成為了一棵"樹"
樹裡的節點可以分成幾層,"不同層之間符合線性規律"(任何兩層之間都有前後順序)
樹的最上面一層只有一個節點,這個節點叫做"樹的根節點"
可以"用根節點代表整棵樹"
如果樹中兩個節點之間有直接的聯絡,就說明他們之間存在"父子關係"
靠近根節點的叫"父節點",遠離根節點的叫"子節點"
任何節點"最多隻能有一個父節點",但是"可以有多個子節點"
【二叉樹】
如果每個節點"最多隻有兩個"子節點,就把這種樹叫做"二叉樹"
二叉樹是最簡單的樹(所有的樹都可以轉化為二叉樹)
二叉樹裡"用左右區分"一個節點的兩個不同子節點
二叉樹裡任何一個節點都可以看作是一個二叉樹的根節點
如果二叉樹中一個節點叫 A ,它的左子節點叫 B 右子節點叫 C,則B所代表的二叉樹叫做A的"左子樹",C所代表的二叉樹叫做A的"右子樹"
樹的遍歷指的是:"依次處理樹裡的每一個節點"
樹的大多數操作都是通過遍歷實現的
通常採用"遞迴方式解決樹的遍歷"問題
在對樹進行遍歷的時候,一定"先遍歷左"子樹,然"後遍歷右"子樹
如果最先處理根節點叫做"前序遍歷"
如果中間處理根節點叫做"中序遍歷"
如果最後處理根節點叫做"後序遍歷"
【練習】
編寫函式計算一棵樹的高度
/*程式碼*/ 01tree.h 標頭檔案 #ifndef __01TREE_H__ #define __01TREE_H__ struct node; typedef struct { struct node *p_node; } tree; typedef struct node { int num; tree left; tree right; } node; //樹的初始化函式 void tree_init(tree *); //樹的清理函式 void tree_deinit(tree *); //按順序在樹裡插入新數字 void tree_insert_in_order(tree *, int ); //中序遍歷函式 void miter(tree *, void (*)(int)); //前序遍歷函式 void fiter(tree *, void (*)(int)); //後序遍歷函式 void liter(tree *, void (*)(int)); //刪除節點的函式 void tree_remove(tree *, int ); //計算樹高度的函式 int tree_height(const tree *); #endif //__01TREE_H__ /*程式碼*/ 01tree.c 函式原始檔 #include <stdlib.h> #include "01tree.h" //樹的初始化函式 void tree_init(tree *p_tree) { p_tree->p_node = NULL; } //樹的清理函式 void tree_deinit(tree *p_tree) { if (!p_tree->p_node) { return ; } tree_deinit(&(p_tree->p_node->left)); tree_deinit(&(p_tree->p_node->right)); free(p_tree->p_node); p_tree->p_node = NULL; } //在有序二叉樹裡查詢某個數字所在的位置 const tree *tree_search_in_order(const tree *p_tree, int num) { if (!p_tree->p_node) { return p_tree; } if (p_tree->p_node->num == num) { return p_tree; } else if (p_tree->p_node->num > num) { return tree_search_in_order(&(p_tree->p_node->left), num); } else { return tree_search_in_order(&(p_tree->p_node->right), num); } } //按順序向有序二叉樹中插入新數字 void tree_insert_in_order(tree *p_tree, int num) { tree *p_tmp = (tree *)tree_search_in_order(p_tree, num); if (!p_tmp->p_node) { node *p_node = (node *)malloc(sizeof(node)); if (p_node) { p_node->num = num; p_node->left.p_node = NULL; p_node->right.p_node = NULL; p_tmp->p_node = p_node; } } } //按順序把有序二叉樹裡所有數字顯示 //在螢幕上 /*void tree_print(tree *p_tree) { if (!p_tree->p_node) { return ; } tree_print(&(p_tree->p_node->left)); printf("%d ", p_tree->p_node->num); tree_print(&(p_tree->p_node->right)); }*/ //中序遍歷函式 void miter(tree *p_tree, void (*p_func)(int)) { if (!p_tree->p_node) { return ; } miter(&(p_tree->p_node->left), p_func); p_func(p_tree->p_node->num); miter(&(p_tree->p_node->right), p_func); } //前序遍歷函式 void fiter(tree *p_tree, void (*p_func)(int)) { if (!p_tree->p_node) { return ; } p_func(p_tree->p_node->num); miter(&(p_tree->p_node->left), p_func); miter(&(p_tree->p_node->right), p_func); } //後序遍歷函式 void liter(tree *p_tree, void (*p_func)(int)) { if (!p_tree->p_node) { return ; } miter(&(p_tree->p_node->left), p_func); miter(&(p_tree->p_node->right), p_func); p_func(p_tree->p_node->num); } //刪除節點的函式 void tree_remove(tree *p_tree, int num) { node *p_node = NULL; tree *p_tmp = (tree *)tree_search_in_order(p_tree, num); if (!p_tmp->p_node) { return ; } p_node = p_tmp->p_node; if (!p_tmp->p_node->left.p_node) { p_tmp->p_node = p_tmp->p_node->right.p_node; } else if (!p_tmp->p_node->right.p_node) { p_tmp->p_node = p_tmp->p_node->left.p_node; } else { tree *p_tmp1 = (tree *)tree_search_in_order(&(p_tmp->p_node->left), p_tmp->p_node->right.p_node->num); p_tmp1->p_node = p_tmp->p_node->right.p_node; p_tmp->p_node = p_tmp->p_node->left.p_node; } free(p_node); p_node = NULL; } //計算樹高度的函式 int tree_height(const tree *p_tree) { int ret = 0, left_height = 0, right_height = 0; if (!p_tree->p_node) { return 0; } left_height = tree_height(&(p_tree->p_node->left)); right_height = tree_height(&(p_tree->p_node->right)); ret = left_height > right_height ? left_height : right_height; return ret + 1; } /*程式碼*/ 01main.c 樹的主函式 #include <stdio.h> #include "01tree.h" void print(int num) { printf("%d ", num); } int main() { tree tr = {0}; tree_init(&tr); tree_insert_in_order(&tr, 50); tree_insert_in_order(&tr, 30); tree_insert_in_order(&tr, 70); tree_insert_in_order(&tr, 15); tree_insert_in_order(&tr, 40); tree_insert_in_order(&tr, 60); tree_insert_in_order(&tr, 80); tree_insert_in_order(&tr, 7); tree_insert_in_order(&tr, 21); tree_insert_in_order(&tr, 100); printf("樹的高度是%d\n", tree_height(&tr));//樹的高度是4 miter(&tr, print); //7 15 21 30 40 50 60 70 80 100 printf("\n"); tree_remove(&tr, 30); miter(&tr, print); //7 15 21 40 50 60 70 80 100 printf("\n"); tree_deinit(&tr); return 0; }