前言:
在dotcpp上碰到了一道题,链接放这了,这道题就是让你自己构建一遍链表的创建,插入节点,删除节点,获取节点,输出链表,题目给了几张代码图,不过不用管那些图,按自己的思路写一遍
也是这道题,让我第一次自己完整的去实现了一遍链表,通过题目来学,我一直都觉得效果是非常明显的
在学校老师教的数据结构,那本书是一本黄色外壳的书,不是说这书不好,书里代码采用的C和C++的混用,有的地方他用C实现,有地方他用C++实现,就感觉这对像我这种菜鸟很不友好,这让我自已写时,很容易出现错误,看半天看不出来
题目描述:
链表是数据结构中一种最基本的数据结构,它是用链式存储结构实现的线性表。它较顺序表而言在插入和删除时不必移动其后的元素。现在给你一些整数,然后会频繁地插入和删除其中的某些元素,会在其中某些时候让你查找某个元素或者输出当前链表中所有的元素。
下面给你基本的算法描述:
图1:链表类型的定义以及获得链表元素的算法描述
图2:链表的插入算法描述
图3:链表的删除算法描述
图4:链表的创建算法描述
输入格式:
输入数据只有一组,第一行有n+1个整数,第一个整数是这行余下的整数数目n,后面是n个整数。这一行整数是用来初始化列表的,并且输入的顺序与列表中的顺序相反,也就是说如果列表中是1、2、3那么输入的顺序是3、2、1。
第二行有一个整数m,代表下面还有m行。每行有一个字符串,字符串是“get”,“insert”,“delete”,“show”中的一种。如果是“get”或者“delete”,则其后跟着一个整数a,代表获得或者删除第a个元素;如果是“insert”,则其后跟着两个整数a和e,代表在第a个位置前面插入e;“show”之后没有整数。
输出格式
如果获取成功,则输出该元素;如果删除成功则输出“delete OK”;如果获取失败或者删除失败,则输出“get fail”以及“delete fail”。如果插入成功则输出“insert OK”,否则输出“insert fail”。如果是“show”则输出列表中的所有元素,如果列表是空的,则输出“Link list is empty”。注:所有的双引号均不输出。
样例输入:
3 3 2 1
21
show
delete 1
show
delete 2
show
delete 1
show
delete 2
insert 2 5
show
insert 1 5
show
insert 1 7
show
insert 2 5
show
insert 3 6
show
insert 1 8
show
get 2
样例输出:
1 2 3
delete OK
2 3
delete OK
2
delete OK
Link list is empty
delete fail
insert fail
Link list is empty
insert OK
5
insert OK
7 5
insert OK
7 5 5
insert OK
7 5 6 5
insert OK
8 7 5 6 5
7
解题思路:
不必去看那几张图,就依自己的思路写,链表就是结构体变量连起来,也就是结构体变量的成员多了一个指向下一个结构体变量的指针,
- 题目要求输入的整数,在存入链表时是倒着存入的,也就是要用头插法,头插法也就是一直在头节点h后插入新节点q,主要代码为
q->next=h->next; h->next=q;
,拓展下,正着存入也就是尾插法,需要增加一个尾指针p,使其始终指向当前链表的尾结点,每插入一个新节点后让p指向这个新节点,主要代码为p->next=q; p=q;
- 头节点只有指针域,没有数据域, 别看输入输出的规矩这么一大串,写4个if用strcmp函数比对字符串,看调用那个函数,
参考代码:
部分函数我用的是bool类型的,要加上#include<stdbool.h>
,也可以用int类型,只要返回值有正确和错误的性质就好
先拆分开解释:
- 链表的定义:
typedef struct LNode{
int data;//数据域
struct LNode *next;//指针域
}LinkList;//定义结构体变量的别称,加个解引符*,*LinkList就是定义结构体指针的别称
- 创建链表:
void createList(LinkList *L,int n)//依照题目使用的是头插法
{
LinkList *p,*q;
p=L;//使p指向头节点
//如果在main函数里已经L->next=NULL,就不用在这p->next=NULL,因为是头插法,所以赋空的操作应该在创建节点前,因为头插法没有指向尾节点的指针,尾插法的赋空操作在创建节点后
for(int i=0;i<n;i++)//创建n个节点
{
q=(LinkList*)malloc(sizeof(LinkList));
scanf("%d",&q->data);
q->next=p->next;//把p节点后续的节点接在q节点后
p->next=q;//再把q节点接在p节点后
}
}
- 在第a个位置前面插入e:
bool insertList(LinkList *L,int a,int e)
{
LinkList *p=L,*q;
q=(LinkList*)malloc(sizeof(LinkList));
q->data=e;//创建一节点存储e,该节点为要插入的节点
while(a>1)//a>1是保证p在循环结束后指向第a-1个位置的节点
{
p=p->next;
if(p==NULL)
return false;
a--;
}
q->next=p->next;//与头插法原理一样,把p节点后续的节点接在q节点后
p->next=q;//把q节点接在p节点后
return true;
}
- 获得第a个位置的元素,获取成功输出该位置上的元素
bool getList(LinkList *L,int a)
{
LinkList *p=L->next;//此时p指向第一个节点,也就是如果a=1的话,即输出p节点的数据域
while(a>1)//使p指向第a个位置的节点
{
p=p->next;
if(p==NULL)
{
printf("get fail\n");
return false;
}
a--;
}
printf("%d\n",p->data);
return true;
}
- 删除第a个元素
bool deleteList(LinkList *L, int a)
{
LinkList *p = L,*q=L->next;//p指向头节点,q指向第一个节点
if(q==NULL)//判断链表是否为空
return false;
while(a>1)//p指向被删除节点的前一个结点,q指向要被删除节点
{
p=p->next;
q=q->next;
if(q==NULL)
return false;
a--;
}
p->next=q->next;//使p节点跳过q节点指向q节点的下一个节点
free(q);//释放q节点占用的空间,也就是删除了
return true;
}
- 输出链表中所有元素
bool displayList(LinkList *L)
{
LinkList *p;
p=L->next;
if(p==NULL)//判断链表是否为空
{
printf("Link list is empty\n");
return false;
}
while(p)//输出链表所有元素
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return true;
}
以上就为题目用得上的功能函数了
整体代码:
为了main函数看得更美观点,做了些改动
#include<stdio.h>
#include<stdlib.h>//需要这个才能用malloc
#include<string.h>
#include<stdbool.h>//bool
typedef struct LNode{
int data;
struct LNode *next;
}LinkList;
void createList(LinkList *L,int n)
{
LinkList *p,*q;
p=L;
for(int i=0;i<n;i++)
{
q=(LinkList*)malloc(sizeof(LinkList));
scanf("%d",&q->data);
q->next=p->next;
p->next=q;
}
}
bool getList(LinkList *L,int a)
{
LinkList *p=L->next;
while(a>1)
{
p=p->next;
if(p==NULL)
return false;
a--;
}
printf("%d\n",p->data);
return true;
}
bool insertList(LinkList *L,int a,int e)
{
LinkList *p=L,*q;
q=(LinkList*)malloc(sizeof(LinkList));
q->data=e;
while(a>1)
{
p=p->next;
if(p==NULL)
return false;
a--;
}
q->next=p->next;
p->next=q;
return true;
}
bool deleteList(LinkList *L, int a)
{
LinkList *p = L,*q=L->next;
if(q==NULL)
return false;
while(a>1)
{
p=p->next;
q=q->next;
if(q==NULL)
return false;
a--;
}
p->next=q->next;
free(q);
return true;
}
bool displayList(LinkList *L)
{
LinkList *p;
p=L->next;
if(p==NULL)
return false;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return true;
}
int main()
{
int n;
scanf("%d",&n);
LinkList *L;
L=(LinkList*)malloc(sizeof(LinkList));
L->next=NULL;
createList(L,n);
int m,a,e;
char str[10];
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%s",str);
if(strcmp(str,"show")==0)//将所有操作失败时会输出的信息,放在这些if分支里
{
if(!displayList(L))
printf("Link list is empty\n");
}
else if(strcmp(str,"delete")==0)
{
scanf("%d",&a);
if(deleteList(L,a))
printf("delete OK\n");
else
printf("delete fail\n");
}
else if(strcmp(str,"get")==0)
{
scanf("%d",&a);
if(!getList(L,a))
printf("get fail\n");
}
else if(strcmp(str,"insert")==0)
{
scanf("%d%d",&a,&e);
if(insertList(L,a,e))
printf("insert OK\n");
else
printf("insert fail\n");
}
}
return 0;
}
由于自己对于链表的学习也只是刚刚起步,有错的地方希望大家能够指正(。・ω・。)