学习日记二——单链表的创建、插入、删除
头插法最先开辟的结点在尾端(逻辑上的),访问时最后访问,所以输出是逆序的
尾插法最先开辟的结点访问时最先访问,所以输出是顺序的
可以看完文章再回头看一遍
1. 结点结构体的创建
typedef struct Listnode{
int data; //数据域
struct Listnode* next; //指针域
}NODE; //方便后续操作
2. 操作函数声明
head表示头指针
NODE* createlist1(); //头插法创建单链表
NODE* createlist2(); //尾插法创建单链表
NODE* Choice1(); //选择头插法还是尾插法
int Choice2(NODE* head); //是否选择增加数据或删除数据
int Insert(NODE* head,int i,int x); //在头指针为head的单链表中插入数据x到位置i上
int Delete(NODE* head,int i); //删除数据
3. 函数体
(1)、头插法创建单链表
返回的头指针是最后开辟的结点的地址
NODE* createlist1(){ //头插法创建单链表
NODE *head,*p; //头指针和工作指针
int i; //存储变量
head=NULL;
printf("请输入数据,退出请输入非数字即可\n");
while(scanf("%d",&i)){ //如果输入正确scanf函数返回输入值,输入错误返回EOF结束输入,这样可以简化输入
p=(NODE*)malloc(sizeof(NODE)); //为新结点开辟空间
p->data=i; //将变量存储到新结点的数据域中
p->next=head;
head=p; //这两步是头插法的关键,起到连接链表的作用
}
return head; //返回头指针
}
(2)、尾插法创建单链表
返回的头指针是最先开辟的结点的地址
NODE* createlist2(){ //尾插法创建单链表
NODE *head=NULL,*p,*temp; //尾插法和头插法的最大区别,如果没有head保存头指针最后返回的指针是指向最后创建的结点
//所以尾插法输出的数据是顺序的,头插法输出的数据是逆序的
int i;
printf("请输入数据,退出请输入非数字即可\n");
while(scanf("%d",&i)){
p=(NODE*)malloc(sizeof(NODE));
p->data=i;
if(head==NULL){
head=p; //这一步的目的是保存最先创建的结点的地址这样输出时就是顺序的了
}else{
temp->next=p;
}
temp=p;
}
if(p!=NULL) p->next=NULL;
return head;
}
(3)、插入结点的函数
插入成功返回1,失败返回0
int Insert(NODE* head,int i,int x){
NODE *p,*r; //p用来接收head的地址进行操作,r用来开辟新结点
int j=1; //用来控制循环
p=head; //将head的地址赋给p
while(p!=NULL&&j<i-1){
p=p->next;
j++;
} //遍历使得p为第i-1个结点的地址
if(j!=i-1){
printf("插入的位置不合理\n");
return 0;
}
if((r=(NODE*)malloc(sizeof(NODE)))==NULL){
printf("开辟新结点失败\n");
return 0;
}
r->data=x; //把要插入的值赋给新开辟的结点
r->next=p->next; //使新开辟的结点指向原来的第i个结点
p->next=r; //使原来的第i-1个结点指向新开辟的结点
return 1;
}
(4)、删除结点的函数
删除成功返回1,失败返回0
int Delete(NODE* head,int i){
NODE *r=head,*temp;
int j=1;
while(r!=NULL&&j<i-1){
r=r->next;
j++;
}
if(j!=i-1){
printf("你要删除的结点位置不合适\n");
return 0;
}
temp=r->next;
r->next=r->next->next;
free(temp);
return 1;
}
(5)、选择头插法还是尾插法的函数
NODE* Choice1(){
NODE*p;
int choice;
printf("选择头插法创建链表请输入1,尾插法创建链表请输入2\n");
scanf("%d",&choice);
if(choice==1){
p=createlist1();
}else if(choice==2){
p=createlist2();
}else{
printf("你输入了错误的选项,请重新开始\n");
exit(-1);
}
return p;
}
(6)、选择是否要进行插入或删除操作的函数
int Choice2(NODE* head){
int i,j,x;
printf("如果要插入数据选择1,删除数据选择2,都不需要输入其他数字即可\n");
getchar(); //吸收换行符,没有会出现错误
scanf("%d",&j);
if(j==1){
printf("请输入你要插入的位置\n");
scanf("%d",&i);
printf("请输入你要插入的数据(整数)\n");
scanf("%d",&x);
Insert(head,i,x);
}else if(j==2){
printf("请输入你要删除的位置\n");
scanf("%d",&i);
Delete(head,i);
}else return 0; //错我输入返回0
return 1;
}
(7)、完整代码
#include<stdio.h>
#include<stdlib.h>
typedef struct Listnode{
int data; //数据域
struct Listnode* next; //指针域
}NODE;
NODE* createlist1(); //头插法创建单链表
NODE* createlist2(); //尾插法创建单链表
NODE* Choice1(); //选择头插法还是尾插法
int Choice2(NODE* head); //是否选择增加数据或删除数据
int Insert(NODE* head,int i,int x); //在头指针为head的单链表中插入数据x到位置i上
int Delete(NODE* head,int i); //删除数据
int main(void){
NODE *p,*r;
p=Choice1();
Choice2(p);
while(p){ //p不为空指针就循环
printf("%d\n",p->data);
r=p; //r保存p的地址,使用r主要是为了释放使用过的结点
p=p->next; //移动p,使p的地址为下一个结点的地址
free(r); //释放使用过的结点的空间
}
return 0;
}
NODE* createlist1(){ //头插法创建单链表
NODE *head,*p; //头指针和工作指针
int i; //存储变量
head=NULL;
printf("请输入数据,退出请输入非数字即可\n");
while(scanf("%d",&i)){ //如果输入正确scanf函数返回输入值,输入错误返回EOF结束输入,这样可以简化输入
p=(NODE*)malloc(sizeof(NODE)); //为新结点开辟空间
p->data=i; //将变量存储到新结点的数据域中
p->next=head;
head=p; //这两步是头插法的关键,起到连接链表的作用
}
return head;
}
NODE* createlist2(){ //尾插法创建单链表
NODE *head=NULL,*p,*temp; //尾插法和头插法的最大区别,如果没有head保存头指针最后返回的指针是指向指针尾部的
//所以尾插法输出的数据是顺序的,头插法输出的数据是逆序的
int i;
printf("请输入数据,退出请输入非数字即可\n");
while(scanf("%d",&i)){
p=(NODE*)malloc(sizeof(NODE));
p->data=i;
if(head==NULL){
head=p; //这一步的目的是保存最先创建的结点的地址这样输出时就是顺序的了
}else{
temp->next=p;
}
temp=p;
}
if(p!=NULL) p->next=NULL;
return head;
}
NODE* Choice1(){
NODE*p;
int choice;
printf("选择头插法创建链表请输入1,尾插法创建链表请输入2\n");
scanf("%d",&choice);
if(choice==1){
p=createlist1();
}else if(choice==2){
p=createlist2();
}else{
printf("你输入了错误的选项,请重新开始\n");
exit(-1);
}
return p;
}
int Insert(NODE* head,int i,int x){
NODE *p,*r; //p用来接收head的地址进行操作,r用来开辟新结点
int j=1; //用来控制循环
p=head; //将head的地址赋给p
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(j!=i-1){
printf("插入的位置不合理\n");
return 0;
}
if((r=(NODE*)malloc(sizeof(NODE)))==NULL){
printf("开辟新结点失败\n");
return 0;
}
r->data=x; //把要插入的值赋给新开辟的结点
r->next=p->next; //使新开辟的结点指向原来的第i个结点
p->next=r; //使原来的第i-1个结点指向新开辟的结点
return 1;
}
int Delete(NODE* head,int i){
NODE *r=head,*temp;
int j=1;
while(r!=NULL&&j<i-1){
r=r->next;
j++;
}
if(j!=i-1){
printf("你要删除的结点位置不合适\n");
return 0;
}
temp=r->next;
r->next=r->next->next;
free(temp);
return 1;
}
int Choice2(NODE* head){
int i,j,x;
printf("如果要插入数据选择1,删除数据选择2,都不需要输入其他数字即可\n");
getchar();
scanf("%d",&j);
if(j==1){
printf("请输入你要插入的位置\n");
scanf("%d",&i);
printf("请输入你要插入的数据(整数)\n");
scanf("%d",&x);
Insert(head,i,x);
}else if(j==2){
printf("请输入你要删除的位置\n");
scanf("%d",&i);
Delete(head,i);
}else return 0;
return 1;
}
4. 测试结果
(1)、头插法创建的单链表
选择头插法创建链表请输入1,尾插法创建链表请输入2
1
请输入数据,退出请输入非数字即可
123
23
43
65
4543
765
54
t
如果要插入数据选择1,删除数据选择2,都不需要输入其他数字即可
1
请输入你要插入的位置
3
请输入你要插入的数据(整数)
666
54
765
666
4543
65
43
23
123
(2)、尾插法创建的单链表
选择头插法创建链表请输入1,尾插法创建链表请输入2
2
请输入数据,退出请输入非数字即可
1
2
3
4
5
6
7
s
如果要插入数据选择1,删除数据选择2,都不需要输入其他数字即可
2
请输入你要删除的位置
3
1
2
4
5
6
7
5. 一些小体会
(1)、头插法创建的单链表逆序输出,尾插法顺序输出。
(2)、尾插法一定要有中间指针,因为要有一个指针保存起始结点地址。
(3)、创建结点时最好检查下开辟成功没,程序结束记得释放已经使用过的结点。
(4)、学习链表时最难的一点是在脑中形成链表的抽象结构,当你有对链表的抽象结构有深刻认识时,链表就基本学会了。