基于双向链表的操作(重点是链表的逆序存放+两个非递减链表的合并)

一、来自“本次拒绝出场,被虐的体无完肤的Ivy”的温馨提示:

        如果,要用双向链表实现逆序存放,最好最好最好设置一个虚拟头节点和一个虚拟尾节点(这样就对称了),如果只设置一个虚拟头节点,单一个逆序存放操作还好实现,当跟链表的插入删除。。。等一系列操作放在一起的时候,强颜欢笑😬,反正Ivy小可爱,已经被特判烦得走火入魔了(其实没这么过分。。),所以这次只能咱们自己孤零零的玩儿了,QAQ

        其实非递减链表的合并挺有意思的,由于链表天然可递归,而可以递归的一般都可以迭代,所以至少有两种以上的做法,感兴趣的可以在力扣上找此类题型的题解欣赏一下捏~

二、实验目标:

通过该实验,深入理解链表的逻辑结构、物理结构等概念,掌握链表基本操作的编程实现,熟练掌握C语言中指针的操作。和顺女表对比,掌握线性结构两种不同存储方式的区别。

菜单页面如下:

 1 ---- 初始化双链表

2 ---- 销毁双链表

3 ---- 清空双链表

4 ---- 判断双链表是否为空

5 ---- 计算双链表的长度

6 ---- 获取特定位置的元素值

7 ---- 获取特定元素的下标

8 ---- 获取特定元素的前驱

9 ---- 获取特定元素的后继

10 --- 在特定位置插入新元素

11 --- 在特定位置删除元素

12 --- 显示双链表

13 --- 合并两个非递减的双链表
14 --- 反转双链表

 ⚠️输入负数退出!!

三、代码实现

        (来自Ivy小可爱的怒吼💢:亲们,该代码也可直接运行哦😯~)

//
//  main.c
//  数据结构---实验作业2
//
//  Created by *** on 10/09/2023.
//
//双向线性表:无虚拟尾节点
//布尔类型的转义符号
//free()和malloc() : void free(str* )
//
//这是一份需要重构的代码,,,

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

typedef int ElemType;

typedef struct ListNode{
    ElemType data;
    struct ListNode *next;
    struct ListNode *prev;
}MyLinkedList;

// 为什么这两个压根儿没有必要的全局变量还在这儿?QAQ,自己往下看吧,作者大大想记录一下自己的误判
struct ListNode *SpecificNext;
struct ListNode *SpecificPrev;

//1.创建双向链表
MyLinkedList* MyLinkedListCreat(void){
    MyLinkedList *head;
    head = (MyLinkedList*)malloc(sizeof(struct ListNode));
    head->next = NULL;
    head->data = -1;
    head->prev = NULL;

    return head;
}

//1.1初始化线性表,为什么有个1.1(强颜欢笑,闲的(bushi
bool MyLinkedListInit(MyLinkedList *obj, ElemType val){
    if(val < 0)
        return false;
    struct ListNode *q;
    struct ListNode *p;
    p = obj;
    q = (struct ListNode*)malloc(sizeof(struct ListNode));
    while(p&&p->next){
        if(p->data == val){
            printf("The number is duplicative. Please enter another positive number again.\n");
            continue;
        }
        p = p->next;
    }
    p->next = q;
    q->next = NULL;
    q->prev = p;
    q->data = val;
    
    return obj;
}

//15.线性表快速排序,QAQ,还没学暂时未解锁,也许能作者大大再强点儿就可以解锁了

//4.判断链表是否为空
bool MyLinkedListIsEmpty(MyLinkedList *obj){
    return ( (obj == NULL || obj->next == NULL)?true:false );
}

//5.求线性表的长度
int MyLinkedListLength(MyLinkedList *obj){
    int length = 0;
    struct ListNode *p;
    if( MyLinkedListIsEmpty(obj) )
        return 0;
    p = obj->next;
    while(p){
        length++;
        p = p->next;
    }
    
    return length;
}

//6.获取线性表中特定元素
ElemType MyLinkedListGet(MyLinkedList *obj, int index){
    int size = MyLinkedListLength(obj);
    if(index < 0 || index > size)
        return -1;
    
    struct ListNode *p;
    p = obj;
    int i = 0;
    while(i<index){
        p = p->next;
        i++;
    }
    
    return p->data;
}

