數據結構——雙向鏈表(C語言實現)

/*************************************************************************
> File Name: link_list.c
> Author: Andy001847
> Mail: [email protected]
> Created Time: 2014年10月25日 星期六 11時51分34秒
************************************************************************/


//雙向鏈表的實現


#include <stdio.h>
#include <stdlib.h>


//定義雙向鏈表中的節點
typedef struct node{
    int data; //節點的數據域
    struct node *p_next;//下一個節點的位置
    struct node *p_pre;//前一個結點的位置
}Node;


static Node head, tail; //虛構頭尾節點


//初始化鏈表
void init(){
    head.p_next = &tail;//將尾節點“掛”在頭節點後面
    tail.p_pre = &head;//將頭節點“掛”在尾節點前面
}


//清空鏈表
void deinit(){
    while (head.p_next != &tail){
        Node *p_node = head.p_next;//將節點“掛”在頭節點後面,形成第一個有效數據元素
        head.p_next = head.p_next->p_next;//刪除第一個有效節點
        p_node->p_next->p_pre = &head;//將刪除後的第一個有效節點和頭節點相連
        free(p_node); //釋放已經刪除的節點空間
        p_node = NULL;//防止釋放後的野指針
    }
}


//從鏈表頭部插入節點
void insert_head(int num){
    Node *p_node = (Node *)malloc(sizeof(Node));//動態分配內存一個新節點
    if (!p_node){ //處理動態內存分配失敗的情況
        perror("malloc1");
        return;
    }
    p_node->data = num;//將值賦給新節點
    head.p_next->p_pre = p_node;//將新節點“掛”頭節點後面
    p_node->p_next = head.p_next;//是新節點成爲鏈表的一個節點
    p_node->p_pre = &head;//使頭節點和新節點項鍊
    head.p_next = p_node;
}


//從尾部插入新節點
void append(int num){
    Node *p_node = (Node *)malloc(sizeof(Node));//動態分配一個內存給新節點
    if (!p_node){
        perror("malloc2");
        return;
    }
    p_node->data = num;//將值賦給新節點;
    tail.p_pre->p_next = p_node;//將新節點“掛”在尾部的第一個有效節點上
    p_node->p_pre = tail.p_pre;//使新節點和尾節點相連
    p_node->p_next = &tail;//將新節點“掛”在尾部
    tail.p_pre = p_node;
}


//按順序插入新節點
void insert_order(int num){
    Node *n_node = (Node *)malloc(sizeof(Node));//爲新節點動態分配內存
    if (!n_node){ //處理動態內存分配失敗的情況
        perror("malloc3");
        return;
    }

    Node *p_node = NULL;
    for (p_node = &head; p_node != &tail; p_node = p_node->p_next){
        Node *p_tmp = p_node->p_next;
        if ((p_tmp->data) > num || p_tmp == &tail){//插入新節點的情況
            n_node->data = num;//將值賦給新節點
            n_node->p_pre = p_tmp->p_pre;//將新節點“掛”在指定位置中
            p_tmp->p_pre->p_next = n_node;
            n_node->p_next = p_tmp;
            p_tmp->p_pre = n_node;
            break;
        }
    }
}


//指定插入某節點後面(如果有多個值相同,插入第一個後面)
void insert(int base, int num){
    Node *n_node = (Node *)malloc(sizeof(Node));
    if (!n_node){ //處理動態內存分配失敗的情況
        perror("malloc4");
        return;
    }


    Node *p_node = NULL;
    for (p_node = &head; p_node != &tail; p_node = p_node->p_next){
        Node *p_tmp = p_node->p_next;
        if ((p_tmp->data) == base){//找到基準位置
            n_node->data = num;//將值賦給新節點
            n_node->p_next = p_tmp->p_next;//將新節點“掛”在基準點後面
            p_tmp->p_next->p_pre = n_node;//使新節點和基準點相連接
            n_node->p_pre = p_tmp;
            p_tmp->p_next = n_node;
            return;
        }
    }
    //處理基準點不存在的情況
    printf("值爲%d的節點不存在!無法將值爲值%d的節點插入其後。\n", base, num);
    free(n_node);
    n_node = NULL;
}


//刪除第一個有效節點
void delete_head(){
    if (head.p_next != &tail){
        Node *p_node = head.p_next;
        head.p_next = head.p_next->p_next;//將第一個有效節點刪除
        p_node->p_next->p_pre = &head;//使刪除後的節點相連
        free(p_node);
        p_node = NULL;
    }
}


//刪除最後一個有效節點
void delete_tail(){
    if (tail.p_pre != &head){
        Node *p_node = tail.p_pre;
        tail.p_pre = tail.p_pre->p_pre;//將最後一個有效節點刪除
        p_node->p_pre->p_next = &tail;//使刪除後的節點相連
        free(p_node); //釋放刪除後的節點空間
        p_node = NULL;
    }
}


//刪除指定節點
void delete(int num){
    Node *p_node = NULL;
    for (p_node = &head; p_node != &tail; p_node = p_node->p_next){
        Node *p_tmp = p_node->p_next;
        if (p_tmp != &tail && (p_tmp->data) == num){//找到要刪除的節點位置
            p_node->p_next = p_tmp->p_next;//刪除指定的節點
            p_tmp->p_next->p_pre = p_node;//使刪除後的節點相連
            free(p_tmp); //刪除釋放後的節點空間
            p_tmp = NULL;
            break;
        }
    }
}


