1. 程式人生 > >【資料結構】高效雙向連結串列list、樹tree(二叉樹)

【資料結構】高效雙向連結串列list、樹tree(二叉樹)

vi正常模式下:
"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;
}