//7.获取线性表中元素位置
int StructListNodeWhere(MyLinkedList *obj, ElemType val){
    if(MyLinkedListIsEmpty(obj))
        return -1;
    struct ListNode *p;
    p = obj;
    int index = 0;
    while(p){
        if(p->data == val){
            //SpecificNext = p->next;  // 本来想着在这里记录前驱和后继之后就不用再求一遍了,但是无法保证用户在进行8、9操作前一定会进行7操作,所以就,QAQ了
            //SpecificPrev = p->prev;
            return index;
        }
        p = p->next;
        index++;
    }
    
    return -1;
    
}

//8.求元素前驱
ElemType StructListNodePrev(MyLinkedList *obj, ElemType val){
    StructListNodeWhere(obj, val);
    if(SpecificPrev == NULL)  // 特判,排除首元素无前驱
        return -1;
    
    return SpecificPrev->data;
}

//9.求元素后继
ElemType StructListNodeNext(MyLinkedList *obj, ElemType val){
    StructListNodeWhere(obj, val);
    if(SpecificNext == NULL) // 特判,排除末尾元素无后继
        return -1;
    
    return SpecificNext->data;
}

//10. 在index前插入元素,index要合法 // 没有特判元素不能重复
bool MyLinkedListInsert(MyLinkedList *obj, int index, ElemType val){
    int i = 0;
    struct ListNode *p;
    if(obj == NULL)
        return false;
    //关于index的合法判断,放在主函数里了
    p = obj;
    
    while(p&&i<index){
        p = p->next;
        i++;
        }
    
    struct ListNode *q;
    q = (struct ListNode*)malloc(sizeof(struct ListNode));
    
    q->data = val;
    q->prev = p;
    q->next = p->next;
    if(p->next)
        p->next->prev = q;
    p->next = q;
    
    return true;
}

//11.删除线性表指定位置的元素
bool MyLinkedListDelete(MyLinkedList *obj, int index){
    int i = 0;
    struct ListNode *p, *q;
    if(obj == NULL)
        return false;
    p = obj;
    while(p&&i<index){
        p = p->next;
        i++;
        if(p == NULL)
            return false;
        }
    q = p->next;
    if(q == NULL)
        return false;
    p->next = q->next;
    q->next->prev = p;
    free(q);
    
    return true;
}

//12.显示线性表
bool MyLinkedListShow(MyLinkedList *obj){
    if(MyLinkedListIsEmpty(obj))
        return false;
    struct ListNode *p;
    p = obj;
    while(p){  // 此处需要加上p->data > 0是因为没有虚拟尾节点,所以链表反转之后,必须从obj开始打印,所以为了不打印出obj的元素加上一个特判
        if(p->data > 0)
            printf("%d ", p->data);
        p = p->next;
    }
    
    return true;
}

//3.清空线性表
MyLinkedList* MyLinkedListFree(MyLinkedList *L){
    if(L == NULL || L->next == NULL)
        return NULL;
    struct ListNode *p = L->next;
    struct ListNode *q;
    while(p && p != L->prev){  // 不可删除虚拟尾节点,为什么??
        q = p->next;
        free(p);
        p = q;
    }
    L->next = NULL;  // 双链表的删除,注意分析步骤
    
    //L->prev->prev = L;
    return L;
}

//2.销毁线性表
bool MyLinkedListDestroy(MyLinkedList *L){
    MyLinkedListFree(L);
    free(L);
    return true;
}

//13.合并两个非递减有序线性表:递归或迭代均可
MyLinkedList* MergeTwoLists(MyLinkedList *A, MyLinkedList *B){
    MyLinkedList *C;
    C = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    struct ListNode *a, *b, *c;
    a = A;
    b = B;
    c = C;
    
    if(a == NULL && b == NULL)
        return NULL;
    if(a == NULL)
        return b;
    if(b == NULL)
        return a;
    if(a->data == b->data){
        struct ListNode *c1;
        c1 = (struct ListNode*)malloc(sizeof(struct ListNode));
        c1->next = a->next;
        c1->prev = a->prev;
        c1->data = a->data;
        c->next = c1;
        c1->prev = c;
        c = c->next;
        c->next = MergeTwoLists(a->next, b->next);
        return c;  // 注意,每一步递归都要有返回值
    }
    if(a->data < b->data){
        struct ListNode *c1;
        c1 = (struct ListNode*)malloc(sizeof(struct ListNode));
        c1->next = a->next;
        c1->prev = a->prev;
        c1->data = a->data;
        c->next = c1;
        c1->prev = c;
        c1->next = MergeTwoLists(a->next, b);
        return c1;  // 注意,每一步递归都要有返回值
    }
    if(a->data > b->data){
        struct ListNode *c1;
        c1 = (struct ListNode*)malloc(sizeof(struct ListNode));
        c1->next = a->next;
        c1->prev = a->prev;
        c1->data = a->data;
        c->next = c1;
        c1->prev = c;
        c1->next = MergeTwoLists(a, b->next);
        return c1;  // 注意,每一步递归都要有返回值
    }
    
    return C;
}

