数据结构——双向链表(C语言实现)

来源:互联网 发布:js语言精粹怎么样 编辑:程序博客网 时间:2024/04/26 08:25
/*************************************************************************> File Name: link_list.c> Author: Andy001847> Mail: yunzhonglai@hotmail.com> 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;}

运行结果演示:
这里写图片描述

0 0