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

13 篇文章 0 订阅
5 篇文章 2 订阅
/*************************************************************************
> 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值