对于很多人来讲链表的逻辑结构都能明白,可一到存储结构怎么代码实现就感到异常困难,打开IDE就懵,我自己也是从这个阶段心心念念跋山涉水终于搞懂,激动之下想写一帖给那些还在这个阶段徘徊的伙伴。
本人才疏学浅 如有错误欢迎同行指出共同交流 本人感激不尽
本博客将彻底讲解
- 链表的基础知识和隐藏细节
- 彻底区分弄懂带头结点和不带头结点的单链表
- 十五个常见链表功能考研题目的实现
- 全部讲解均包含相应代码
目录包括
- 基础讲解1
- 基础讲解2
- 手把手写代码
- 其他功能实现
一 基础知识讲解1(有基础可以跳过)
首先对于链表的不理解大多可能还是指针的基础不熟,再第一部分对一些指针的地方我们会重点解释。我们知道一个结点包含一个数据域和一个指针域,那么我们怎么用一个结构去包含这个结点的信息呢,这里就需要用到结构体了。
typedef int ElemType;
typedef struct Node
{
ElemType elem;
struct Node *next;
}LinkNode,*Linklist;
第一次读这段代码的时候有很多东西是不理解的,把这个结构体写的复杂一点,直观一点可以这样表示
typedef int ElemType;
struct Node
{
ElemType elem;
struct Node *next;
};
typedef struct Node LinkNode;
typedef struct Node* Linklist;
首先在这个结构体中有一个指向自身结构体的指针初次看时可能会感到纳闷。到底是结构体先被定义还是指针先被定义?这结构体都没定义完整怎么在里面定义指针?内存分配顺序到底是怎样的?我最开始就陷入了这种类似先有鸡还是先有蛋的循环。其实结构体分配内存和指针分配内存本身就是两个不同的地方,互不干扰。即使不把结构体写完整 struct Node,struct Node *next也是完全可以的。其次就说到用typedef取别名。前者你可以直接认为是我们嫌struct Node写的太麻烦重新定义一个。可为什么还要定义一个指向结构体的指针呢。这样想我们得到了很多的结点,可怎么样把这些结点都串起来形成一个链表呢。这时候就需要指针。只需要提供一个头指针就可以把说有的结表串起来,头指针指向一个结点,这个结点的指针域指向下一个,在一直指向下去到最后为空就形成了一个链表。所以取名大家用的是list而不再是Node。其实严格来说一个头指针就是一个链表,所以你可以看到很多习题的传参都是(Linklist &head)。
凡是涉及到指针全部都要给他初始化分配空间
所以一定记住以后一旦你定义了一个指针 Linklist newNode Linklist p全部要给newNode p分配空间用C语言的方式就是 p=(Linklist)malloc(LinkNode),C++为 p=new LinkNode;最后如果有需要回收(删除)结点 C free(p) C++ delete []p;
Q:怎么没见过程序中对结构体中的*next分配空间啊 A:结点的建立中就完成了指针的创建 p->next的赋值中对指向进行了交代
在链表中我们经常会构造一个辅助指针p=l->next 为什么要这样呢 ·简单来说一个结点就只有一个你对其的操作有时一下就改变了它接下来的指向等等 此时很多时候在想删除插入就很麻烦了因为next域改变后再想删除就不是同一个东西了 所以我们相当于需要一个备份,另外辅助指针也能方便我们的搜寻 遍历 和查找 下面这几段代码在链表中重复度比较高 希望不知道怎么写代码的同学先单独好好感受体会一下
//设置辅助指针p用来遍历
Linklist p=l->next;
int j=1;
while(p&&j<i)
{
p=p->next;
j++;
}
//用先保留p->next的备份 直接free(p->next)是不行的 大家可以自己体会下
Linklist q=p->next;
p->next=q->next;
*e=q->elem;
free(q);
//要对p结点进行操作同时还要将p往后移动需要设置一个备份q
while(p)
{
Linklist q=p->next;
p->next=l->next;
l->next=p;
p=q;
}
二 基础知识讲解2(强烈建议阅读)
在整个链表的实现过程中还有一个特别隐蔽的地方:二级指针的运用 可以说很多教材和代码对这个问题都讲的不太透彻,作为一个力求精简并深刻理解每行代码有点洁癖的我在这里想了好久好久。。。
程杰 大话数据结构单链表创建 void CreateListHead(Linklist *L,int n) { ....... }
严奶奶 数据结构(C语言版) void CreaList_L(Linklist &L,int n) {......}
指针和C++中的引用是一个性质这里不详谈 注意到Linklist 好像本身就是一个指针了啊 在来个*不就是指向指针的指针么 这么复杂的么?好像他们很多别的函数也用到了Linklist *L啊 完了这个函数又没用* Linklist L 就解决了 到底怎么回事啊??
首先更正一点严奶奶的教材中并没有说这个是否为引用 原书在P8对&进行了解释(说是引用参数 我们老师说这个符号只表示这个值发生了变化不代表引用 具体怎样大家见仁见智 我个人认为教材还是比较严谨的吧)
那么到底这个二级指针什么回事呢什么时候要用的 ?我查找了很多有的地方还是没有理解 觉得有时候用二级是多余的 最后 实践出真知 敲就完事了 下面这段代码是测验过得最简便的一种哪里该用二级指针都非常清楚
#include <stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct Node
{
ElemType date;
struct Node *next;
}*Linklist;
void Initiate(Linklist *l)
{
*l=(Linklist)malloc(sizeof(Node));
(*l)->next=NULL;
(*l)->date=NULL;
}
void Append(Linklist l,int n)
{
int i;
for(i=n;i>0;--i)
{
Linklist p=(Linklist)malloc(sizeof(Node));
scanf("%d",&p->date);
p->next=l->next;
l->next=p;
}
}
int Insert(Linklist l,int i,ElemType x)
{
Linklist p=l;
int j=0;
while(p&&j<i-1)
{
p=p->next;
j++;
}
if(!p||j>i-1) return -1;
Linklist pnew=(Linklist)malloc(sizeof(Node));
pnew->date=x;
pnew->next=p->next;
p->next=pnew;
return 1;
}
int Delete(Linklist l,int i,ElemType *e)
{
Linklist p=l;
int j=0;
while(p->next&&j<i-1)
{
p=p->next;
j++;
}
if(!(p->next)||j>i-1) return -1;
Linklist q=p->next;
p->next=q->next;
*e=q->date;
free(q);
}
void Print(Linklist l)
{
Linklist p=l->next;
if(p==NULL) printf("empty link\n");
else{
while(p)
{
printf("%d->",p->date);
p=p->next;
}
printf("\n");
}
}
void Destroy(Linklist *l)
{
Linklist p,q;
p=(*l)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
free(*l);
}
int main()
{
Linklist l;
Initiate(&l);
Append(l,3);
Print(l);
Insert(l,4,47);
Print(l);
int x=31;
Delete(l,1,&x);
printf("%d\n",x);
Print(l);
Destroy(&l);
return 0;
}
其实只有链表的初始化创建和销毁才需要用到二级指针具体讲解我自认为没下文讲的清楚和明白
http://www.cnblogs.com/tianzeng/p/9689516.html //一级指针与二级指针在动态链表中的应用 确保看完很重要
在我复习的过程中翻看了大一上C语言搭建链表中的例子也再次辅佐很印证了我对这方面的理解和思考 只能深深的感觉到教材真TM是个好东西 话不多说 直接上教材上的代码 教材名字:C语言程序设计教程(第二版) 清华大学出版社
#include<bits/stdc++.h>
typedef struct Node
{
int score;
struct Node *next;
}Node;
Node* Create()
{
Node *head ,*tail,*pnew;
int score;
head=(Node *)malloc(sizeof(Node));
if(head==NULL)
{
printf("no enough memory!\n");
return(NULL);
}
head->next=NULL;
tail=head;
printf("input the date of students:\n");
while(1)
{
scanf("%d",&score);
if(score<0)
break;
pnew=(Node *)malloc(sizeof(Node));
if(pnew==NULL)
{
printf("no enough memory!\n");
return(NULL);
}
pnew->score=score;
pnew->next=NULL;
tail->next=pnew;
tail=pnew;
}
return(head);
}
void Insert(Node* head,Node* pnew,int i)
{
Node *p;
int j;
p=head;
for(j=0;j<i&&p!=NULL;j++)
p=p->next;
if(p==NULL)
{
printf("not found\n");
return;
}
pnew->next=p->next;
p->next=pnew;
}
void Delete(Node *head,int i)
{
Node *p,*q;
int j;
if(i==0)
return;
p=head;
for(j=1;j<i&&p->next!=NULL;j++)
p=p->next;
if(p->next==NULL)
{
printf("not found\n");
return;
}
q=p->next;
p->next=q->next;
free(q);
}
void Print(Node *head)
{
Node *p;
for(p=head->next;p!=NULL;p=p->next)
printf("%d->",p->score);
printf("\n");
}
void Destroy(Node *head)
{
Node *p,*q;
p=head;
while(p->next!=NULL)
{
q=p->next;
p->next=q->next;
free(q);
}
free(head);
}
int main()
{
Node *head,*pnew;
head=Create();
Print(head);
pnew=(Node*)malloc(sizeof(Node));
if(pnew==NULL)
printf("no memory\n");
pnew->score=88;
Insert(head,pnew,0);
Print(head);
Delete(head,3);
Print(head);
Destroy(head);
return 0;
}
既然如此我们为什么很多地方还是用到了C++中的引用呢明明多打一个&啊? 一 简单,简便想那么多不累啊 二 引用传参比赋值传参拷贝一个备份的方法更加简洁可降低参数传递的时间和空间代价;可简化线性表操作的实现(取自殷人昆《数据结构(C语言描述)》P33)
以下所有功能实现均采用引用的写法 不在特意区分一级和二级指针
三 手把手实现函数 开干!(头结点与不带头结点)
从最简单的几个功能写起 创建链表 插入 删除 打印 清空 (代码在后面有贴出)(更多的功能实现在后文)
1 创建 void initList(Linklist &l)
我个人更喜欢管这个过程叫初始化 即创建一个空链表 在这其中不添加数据 添加数据用插入函数 我觉得这样感觉更好这样想来我们似乎只用传一个参数
对于带头结点的链表我们知道要首先建立一个头结点 这个头结点的头指针才是我们所谓的链表 即 l=new LinkNode l->next=null那么不带头结点的如何思考呢 诶?我们似乎已经有一个指针了诶 那就应该把之个指针看成头指针吧 所以 简简单单 l=null
2 插入 void Insert(Linklist &l,int i,ElemType e)
要想插入到指定位置我们必须从头通过指针域一个个搜寻 对于链表我们一般是不知道其长度的所以此时的搜寻一般选择while循环,循环到什么时候停止呢 要插到第 i 个要找到 i-1的指针域吧 所以要声明一个变量 k 通过循环使k变为i-1 (有能力的要设计如果插入位置无效的代码) 最后就是我们典型的插入语句改变指针域指向了
那没有头结点的呢?那是不是第一个插入的时候(i==1)代码不一样啊 要单独设计并且还要将头指针的位置改变啊 原来的第一就变成第二了
3 删除 void Delete(Linklist &l,int i,ElemType *e)
删除就删除感觉两个参数就够了吧?确实如此 大多数教材这么写可能是考虑到实际项目开发删除的数要存起来干别的把?为什么这个删除要用指针 插入不用指针啊? 如果你现在不能独立思考解决这个问题 建议详读基础2 并 百度交换函数与指针 同样我们还是要用到遍历 找到这个点(方法同上) 最后改变next指针
没有头结点的情况同样更加复杂 要考虑第一个 不过还好 只要让头指针往下next一个就好
4 打印 void Print(Linklist &l)
简单粗暴边遍历边打印就好
5 清空
清空的思想很简单 说到底还是遍历 遍历一个点 free释放掉一个点 再接着下一个就行
思考:清空和销毁的关系 A:销毁多一行代码free(l)就还 因为此时就只剩这一个结点了
代码实现 (多加了两个函数 求长度 和找第i个位置的元素)
//"LINK.h" 源文件
#ifndef LINK_H_INCLUDED
#define LINK_H_INCLUDED
typedef int ElemType;
typedef struct Node
{
ElemType elem;
struct Node *next;
}LinkNode,*Linklist;
void Initlist(Linklist &l);
void Clearlist(Linklist &l);
int GetLength(Linklist &l);
int GetElem(Linklist &l,int i,ElemType *e);
int Insert(Linklist &l,int i,ElemType e);
int Delete(Linklist &l,int i,ElemType *e);
void Print(Linklist &l);
#endif // LINK_H_INCLUDED
//"LINK.h"头文件
//分别实现了带头结点和不带头结点
#include"LINK.h"
#include<iostream>
#include<stdlib.h>
using namespace std;
//不带头结点链表基本功能实现
/*void Initlist(Linklist &l)
{
l=nullptr;
}
int Insert(Linklist &l,int i,ElemType e)
{
if(!l||i==1)
{
LinkNode *newNode=new LinkNode;
if(!newNode) {cerr<<"存储分配错误!\n"; exit(1);}
newNode->elem=e;
newNode->next=l;
l=newNode;
}
else
{
Linklist p=l;
int j=1;
while(p&&j<i-1)
{
p=p->next;
j++;
}
if(!p||j>i) { cerr<<"无效的插入位置!\n"; return 0;}
Linklist newNode=new LinkNode;
if(!newNode) {cerr<<"存储分配错误!\n";exit(1);}
newNode->elem=e;
newNode->next=p->next;
p->next=newNode;
return 1;
}
}
int Delete(Linklist &l,int i,ElemType *e)
{
Linklist q;
if(i==1)
{
q=l;
l=l->next;
}
else
{
Linklist p;
int j=1;
p=l;
while(p->next&&j<i-1)
{
p=p->next;
j++;
}
if(!(p->next)||j>i) {cerr<<"不存在该位置!\n"; return 0;}
q=p->next;
p->next=q->next;
}
*e=q->elem;
delete q;
return 1;
}
void Clearlist(Linklist &l)
{
Linklist p=l;
while(p)
{
Linklist q=p;
free(q);
p=p->next;
}
l=nullptr;
cout<<"链表已清空长度为:"<<GetLength(l)<<endl;
}
int GetLength(Linklist &l)
{
Linklist p=l;
int k=0;
while(p)
{
k++;
p=p->next;
}
return k;
}
int GetElem(Linklist &l,int i,ElemType *e)
{
Linklist p=l;
int j=1;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i) {cerr<<"不存在该位置!\n";return 0;}
*e=p->elem;
return 1;
}
void Print(Linklist &l)
{
Linklist p=l;
while(p)
{
cout<<p->elem<<" ";
p=p->next;
}
cout<<endl;
}
*/
//带头结点链表基本功能实现
void Initlist(Linklist &l)
{
l=new LinkNode;
if(!l) {cerr<<"存储分配失败!\n";exit(1);}
l->next=nullptr;
}
void Clearlist(Linklist &l)
{
Linklist p=l->next;
while(p)
{
Linklist q=p;
free(q);
p=p->next;
}
}
int GetLength(Linklist &l)
{
Linklist p=l->next;
int k=0;
while(p)
{
k++;
p=p->next;
}
return k;
}
int GetElem(Linklist &l,int i,ElemType *e)
{
Linklist p=l->next;
int j=1;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i) {cerr<<"不存在该位置!\n";return 0;}
*e=p->elem;
return 1;
}
int Insert(Linklist &l,int i,ElemType e)
{
Linklist p=l;
int j=1;
Linklist q=new LinkNode;
if(!q) {cerr<<"存储分配错误!\n"; exit(1);}
q->elem=e;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i) {cerr<<"不存在该位置!\n"; return 0;}
q->next=p->next;
p->next=q;
return 1;
}
int Delete(Linklist &l,int i,ElemType *e)
{
Linklist p=l;
int j=1;
while(p&&j<i)
{
j++;
p=p->next;
}
if(!(p->next)||j>i) {cerr<<"该位置不存在!\n"; return 0;}
Linklist q=p->next;
p->next=q->next;
*e=q->elem;
free(q);
return 1;
}
void Print(Linklist &l)
{
Linklist p=l->next;
while(p)
{
cout<<p->elem<<" ";
p=p->next;
}
cout<<endl;
}
// main 文件
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include"LINK.h"
void testdemo()
{
Linklist l;
Initlist(l);
Insert(l,1,10);
Insert(l,1,20);
Insert(l,1,30);
Insert(l,1,40);
Insert(l,1,50);
Insert(l,1,60);
Insert(l,3,90);
int x=21;
cout<<x<<endl;
Print(l);
cout<<"链表长度为:"<<GetLength(l)<<endl;
Delete(l,1,&x);
Print(l);
cout<<x<<endl;
int y=15;
cout<<"删除后的长度为:"<<GetLength(l)<<endl;
GetElem(l,2,&y);
cout<<y<<endl;
Clearlist(l);
}
int main()
{
testdemo();
return 0;
}
你现在彻底理解了头结点的好处和书上所写的设立头结点是为了空表和非空统一方便插入与删除这句话了么??
好像还有一个没做我们设立ElemType是方便各种基本类型对嘛?那我们把int 改成char 换成字符试试换成Char *弄弄字符串?main函数也要改下吧 快去试试吧 亲测有效哦~!
四 进阶实现 根据题目条件编写函数
你以为这你就学会了?还远远没达到考研水平啊 题目变化莫测 用心去感受吧 少年自己尝试完成以下算法的编写?代码附上
- 一 已知线性表元素有有序递增,并以带头结点的单链表作为存储结构,设计一个高效算法,删除表中所有值大于mink且小于maxk的元素
- 运用头插法建立单链表 数据类型为字符 输入$表示结束
- 运用尾插法建立单链表 数据类型为字符 输入$表示结束
- 线性表逆置
- 头结点的单链表,编写一个从中删除自第i个元素起共len个元素的算法
- 设计带头结点单链表中删除最小结点的高效算法
- 递增有序不带头结点的单链表中删除重复的元素
- 删除非空不带头结点单链表中重复的值
- 将带头结点单链表的前m个元素放到最后面(a1,a2,a3...am,b1,b2...bn) turn to(b1,b2...bn,a1,a2...am)
- 两个按元素值递增次序排列的带头结点单链表,编写算法将其归并为按元素值递减排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表
- 指针la,lb代表两个无头结点的单链表,设计算法从la中删除自i个元素起共len个元素后,并将它们插入到lb中第j个元素之前的算法两个带头结点的递增单链表 求其交集 并存放于链表A中
- 设计算法比较链表的大小 0相等 1大于 -1小于
- 带头结点的单链表 数据无重复用直接插入原则 设计算法将其整理成递增有序的
- 两个带头结点的递增单链表 求其交集 并存放于链表A中
- 两个带头结点的递增单链表 求其差集(a出现b不出现) 存储在a中
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include"LINK.h"
using namespace std;
/*void testdemo()
{
Linklist l;
Initlist(l);
Insert(l,1,10);
Insert(l,1,20);
Insert(l,1,30);
Insert(l,1,40);
Insert(l,1,50);
Insert(l,1,60);
Insert(l,3,90);
int x=21;
cout<<x<<endl;
Print(l);
cout<<"链表长度为:"<<GetLength(l)<<endl;
Delete(l,1,&x);
Print(l);
cout<<x<<endl;
int y=15;
cout<<"删除后的长度为:"<<GetLength(l)<<endl;
GetElem(l,2,&y);
cout<<y<<endl;
Clearlist(l);
}*/
//其他功能及算法实现
// 一 已知线性表元素有有序递增,并以带头结点的单链表作为存储结构,设计一个高效算法,删除表中所有值大于mink且小于maxk的元素
/* 核心思想:
运用遍历标记两个指针分别指向[min,max]这个区间min前面第一个节点和max后面第一个节点 运用遍历删除其中元素
*/
/*void Fun1(Linklist &l,ElemType mink,ElemType maxk)
{
Linklist p,q;
p=l->next;
while(p&&p->elem<=mink)
{
q=p;
p=p->next;
}
if(p) //可能不存在满足题目要求的元素
{
while(p&&p->elem<=maxk) p=p->next;
Linklist m=q;next=p; //将断掉的链表重新连接起来
q=q->next;
while(q!=p)
{
Linklist s;
s=q->next;
delete[]q;
q=s;
}
}
}
void test01()
{
Linklist l;
Initlist(l);
Insert(l,1,90);
Insert(l,1,80);
Insert(l,1,70);
Insert(l,1,60);
Insert(l,1,50);
Insert(l,1,40);
Insert(l,1,30);
Insert(l,1,20);
Insert(l,1,10);
Insert(l,1,0);
Print(l);
Fun1(l,45,82);
Print(l);
}*/
// 二 运用头插法建立单链表 数据类型为字符 输入$表示结束
/*void Fun2(Linklist &l)
{
// 不带头结点
/* l=nullptr;
char ch;
while(scanf("%c",&ch)&&ch!='$')
{
Linklist newNode=new LinkNode;
newNode->elem=ch;
newNode->next=l;
l=newNode;
}
//打印程序用来检测
Linklist p=l;
while(p)
{
printf("%c ",p->elem);
p=p->next;
}
printf("\n"); */
//带头结点
/* l=new LinkNode;
l->next=nullptr;
char ch;
while(scanf("%c",&ch)&&ch!='$')
{
Linklist newNode=new LinkNode;
newNode->elem=ch;
newNode->next=l->next;
l->next=newNode;
}
Linklist p=l->next;
while(p)
{
printf("%c ",p->elem);
p=p->next;
}
printf("\n");
}
void test02()
{
Linklist l;
Fun2(l);
}*/
// 三 运用尾插法建立单链表 数据类型为字符 输入$表示结束
//void Fun3(Linklist &l)
//{
//有头结点
/*l=new LinkNode;
Linklist tail=l;
char ch;
while(scanf("%c",&ch)&&ch!='$')
{
Linklist newNode=new LinkNode;
newNode->elem=ch;
tail->next=newNode;
tail=newNode;
}
tail->next=nullptr;
Linklist p=l->next;
while(p)
{
printf("%c ",p->elem);
p=p->next;
}
printf("\n");*/
//无头结点
/* l=nullptr;
Linklist tail=nullptr;
char ch;
while(scanf("%c",&ch)&&ch!='$')
{
Linklist newNode=new LinkNode;
newNode->elem=ch;
if(l==nullptr) l=newNode;
else tail->next=newNode;
tail=newNode;
}
tail->next=nullptr;
Linklist p=l;
while(p)
{
printf("%c ",p->elem);
p=p->next;
}
printf("\n");
}
void test03()
{
Linklist l;
Fun3(l);
}*/
// 四 线性表逆置
//void Fun4(Linklist &l)
//{
//带头结点
/*Linklist p=l->next;
l->next=nullptr;
while(p)
{
Linklist q=p->next;
p->next=l->next;
l->next=p;
p=q;
}*/
//不带头结点 注意 调试此函数的时候要注意Link.cpp中用的是否是不带头结点的那种实现
/* if(l==nullptr) return;
Linklist p=l->next;
l->next=nullptr;
while(p)
{
Linklist q=p->next;
p->next=l;
l=p;
p=q;
}
}
void test04()
{
Linklist l;
Initlist(l);
Insert(l,1,90);
Insert(l,1,80);
Insert(l,1,70);
Insert(l,1,60);
Insert(l,1,50);
Insert(l,1,40);
Insert(l,1,30);
Insert(l,1,20);
Insert(l,1,10);
Print(l);
Fun4(l);
Print(l);
}*/
// 五 头结点的单链表,编写一个从中删除自第i个元素起共len个元素的算法
/*void Fun5(Linklist &l,int i,int len)
{
Linklist p=l->next;
int j=1;
while(p&&j<i-1)
{
p=p->next;
j++;
}
j=0;
while(p->next&&j<len)
{
Linklist q=p->next;
p->next=q->next;
free(q);
j++;
}
}
void test05()
{
Linklist l;
Initlist(l);
Insert(l,1,90);
Insert(l,1,80);
Insert(l,1,70);
Insert(l,1,60);
Insert(l,1,50);
Insert(l,1,40);
Insert(l,1,30);
Insert(l,1,20);
Insert(l,1,10);
Print(l);
Fun5(l,2,4);
Print(l);
}*/
//六 设计带头结点单链表中删除最小结点的高校算法
//打擂的思想有点类似于冒泡排序
/*void Fun6(Linklist &l)
{
Linklist p=l;
Linklist q=p->next;
while(q->next)
{
if(p->next->elem>q->next->elem)
{
p=q;
}
q=q->next;
}
q=p->next;
p->next=q->next;
free(q);
}
void test06()
{
Linklist l;
Initlist(l);
Insert(l,1,90);
Insert(l,1,80);
Insert(l,1,70);
Insert(l,1,60);
Insert(l,1,7);
Insert(l,1,40);
Insert(l,1,30);
Insert(l,1,3);
Insert(l,1,10);
Print(l);
Fun6(l);
Print(l);
}*/
// 七 递增有序不带头结点的单链表中删除重复的元素
// 方法 遍历依次比较
/*void Fun7(Linklist &l)
{
Linklist p=l;
Linklist q=p->next;
while(q)
{
if(q->elem!=p->elem)
{
p=q;
q=q->next;
}
else
{
p->next=q->next;
free(q);
q=p->next;
}
}
}
void test07()
{
Linklist l;
Initlist(l);
Insert(l,1,60);
Insert(l,1,50);
Insert(l,1,50);
Insert(l,1,50);
Insert(l,1,40);
Insert(l,1,40);
Insert(l,1,30);
Insert(l,1,20);
Insert(l,1,10);
Print(l);
Fun7(l);
Print(l);
}*/
// 八 删除非空不带头结点单链表中重复的值
/*void Fun8(Linklist &l)
{
Linklist p=l;
while(p&&p->next) //p用来进行外层循环
{
Linklist r=p->next,q=p; //r用来删除 q用来保留p的缓存
while(r) //r用来进行内层循环
{
if(r->elem==p->elem)
{
q->next=r->next;
free(r);
r=q->next;
}
else
{
q=r;
r=r->next;
}
}
p=p->next;
}
}
void test08()
{
Linklist l;
Initlist(l);
Insert(l,1,17);
Insert(l,1,14);
Insert(l,1,50);
Insert(l,1,25);
Insert(l,1,30);
Insert(l,1,14);
Insert(l,1,25);
Insert(l,1,17);
Insert(l,1,14);
Print(l);
Fun8(l);
Print(l);
}*/
// 九 将带头结点单链表的前m个元素放到最后面(a1,a2,a3...am,b1,b2...bn) turn to(b1,b2...bn,a1,a2...am)
/*void Fun9(Linklist &l,int m)
{
Linklist p=l->next;
Linklist tail=l->next;
int j=1;
while(p&&j<m)
{
j++;
p=p->next;
}
while(tail->next)
{
tail=tail->next;
}
tail->next=l->next;
l->next=p->next;
p->next=nullptr;
}
void test09()
{
Linklist l;
Initlist(l);
Insert(l,1,9);
Insert(l,1,8);
Insert(l,1,7);
Insert(l,1,6);
Insert(l,1,5);
Insert(l,1,4);
Insert(l,1,3);
Insert(l,1,2);
Insert(l,1,1);
Print(l);
Fun9(l,5);
Print(l);
}*/
// 十 两个按元素值递增次序排列的带头结点单链表,编写算法将其归并为按元素值递减排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表
//相当于存在一个逆置的过程 运用头插法的思想
/*void Fun10(Linklist &la,Linklist &lb,Linklist &lc)
{
Linklist p=la->next;
Linklist q=lb->next;
lc=la;
lc->next=nullptr;
Linklist r;//用于缓存
while(q&&p)
{
if(p->elem<q->elem)
{
r=p->next;
p->next=lc->next;
lc->next=p;
p=r;
}
else
{
r=q->next;
q->next=lc->next;
lc->next=q;
q=r;
}
}
while(p)
{
r=p->next;
p->next=lc->next;
lc->next=p;
p=r;
}
while(q)
{
r=q->next;
q->next=lc->next;
lc->next=q;
q=r;
}
free(lb);
}
void test10()
{
Linklist la,lb,lc;
Initlist(la),Initlist(lb),Initlist(lc);
Insert(la,1,18);
Insert(la,1,16);
Insert(la,1,14);
Insert(la,1,12);
Insert(la,1,10);
Insert(la,1,8);
Insert(la,1,6);
Insert(la,1,3);
Insert(la,1,2);
Insert(lb,1,17);
Insert(lb,1,15);
Insert(lb,1,13);
Insert(lb,1,11);
Insert(lb,1,9);
Insert(lb,1,7);
Insert(lb,1,5);
Insert(lb,1,3);
Insert(lb,1,1);
Print(la),Print(lb);
Fun10(la,lb,lc);
Print(lc);
}*/
//十一 指针la,lb代表两个无头结点的单链表,设计算法从la中删除自i个元素起共len个元素后,并将它们插入到lb中第j个元素之前的算法
//思路:我需要获取哪些位置的指针,用遍历去寻找,注意下标
/*void Fun11(Linklist &la,Linklist&lb,int i,int len,int j)
{
Linklist p=la;
int k=1;
while(p&&k<i-1) //寻找i前面第一个结点
{
k++;
p=p->next;
}
Linklist q=la;
k=1;;
while(q&&k<i+len-1)
{
k++;
q=q->next;
}
Linklist r=lb;
k=1;
while(r&&k<j-1)
{
k++;
r=r->next;
}
q->next=r->next;
r->next=p->next;
}
void test11()
{
Linklist la,lb;
Initlist(la),Initlist(lb);
Insert(la,1,18);
Insert(la,1,16);
Insert(la,1,14);
Insert(la,1,12);
Insert(la,1,10);
Insert(la,1,8);
Insert(la,1,6);
Insert(la,1,3);
Insert(la,1,2);
Insert(lb,1,17);
Insert(lb,1,15);
Insert(lb,1,13);
Insert(lb,1,11);
Insert(lb,1,9);
Insert(lb,1,7);
Insert(lb,1,5);
Insert(lb,1,3);
Insert(lb,1,1);
Print(la),Print(lb);
Fun11(la,lb,3,4,2);
Print(lb);
}*/
// 十二 设计算法比较链表的大小 0相等 1大于 -1小于
/*int Fun12(Linklist &la,Linklist &lb)
{
//不带头结点
Linklist p=la;
Linklist q=lb;
while(p&&q&&p->elem==q->elem)
{
p=p->next;
q=q->next;
}
if(!p&&!q) return 0;
else if(q&&p) return p->elem>q->elem?1:-1;
else if(p) return 1;
else return -1;
}
void test12()
{
Linklist la,lb;
Initlist(la),Initlist(lb);
Insert(la,1,18) , Insert(lb,1,18);
Insert(la,1,16) , Insert(lb,1,16);
Insert(la,1,14) , Insert(lb,1,14);
Print(la),Print(lb);
cout<<Fun12(la,lb)<<endl;
}*/
//十三 带头结点的单链表 数据无重复用直接插入原则 设计算法将其整理成递增有序的
/*void Fun13(Linklist &l)
{
Linklist p=l->next->next;
l->next->next=nullptr;
while(p)
{
Linklist r=p->next;
Linklist q=l;
while(q->next&&q->next->elem<p->elem) q=q->next;
p->next=q->next;
q->next=p;
p=r;
}
}
void test13()
{
Linklist l;
Initlist(l);
Insert(l,1,99);
Insert(l,1,110);
Insert(l,1,26);
Insert(l,1,42);
Insert(l,1,7);
Insert(l,1,15);
Insert(l,1,3);
Insert(l,1,2);
Insert(l,1,500);
Print(l);
Fun13(l);
Print(l);
}
*/
//十四 两个带头结点的递增单链表 求其交集 并存放于链表A中
/*void Fun14(Linklist &la,Linklist &lb)
{
Linklist pa=la->next,pb=lb->next;
Linklist lc,pc,u; //u用于删除结点
lc=pc=la;
while(pa&&pb)
{
if(pa->elem==pb->elem)
{
pc->next=pa;
pc=pa;
pa=pa->next;
u=pb;
pb=pb->next;
free(u);
}
else if(pa->elem<pb->elem) {u=pa;pa=pa->next;free(u);}
else{u=pb;pb=pb->next;free(u);}
}
while(pa) {u=pa;pa=pa->next;free(u);}
while(pb){u=pb;pb=pb->next;free(u);}
pc->next=nullptr;
free(lb);
//bug 链表b没有实际删除????????
}
void test14()
{
Linklist la,lb;
Initlist(la), Initlist(lb);
Insert(la,1,555);
Insert(la,1,200);
Insert(la,1,66);
Insert(la,1,50);
Insert(la,1,46);
Insert(la,1,10);
Insert(la,1,3);
Insert(lb,1,75);
Insert(lb,1,66);
Insert(lb,1,10);
Insert(lb,1,8);
Insert(lb,1,3);
Print(la),Print(lb);
Fun14(la,lb);
Print(la);
Print(lb);
cout<<GetLength(lb)<<endl;
}*/
//十五 两个带头结点的递增单链表 求其差集(a出现b不出现) 存储在a中
//思路 不要去想怎么取怎么放 删就完事 要充分利用链表的特点
/*void Fun15(Linklist &la,Linklist &lb)
{
Linklist p=la->next;
Linklist q=lb->next;
Linklist pre=la;
while(p&&q)
{
if(p->elem<q->elem) {pre=p;p=p->next;}
else if(p->elem>q->elem) q=q->next;
else {pre->next=p->next;Linklist u=p;p=p->next;free(u);}
}
}
void test15()
{
Linklist la,lb;
Initlist(la),Initlist(lb);
Insert(la,1,9) , Insert(la,1,8) ,Insert(la,1,7) ,Insert(la,1,6) ,Insert(la,1,5) ,Insert(la,1,4) ,Insert(la,1,3), Insert(la,1,2) ,Insert(la,1,1);
Insert(lb,1,9) ,Insert(lb,1,7),Insert(lb,1,5),Insert(lb,1,3),Insert(lb,1,1);
Print(la),Print(lb);
Fun15(la,lb);
Print(la);
}*/
int main()
{
//testdemo();
//test02();
//test03();
//test04();
//test05();
//test06();
//test07();
//test08();
//test09();
//test10();
//test11();
//test12();
//test13();
//test14();
//test15();
return 0;
}