✨链式存储
🎆一定别忘记生成新结点
🍔存储结构
typedef struct LNode{ int data; //数据域 struct LNode *next; //指针域 }LNode,*LinkList;
LinkList为指向结构体LNode的指针类型
🚥🚥🚥🚥🚥🚥
⭐习惯上用LinkList定义单链表,强调的是某个单链表的头指针,用LNode*定义指向单链表中任意结点的指针变量
例如:
定义LinkList L,那么L为单链表的头指针
定义LNode *p,那么p为指向单链表某个结点的指针,用*p代表该节点
⭐注意区分指针变量和结点变量两个不同的概念
例如
若定义LinkList p或LNode *p
p表示指向某个结点的指针变量,表示该结点的地址
*p表示对应的结点变量,表示该结点的名称
这两种定义方式是等价的
🚥🚥🚥🚥🚥🚥
⭐易错:首元结点VS头节点VS头指针
头节点是首元结点之前附设的结点
头指针是指向链表第一个结点的指针
(如果有头节点,那么头指针指向的结点为头节点)
(如果没有头节点,那么头指针指向的结点为首元结点)
🚥🚥🚥🚥🚥🚥
⭐链表增加头结点的好处
🎈便于首元结点的处理
增加头结点后,首元结点的地址
🎈便于空表和非空表的统一处理
没有头结点,设L为单链表的头指针,L应该指向首元结点,那么当单链表为长度为0的空表时 ,L指针为空(L==NULL)
有头结点,无论链表是否为空,头指针都是指向头节点的非空指针,那么当头结点的指针域为空,(L->next==NULL)如下图所示
🚥🚥🚥🚥🚥🚥
在顺序表中,由于相邻的两个元素在物理位置上相邻,那么每个元素的存储位置都可以从线性表的起始位置计算得到
在单链表里面,各个元素的存储位置都是任意的,都是每个元素的存储位置都包含着其直接前驱结点,因为p->next=ai , p->next->next=ai+1 , 那么想要取得第i个数据元素必须从头指针出发顺链寻找
🚥🚥🚥🚥🚥🚥
🎈初始化
生成新结点作为头结点,用头指针 L 指向头结点
头结点的指针域置空
int InitList(LinkList &L) { L=new LNode; L->next=NULL; return 1; }
int InitList(LinkList *L) { *L=new LNode; (*L)->next=NULL;//必须加上括号 return 1; }
为什么必须加上():优先级:->大于*
🎈尾插法建立单链表
1.创建一个只有头结点的空链表
2.尾指针 r 初始化,指向头结点
3.根据创建链表包括的元素个数n,循环n次执行以下操作
~生成一个新结点*p
~输入元素并把值赋给新结点*p的数据域
~将新结点*p插入尾结点*r后面
~尾指针r指向新的尾结点*p
(在前面说过,*X表示结点的名称)
int Create_back(LinkList &L,int num) { L=new LNode; LNode *p,*r; //先建立一个带有头节点的空链表 L->next=NULL; //尾指针r指向头结点 r=L; for(int i=0;i<num;i++) { p=new LNode; //生成新结点 cin>>p->data; p->next=NULL; r->next=p; //新结点*p插入尾结点*r后(*X表示结点的名称) r=p; //r指向新的尾结点*p } return 1; }
代码里面的 LNode *p,*r;还可以写为LinkList p,r;具体原因在上面说过了(就是在上面的⭐注意区分指针变量和结点变量两个不同的概念)
int Create_back(LinkList *L,int num) { *L=new LNode; LNode *p,*r; //先建立一个带有头节点的空链表 (*L)->next=NULL; //尾指针r指向头结点 r=*L; for(int i=0;i<num;i++) { p=new LNode; //生成新结点 cin>>p->data; p->next=NULL; r->next=p; //新结点*p插入尾结点*r后(*X表示结点的名称) r=p; //r指向新的尾结点*p } return 1; }
🎈头插法建立单链表
1.创建一个只有头结点的空链表
2.根据创建链表包括的元素个数n,循环n次执行以下操作
~生成一个新结点*p
~输入元素并把值赋给新结点*p的数据域
~将新结点*p插入到头结点后面
int Create_front(LinkList &L,int num) { L=new LNode; LNode *p; L->next=NULL; for(int i=0;i<num;i++) { p=new LNode; //生成新结点*p cin>>p->data; p->next=L->next; L->next=p; //将新结点*p插入到头结点后面 } return OK; }
int Create_front(LinkList *L,int num) { *L=new LNode; LNode *p; (*L)->next=NULL; for(int i=0;i<num;i++) { p=new LNode; //生成新结点*p cin>>p->data; p->next=(*L)->next; (*L)->next=p; //将新结点*p插入到头结点后面 } return OK; }
🎈插入
注意:插入和前面的前插法尾插法不同,前插法尾插法是建立单链表,而插入是在已经建立好的单链表里面再加入额外的结点
具体步骤如下图所示
注意:插入操作必须要找到该位置的前驱结点
(这句话看上去理所应当,但是在某些时候是真的特别有用)
int ListInsert(LinkList &L,int num,int place) { LNode *p,*s; p=L; int j=0; while(p&&j<place-1) //找到插入位置 { p=p->next; j++; } if(!p||j>place-1) return ERROR; s=new LNode; //生成新结点 s->data=num; //数据域赋值 s->next=p->next; //结点*s的指针域指向a2 p->next=s; //结点*p的指针域指向*s return OK; }
int ListInsert(LinkList *L,int num,int place) { LNode *p,*s; p=*L; int j=0; while(p&&j<place-1) //找到插入位置 { p=p->next; j++; } if(!p||j>place-1) return ERROR; s=new LNode; //生成新结点 s->data=num; //数据域赋值 s->next=p->next; //结点*s的指针域指向a2 p->next=s; //结点*p的指针域指向*s return OK; }
🎈删除
1.先找到待删除位置a的前驱结点
2.创建临时结点q,保存待删除结点的地址
3.将结点*p的指针域指向a的直接后继结点
4.释放结点a的空间
int LinkDelete(LinkList &L,int place) { LNode *p,*q; p=L; int j=0; while((p->next)&&(j<place-1)) { p=p->next; ++j; } if(!(p->next)||(j>place-1) )return ERROR; q=p->next; //创建临时结点q p->next=q->next; //改变待删除结点的前驱结点的指针域 delete q; //释放被删除结点的空间 return OK; }
int LinkDelete(LinkList *L,int place) { LNode *p,*q; p=*L; int j=0; while((p->next)&&(j<place-1)) { p=p->next; ++j; } if(!(p->next)||(j>place-1) )return ERROR; q=p->next; //创建临时结点q p->next=q->next; //改变待删除结点的前驱结点的指针域 delete q; //释放被删除结点的空间 return OK; }
🎈取出元素
找到要取出的元素的位置,然后返回数据域即可
int ListPop(LinkList &L,int num) { LNode *p; p=L; int j=0; while(p&&j<num) { p=p->next; j++; } if(p) return p->data; else return -1; }
int ListPop(LinkList *L,int num) { LNode *p; p=*L; int j=0; while(p&&j<num) { p=p->next; j++; } if(p) return p->data; else return -1; }
🎈输出链表
void OutputList(LinkList &L) { LNode *p; p=L->next; while(p) { cout<<p->data<<' '; p=p->next; } cout<<endl; }
void OutputList(LinkList *L) { LNode *p; p=(*L)->next; while(p) { cout<<p->data<<' '; p=p->next; } cout<<endl; }
😎完整代码1
#include<iostream> using namespace std; #define OK 1 #define ERROR -1 typedef struct LNode { int data; struct LNode *next; }LNode,*LinkList; //1 int InitList(LinkList *L) { *L=new LNode; (*L)->next=NULL; return OK; } int Create_front(LinkList *L,int num) { *L=new LNode; LNode *p; (*L)->next=NULL; for(int i=0;i<num;i++) { p=new LNode; //生成新结点*p cin>>p->data; p->next=(*L)->next; (*L)->next=p; //将新结点*p插入到头结点后面 } return OK; } //3 int ListInsert(LinkList *L,int num,int place) { LNode *p,*s; p=*L; int j=0; while(p&&j<place-1) //找到插入位置 { p=p->next; j++; } if(!p||j>place-1) return ERROR; s=new LNode; //生成新结点 s->data=num; //数据域赋值 s->next=p->next; //结点*s的指针域指向a2 p->next=s; //结点*p的指针域指向*s return OK; } //4 int LinkDelete(LinkList *L,int place) { LNode *p,*q; p=*L; int j=0; while((p->next)&&(j<place-1)) { p=p->next; ++j; } if(!(p->next)||(j>place-1) )return ERROR; q=p->next; //创建临时结点q p->next=q->next; //改变待删除结点的前驱结点的指针域 delete q; //释放被删除结点的空间 return OK; } //5 int ListPop(LinkList *L,int num) { LNode *p; p=*L; int j=0; while(p&&j<num) { p=p->next; j++; } if(p) return p->data; else return -1; } void OutputList(LinkList *L) { LNode *p; p=(*L)->next; while(p) { cout<<p->data<<' '; p=p->next; } cout<<endl; } int main() { LinkList L; int n,e; cout<<"请输入你的操作"<<endl; printf("0:结束程序\n1:建立空表\n2:尾插法建立空表\n3:插入\n4:删除\n5:取出元素\n"); for(;;) { cin>>n; switch(n) { case 0: return 0; break; case 1: cout<<"建立空表"<<endl; if(InitList(&L)) { cout<<"建立成功"<<endl; } else { cout<<"建立失败"<<endl; } break; case 2: cout<<"输入21 18 30 75 42 56,使用尾插法建立链表"<<endl; cout<<"请输入要加入的数字的数量"<<endl; int num; cin>>num; if(Create_front(&L,num)!=0) { cout<<"请进行下一步操作"<<endl; OutputList(&L); } else cout<<"失败了"<<endl; break; case 3: cout<<"在第3个位置插入67,在第9个位置插入10"<<endl; cout<<"请输入需要插入的数字的个数"<<endl; int qqq; cin>>qqq; for(int i=0;i<qqq;i++) { cout<<"需要插入的数据和位置"<<endl; int nn,mm; cin>>nn>>mm; if(ListInsert(&L,nn,mm)) { cout<<"插入成功"<<endl; OutputList(&L); } else { cout<<"插入失败"<<endl; } } break; case 4: cout<<"删除第6个元素和第8个元素"<<endl; cout<<"输入需要删除的元素是哪一个"<<endl; int qq; cin>>qq; if(LinkDelete(&L,qq)) { cout<<"删除成功"<<endl; OutputList(&L); } else { cout<<"删除失败"<<endl; } break; case 5: cout<<"取出第5个元素和第7个元素"<<endl; cout<<"需要取出多少个数"<<endl; int cnt; cin>>cnt; for(int i=0;i<cnt;i++) { cout<<"需要取出哪个位置的元素"<<endl; int _; cin>>_; if(ListPop(&L,_)==-1) cout<<"取出元素失败"<<endl; else cout<<ListPop(&L,_)<<endl; } break; } } }
😎完整代码2
#include<iostream> using namespace std; #define OK 1 #define ERROR -1 typedef struct LNode { int data; struct LNode *next; }LNode,*LinkList; //1 int InitList(LinkList &L) { L=new LNode; L->next=NULL; return OK; } //2 int Create_back(LinkList &L,int num) { L=new LNode;//头节点 LNode *p,*r; L->next=NULL; r=L; for(int i=0;i<num;i++) { p=new LNode; cin>>p->data; p->next=NULL; r->next=p; r=p; } return OK; } //3 int ListInsert(LinkList &L,int num,int place) { LNode *p,*s; p=L; int j=0; while(p&&j<place-1) { p=p->next; j++; } if(!p||j>place-1) return ERROR; s=new LNode; s->data=num; s->next=p->next; p->next=s; return OK; } //4 int LinkDelete(LinkList &L,int place) { LNode *p,*q; p=L; int j=0; while((p->next)&&(j<place-1)) { p=p->next; ++j; } if(!(p->next)||(j>place-1) )return ERROR; q=p->next; p->next=q->next; //e=p->data; delete q; return OK; } //5 int ListPop(LinkList &L,int num) { LNode *p; p=L; int j=0; while(p&&j<num) { p=p->next; j++; } if(p) return p->data; else return -1; } void OutputList(LinkList &L) { LNode *p; p=L->next; while(p) { cout<<p->data<<' '; p=p->next; } cout<<endl; } int main() { LinkList L; int n,e; cout<<"请输入你的操作"<<endl; printf("0:结束程序\n1:建立空表\n2:尾插法建立空表\n3:插入\n4:删除\n5:取出元素\n"); for(;;) { cin>>n; switch(n) { case 0: return 0; break; case 1: cout<<"建立空表"<<endl; if(InitList(L)) { cout<<"建立成功"<<endl; } else { cout<<"建立失败"<<endl; } break; case 2: cout<<"输入21 18 30 75 42 56,使用尾插法建立链表"<<endl; cout<<"请输入要加入的数字的数量"<<endl; int num; cin>>num; if(Create_back(L,num)!=0) { cout<<"请进行下一步操作"<<endl; OutputList(L); } else cout<<"失败了"<<endl; break; case 3: cout<<"在第3个位置插入67,在第9个位置插入10"<<endl; cout<<"请输入需要插入的数字的个数"<<endl; int qqq; cin>>qqq; for(int i=0;i<qqq;i++) { cout<<"需要插入的数据和位置"<<endl; int nn,mm; cin>>nn>>mm; if(ListInsert(L,nn,mm)) { cout<<"插入成功"<<endl; OutputList(L); } else { cout<<"插入失败"<<endl; } } break; case 4: cout<<"删除第6个元素和第8个元素"<<endl; cout<<"输入需要删除的元素是哪一个"<<endl; int qq; cin>>qq; if(LinkDelete(L,qq)) { cout<<"删除成功"<<endl; OutputList(L); } else { cout<<"删除失败"<<endl; } break; case 5: cout<<"取出第5个元素和第7个元素"<<endl; cout<<"需要取出多少个数"<<endl; int cnt; cin>>cnt; for(int i=0;i<cnt;i++) { cout<<"需要取出哪个位置的元素"<<endl; int _; cin>>_; if(ListPop(L,_)==-1) cout<<"取出元素失败"<<endl; else cout<<ListPop(L,_)<<endl; } break; } } }
🥰如果大家有不明白的地方,或者文章有问题,欢迎大家在评论区讨论,指正🥰
Code over!