//獲取鏈表節點個數
int size(){
    int cnt = 0;
    Node *p_node = NULL;
    //方法一

    for (p_node = &head; p_node != &tail; p_node = p_node->p_next){
        Node *p_tmp = p_node->p_next;
        if (p_tmp != &tail){//判斷有效節點的最終位置
            cnt++;
        }
    }
    return cnt;

    //方法二
    /*
    for(p_node = &tail; p_node != &head; p_node = p_node -> p_pre){
    Node *p_tmp = p_node -> p_pre;
    if(p_tmp != &head){
    cnt++;
    }
    }
    return cnt;
    */
}


//獲取頭部節點數據
int first(){
    return head.p_next == &tail ? 0 : head.p_next->data;
}


//獲取尾部節點數據
int last(){
    return tail.p_pre == &head ? 0 : tail.p_pre->data;
}


//順序打印鏈表數據
void print_order(){
    Node *p_node = NULL;
    for (p_node = &head; p_node != &tail; p_node = p_node->p_next){//從頭部開始打印節點數據
        Node *p_tmp = p_node->p_next;
        if (p_tmp != &tail){
            printf("%d  ", p_tmp->data);
        }
    }
    printf("\n");
}


//逆序打印鏈表數據
void print_invert(){
    Node *p_node = NULL;
    for (p_node = &tail; p_node != &head; p_node = p_node->p_pre){//從尾部開始打印節點數據
        Node *p_tmp = p_node->p_pre;
        if (p_tmp != &head){
            printf("%d  ", p_tmp->data);
        }
    }
    printf("\n");
}


//讀取整數函數
int readInt(){
    int value = 0;


    while (!scanf("%d", &value)){
        scanf("%*[^\n]");//清除緩衝區中的非法換行符
        scanf("%*c"); //清除緩衝區中的非法字符
        printf("輸入有誤!請重新輸入:");//如果不合法輸入,給出提示
    }
    scanf("%*[^\n]");
    scanf("%*c");


    return value;
}


int main(void){
    init(); //初始化鏈表
    char choice = 'y';//該變量用於讓用戶選擇是否繼續操作鏈表
    int tmp = 0; //設置臨時變量,用於選擇對於鏈表的操作方式
    do{
        int num = 0;
        printf("請輸入一個整數:");//提示輸入
        num = readInt();

        printf("請選擇插入的方式,0從頭部插入,1從尾部插入,2指定數字後面插入:");//選擇操作鏈表方式
        tmp = readInt();//調用readInt函數,讀取臨時變量的值,從而選定鏈表操作方式


        int base = 0;
        switch (tmp){
        case 0:
            insert_head(num);//在頭部插入數據
            break;
        case 1:
            append(num); //在尾部插入數據
            break;
        case 2:
            printf("請輸入指定數字:");
            base = readInt();
            insert(base, num);//指定位置插入數據,其中base爲鏈表中的基準點,num是要插入在base後面的新節點
            break;
        default: //錯誤處理
            printf("無此種方式!\n");
            break;
        }


        printf("是否繼續?輸入y繼續,否則停止:");//提示是否繼續操作鏈表
        scanf("%c", &choice);
    } while (choice == 'y' || choice == 'Y');//循環條件


    printf("有效元素個數%d\n", size());//檢查輸入的元素個數
    printf("首元素:%d,尾元素:%d\n", first(), last());//查看頭尾節點


    tmp = 0;
    printf("請選擇輸出方式,0代表順序輸出鏈表數據,1代表逆序輸出鏈表數據:");//提示選擇雙向鏈表的打印方式
    tmp = readInt();//調用readInt函數,從而選定輸出方式

    switch (tmp){
    case 0:
        print_order();//順序輸出鏈表元素
        break;
    case 1:
        print_invert();//逆序輸出鏈表元素
        break;
    default: //錯誤處理
        printf("無此種方式!\n");
        break;
    }


    printf("是否要刪除節點?輸入y刪除,否則不刪除:");//選擇是否對鏈表進行刪除操作
    scanf("%c", &choice);//讀入用戶選擇
    if (choice == 'y' || choice == 'Y'){//如果選擇刪除,則進行下面操作
        char ch = 'y';
        do{
            printf("請選擇刪除方式,0代表從頭部刪除,1代表從尾部刪除,2代表指定數字刪除:");//選擇刪除方式
            tmp = readInt();//讀取用戶選擇


            int num = 0;
            switch (tmp){
            case 0:
                delete_head();//從頭部刪除
                break;
            case 1:
                delete_tail();//從尾部刪除
                break;
            case 2:
                printf("請輸入要刪除的數:");
                num = readInt();
                delete(num); //指定位置刪除
                break;
            default: //錯誤處理
                printf("無此種方式!\n");
                break;
            }


            printf("是否繼續刪除?輸入y繼續,否則停止:");//讓用戶選擇是否繼續操作鏈表
            scanf("%c", &ch);//讀取用戶選擇
        } while (ch == 'y' || ch == 'Y');
    }


    printf("請選擇輸出方式,0代表順序輸出鏈表數據,1代表逆序輸出鏈表數據:");//再次選擇輸出鏈表數據
    tmp = readInt();//讀取用戶選擇

    switch (tmp){
    case 0:
        print_order();//順序輸出鏈表數據
        break;
    case 1:
        print_invert();//逆序輸出鏈表數據
        break;
    default: //錯誤處理
        printf("無此種方式!\n");
        break;
    }


    deinit(); //清空鏈表


    return 0;
}

運行結果演示:
這裏寫圖片描述

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