//14.实现线性表的逆序存放(逆序两次,即可复原)
MyLinkedList* MyLinkedListReverse(MyLinkedList *obj){
    struct ListNode *p = obj, *temp, *q;
    if(obj == NULL)
        return obj;
    p = obj;
    //q = p->next;
    while(p){
        q = p->next;
        temp = p->prev;
        p->prev = p->next;
        p->next = temp;
        if(q == NULL)
            obj = p;
        //p = p->next;
        p = q;
        //q = p->next;
    }
        
    return obj;
}

//主函数
int main(int argc, const char * argv[]) {
    
    //菜单
    printf("✨ Welcome to the simple set calculator ✨(Enter anything to continue)");
    getchar();
    printf(" My name is Ivy. It's so great to meet you.");
    getchar();
    printf(" ------------------------------------------");
    getchar();
    printf(" Please enter the corresponding number to order me to do something");
    getchar();
    printf(" 1 ---- Initialisiza a double-linked list\n"
           " 2 ---- Destroy the double-linked list\n"
           " 3 ---- Free the doubl-linked list"
           " 4 ---- Judge if the double-linked list is empty or not\n"
           " 5 ---- Calculate the length of the doubl-linked list\n"
           " 6 ---- Get the element at the specific location\n"
           " 7 ---- Get the location of the specific element\n"
           " 8 ---- Get the precursor of the specific element\n"
           " 9 ---- Get the following of the specific element\n"
           " 10 --- Insert the element at the specific element\n"
           " 11 --- Delete the element at the specific element\n"
           " 12 --- Display the double-linked list\n"
           " 13 --- Merge two no-declining-order double-linked list\n"
           " 14 --- Reverse a list\n"
           " Attention: Enter a negative number to quit");
    getchar();
    printf(" ------------------------------------------");
    getchar();
    printf(" Wish you enjoy playing with the software\n"
           " I will be glad if you could offer me the feedback\n"
           " Thank you so much ❤️ (Enter anything to continue)\n");
    printf(" ------------------------------------------");
    getchar();
    
    int order,  num, location, element, destroy_time_A = 0, destroy_time_B = 0;
    struct ListNode *p, *m;
    MyLinkedList *A, *B, *C, *M;
    char c;
    printf("Please give me an order: (Enter a negative number to quit)\n");
    A = NULL;
    B = NULL;
    
    //判断用户输入命令
    while(scanf("%d", &order)){
        if(order < 0){
            break;
        }
        switch(order){
            case 1:  // 初始化线性表, 没有提示reset的功能
                printf("Please choose which set you want to create.(A/B)\n");
                fflush(stdin);
                c = getchar();
                if(c != 'A' && c != 'B')
                    printf("Sorry we don't have this kind of set.\n");
                else{
                    if(c == 'A'){
                        A = MyLinkedListCreat();
                        C = A;
                    }
                    else{
                        B = MyLinkedListCreat();
                        C = B;
                    }
                    //C = MyLinkedListCreat();
                    fflush(stdin);
                    printf("Please enter set %c:(Enter a letter to finish)\n", c);
                    while( scanf("%d", &num) ){ // 希望有更好的解决方法
                        if(num < 0){
                            goto final;
                        }
                        MyLinkedListInit(C, num);
                    }
                    fflush(stdin);
                    printf("\nThe set you entered :\n"
                           "%c is ", c);
                    p = C->next;
                    while(p&&p->data > 0){
                        printf("%d ", p->data);
                        p = p->next;
                    }
                }
                printf("\n");
                printf(" ------------------------------------------\n");
                break;
            case 2:  // 销毁线性表
                printf("Please choose which set you want to destroy. (A/B)\n");
                fflush(stdin);
                c = getchar();
                //C = (c == 'A'?A:B);
                if(c == 'A' && destroy_time_A){
                    printf("Sorry, but set A has already been destroyed.\n");
                }
                else if(c == 'B' && destroy_time_B){
                    printf("Sorry, but set B has already been destroyed.\n");
                }
                else if(c == 'A'){
                    MyLinkedListDestroy(A);
                    printf("Your link list has been destroyed.\n");
                    destroy_time_A = 1;
                }
                else if(c == 'B'){
                    MyLinkedListDestroy(B);
                    printf("Your link list has been destroyed.\n");
                    destroy_time_B = 1;
                }
                printf(" ------------------------------------------\n");
                break;
            case 3:  // 清空线性表
                MyLinkedListFree(A);
                printf("Your link list has been freed.\n");
                printf(" ------------------------------------------\n");
                break;
            case 4:  // 判断线性表是否为空
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                if(MyLinkedListIsEmpty(C))
                    printf("Your link list is empty.\n");
                else
                    printf("Your link list is not empty. You idiot.\n");
                printf(" ------------------------------------------\n");
                break;
            case 5:  //求线性表的长度
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                if(C == NULL || C->next == C->prev)
                    printf("Please enter the set first.\n");
                else{
                    printf("The length of your link list is %d.\n", MyLinkedListLength(C));
                    printf(" ------------------------------------------\n");
                }
                break;
            case 6:  // 获取线性表中指定位置的元素
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                fflush(stdin);
                if(C == NULL || C->next == C->prev)
                    printf("Please enter the set first.\n");
                else{
                    printf("Please tell me the location of the element you want to view.\n");
                    scanf("%d", &location);
                    if(location >= MyLinkedListLength(C)-1 )
                        printf("The loction dosen't exit.\n");
                    else
                        printf("The location of your idiot element is %d.\n", MyLinkedListGet(A, location));
                }
                printf("\n");
                printf(" ------------------------------------------\n");
                break;
            case 7:  // 获取线性表元素的位置
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                fflush(stdin);
                if(C == NULL || C->next == C->prev)
                    printf("Please enter the set first.\n");
                else{
                    printf("Please tell me the element that you want to know where it is .\n");
                    scanf("%d", &element);
                    if(StructListNodeWhere(C, element) == -1)
                        printf("Sorry, but the element doesn't exit.\n");
                    else
                        printf("The element of you idiot location is %d\n", StructListNodeWhere(A, element));
                }
                printf("\n");
                printf(" ------------------------------------------\n");
                break;
            case 8:  // 求前驱
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                fflush(stdin);
                if(C == NULL || C->next == C->prev)
                    printf("Please enter the set first.\n");
                else{
                    printf("Please tell me the element that you want to know the precursor element.\n");
                    scanf("%d", &element);
                    if(StructListNodePrev(C, element) == -1)
                        printf("This is the first element.\n");
                    else
                        printf("The precursor element is: %d\n", StructListNodePrev(A, element));
                }
                break;
            case 9:  // 求后继
                printf("Please choose which set you want to check. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                fflush(stdin);
                if(C == NULL || C->next == C->prev)
                    printf("Please enter the set first.\n");
                else{
                    printf("Please tell me the element that you want to know the following element is.\n");
                    scanf("%d", &element);
                    if(StructListNodeNext(A, element) == -1)
                        printf("This is the last element");
                    else
                        printf("The following element is: %d\n", StructListNodeNext(A, element));
                }
                printf("\n");
                printf(" ------------------------------------------\n");
                break;
            case 10:  // 在线性表指定位置插入元素
                printf("Please choose which set you want to insert. (A/B)\n");
                fflush(stdin);
                c = getchar();
                C = (c == 'A'?A:B);
                fflush(stdin);
                if(C == NULL || C->next == NULL)
                    printf("Please enter the set first.\n");
                else{
                    printf("Please enter the loction you want to insert the element to:");
                    scanf("%d", &location);
                    if(location >= MyLinkedListLength(C) || location < 0)
                        printf("Sorry, but you can't insert the element here.\n");
                    else{
                        printf("Please enter the element you want to insert:");
                        scanf("%d", &element);
                        if(!MyLinkedListInsert(C, location, element))
                            printf("Please enter another positive number, the number has exited already.\n");
                        else{
                            MyLinkedListInsert(C, location, element);
                            printf("The element has been inserted successfully.\n");
                        }
                    }
                }
                    printf("\n");
                    printf(" ------------------------------------------\n");
                    break;
                case 11:  // 删除线性表指定位置的元素
                    printf("Please choose which set you want to delete. (A/B)\n");
                    fflush(stdin);
                    c = getchar();
                    C = (c == 'A'?A:B);
                    fflush(stdin);
                    if(C == NULL || C->next == NULL)
                        printf("Please enter the set first.\n");
                    else{
                        printf("Please enter the loction where you want to delete the element");
                        scanf("%d", &location);
                        if(location >= MyLinkedListLength(C)-1 )
                            printf("Sorry, but you can't insert the element here.\n");
                        else
                            MyLinkedListDelete(A, location);
                    }
                    printf("\n");
                    printf(" ------------------------------------------\n");
                    break;
                case 12:  // 显示线性表
                    printf("Please choose which set you want to check. (A/B)\n");
                    fflush(stdin);
                    c = getchar();
                    C = (c == 'A'?A:B);
                    fflush(stdin);
                    if(C->next == NULL)
                        printf("Please enter the set first.\n");
                    else if(C == NULL)
                        printf("The set has been destroyed.\n");
                    else{
                        printf("The link list you entered is: ");
                        MyLinkedListShow(A);
                    }
                    printf("\n");
                    printf(" ------------------------------------------\n");
                    break;
                case 13:  // 合并两个非递减有序的线性表
                    printf("Attention: \n due to the ability of the coder, this function will run successfully only when the two list are non-decreasing.\n")
                    if(A == NULL || A->next == NULL)
                        printf("Please enter set A first.\n");
                    else if(B == NULL || B->next == NULL)
                        printf("Please enter set B first.\n");
                    else{
                        M = MergeTwoLists(A, B);
                        printf("The merged list is :\n");
                        m = M->next;
                        //p = C->next;
                        while(m&&m->data > 0){
                            printf("%d ", m->data);
                            m = m->next;
                        }
                    }
                    printf("\n");
                    printf(" ------------------------------------------\n");
                    
                    break;
                case 14:
                    printf("Please enter the set you want to reverse.\n");
                    fflush(stdin);
                    c = getchar();
                    C = (c == 'A'?A:B);
                    fflush(stdin);
                    if(C == NULL || C->next == NULL)
                        printf("Please enter the set first.\n");
                    else{
                        if(c == 'A')
                            A = MyLinkedListReverse(A);
                        if(c == 'B')
                            B = MyLinkedListReverse(B);
                        printf("Your linked list has already been reversed.\n");
                    }
                    printf(" ------------------------------------------\n");
                    
                    break;
                default:
                    printf("Your does not meet the requirement, please print again.\n"
                           "Don't be so naughty, and try to be a good kid!");
                    
                    break;
                }
                printf("\nPlease enter another number to make another order\n(Enter a negetive number to quit):\n");
        }
        
    final:printf("✨Do you enjoy playing with me?\n"
                 "Please rate me for my performance from 5 to 0( Enter any other numbers to quit 😭)\n"
                 " ------------------------------------------\n");
        
        int rate;
        scanf("%d", &rate);
        
        switch(rate){
                
            case 5:
                printf("Thank you sooo much, sweet heart😄\n"
                       "It's so nice to enjoy the time with you\n"
                       "I'll try to work harder!!!\n"
                       "Have a good day and welcome to play with me again😍");
                break;
            case 4:
                printf("Thank you buddy, good luck.\n");
                break;
            case 3:
                printf("Decent rate, thanks.\n");
            case 2:
                printf("I'll try to improve myself day in, day out.\n"
                       "Thank you so much for your feedback.\n");
                break;
            case 1:
                printf("I'll try to improve myself day in, day out.\n"
                       "Thank you so much for your feedback.\n");
                break;
            case 0:
                printf("I'll try to improve myself day in, day out.\n"
                       "Thank you so much for your feedback.\n");
                break;
            default:
                printf("Thank you\n");
        }
        printf("Goodbye~\n");
        
        return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值