一、来自“本次拒绝出场,被虐的体无完肤的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;
}