顺序表
- 顺序表
- 单链表
- 双向链表
- 循环链表
- 线性表的应用
- 线性表学习秘籍
1.顺序表
顺序表静态分配定义
#define Maxsize 100
typedef struct{
ElemType data[Maxsize];//顺序表的基地址
int length;//顺序表的长度
}Sqlist;
提高可移植性
typedef int ElemType;//给int起个别名ElemType
顺序表的基本操作
1.初始化
bool InitList(SqList &L)
{
L.elem = new int[Maxsize]; //为顺序表动态分配Maxsize个空间
if(!L.elem) return false; //分配空间失败
L.length = 0; //顺序表的长度为0
return true;
}
2.创建
bool CreateList(SqList &L)
{
int x,i = 0;
while(x!=-1)//输入-1时结束,也可改为其他条件
{
if(L.length == Maxsize)
{
cout<<"顺序表已满!"
return false;
}
cin>>x; //输入一个数据元素
L.elem[i++]=x; //将数据存入第i个位置。然后i++
L.length++; //顺序表的长度加1
}
return true;
}
3.取值
bool GetElem(SqList L,int i,int &e)
{
if(i<1||i>L.length) return false;
e=L.elem[i-1]; //第i-1个单元存储着第i个数据
return true;
}
4.查找
int LocateElem(SqList L,int e)
{
for(i=0;i<L.length;i++)
if(L.elem[i]==e) return i+1;
return -1; //如果没有找到就返回-1
}
5.插入
bool ListInsert_Sq(SqList &L,int i, int e)
{
if(i<1 || i>L.length) return false;
if(L.length==Maxsize) return false;
for(int j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j]; //从最后一个元素开始后移,直到第i个元素后移
L.elem[i-1]=e;
L.length++;
return true;
}
6.删除
bool ListDelete_Sq(SqList &L;int i; int &e)
{
if(i<1 || i>L.length) return false;
e=L.elem[i-1];
for(int j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j];
L.length--;
return true;
}
2.单链表
单链表的存储方式
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}Lnode,*LinkList;
单链表的基本操作
1.初始化
bool InitList_L(LinkList &L) //构造一个空的单链表L
{
L=new LNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成头节点失败
L->next=NULL; //头节点的指针域空
return true;
}
2.创建(创建单链表有两种方式“头插法”,“尾插法”)
2.1.头插法
void CreateList_H(LinkList &L)//头插法创建单链表
{
int n; //输入n个元素的值建立到头节点的单链表L
LinkList s; //定义一个指针变量
L=new LNode;
L->next=NULL; //先建立一个带头节点的空链表
cout<<"请输入n个元素:"<<endl;
cin>>n;
cout<<"请依次输入n个元素:"<<endl;
cout<<"头插法创建单链表..."<<endl;
while(n--)
{
s=new LNode; //生成新节点s
cin>>s->data; //输入元素值赋值给新节点的数据域
s->next=L->next;
L->next=s; //将新节点s插入头节点之后
}
}
2.2.尾插法
void CreateList_R(LinkList &L)
{
int n;
LinkList s,r;
L=new LNode;
L->next=NULL;
r=L;
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"依次输入n个元素:"<<endl;
cout<<"尾插法创建单链表..."<<endl;
while(n--)
{
s=new LNode;
cin>>s->data;
s->next=NULL;
r->next=s;
r=s;
}
}
3.取值
bool GetElem_L(LinkList L,int i, int &e)
{
//在带头节点的单链表L中查找第i个元素
//用e记录L中第i个数据元素的值
int j;
LinkList p;
p=L->next; //p指向第一个节点
j=1; //j为计数器
while(j<i&&p) //顺着链表向后扫描,直到p指向第i个元素或者p为空
{
p=p->next; //指向下一个节点
j++; //计数器j加1
}
if(!p||j>i) //i值不合法,i>n或i<=0
return false;
e=p->data; //取第i个节点的数据域
return true;
}
4.查找
bool LocateElem_L(LinkList L,int e) //在带头节点的单链表L中查找值为e的元素
{
LinkList p;
p=L->next;
while(p&&p->data!=e) //沿着链表向后扫描,直到p为空或p所指节点数据域等于e
p=p->next; //p指向下一个节点
if(!p)
return false; //查找失败,p为NULL
return true;
}
5.插入
bool ListInsert_L(LinkList &L,int i,int e)
{
//在带头节点的单链表L中第i个位置之前插入值为e的新节点
int j;
LinkList p,s;
p=L;
j=0;
while(p&&j<i-1) //查找第i-1个节点
{
p=p->next;
j++;
}
if(!p||j>i-1) //i>n+1或者i<1
return false;
s=new LNode; //生成新结点
s->data=e; //将数据元素e放入新节点的数据域
s->next=p->next; //将新节点的指针域指向第i个节点
p->next=s; //将节点p的指针域指向节点s
return true;
}
6.删除
bool ListDelete_L(LinkList &L,int i)
{ //在带头节点的单链表L中,删除第i个位置
LinkList p,q;
int j;
p=L;
j=0;
while((p->next)&&(j<i-1)) //查找第i-1个节点,p指向该节点
{
p=p->next;
j++;
}
if(!(p->next)||(j>i-1))
return false;
q=p->next;
p->next=q->next;
delete q;
return true;
}
3.双向链表
双向链表的储存方式
typedef struct DuLnode{
ElemType data;
struct DuLnode *prior,*next;
}DuLNode,*DuLinkList;
双向链表的基本操作
1.初始化
bool InitList_L(DuLinkList &L) //构造一个空的双向链表L
{
L=new DuLNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成节点失败
L->prior=L->next=NULL; //头节点的两个指针域置空
return true;
}
2.创建
创建双向链表也可以用头插法或者尾插法。头插法创建的链表和输入顺序正好相反,称为逆序建表;尾插法创建的链表和输入顺序一致,称为正序建表。
2.1.头插法建双向链表
void CreateDuList_H(DuLinkList &L)
{ //输入n个元素的值,建立到头节点的单链表L
int n;
DuLinkList s; //定义一个指针变量
L=new DuLNode;
L->prior=L->next=NULL; //先建立一个带头节点的空链表
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"请依次输入n个元素:"<<endl;
cout<<"头插法创建单链表..."<<endl;
while(n--)
{
s=new DuLNode; //生成新节点s
cin>>s->data; //输入元素值赋值给新节点的数据域
if(L->next) //如果L后面有节点,则修改其后面的节点的prior指针,
//否则只修改后面3个指针即可
L->next->prior=s;
s->next=L->next;
s->prior=L;
L->next=s; //将新节点s插入头节点之后
}
}
2.2.尾插法建双向链表
bool ListInsert_L(DuLinkList &L,int i,int &e)
{
//在带头结点的单链表L中第i个位置之前插入值为e的新节点
int j;
DuLinkList p,s;
p=L;
j=0;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i) //i>n+1或者i<1
return false;
s=new DuLNode; //生成新节点
s->data=e; //将新节点的数据域置为e
p->prior->next=s;
s->prior=p->prior;
s->next=p;
p->prior=s;
return true;
}
3.取值和查找
双向链表的取值和查找和单链表一样
4.插入
bool ListInsert_L(DuLinkList &L,int i,int &e)//双向链表的插入
{
//在带头节点的双线链表L中第i个位置之前插入值为e的新节点
int j;
DuLinkList p,s;
p=L;
j=0;
while(p&&(j<i)) //查找第i个节点,p指向该节点
{
p=p->next;
j++;
}
if(!p||(j>i)) //当i>n或i<1
return false;
s=new DuLNode; //生成新结点
s->data=e; //将新节点的数据域置为e
p->prior->next=s;
s->prior=p->prior;
s->next=p;
p->prior=s;
return true;
}
5.删除
bool ListDelete_L(DuLinkList &L,int i)//双向链表的删除
{
//在带头节点的双线链表L中,删除第i个节点
DuLinkList p;
int j;
p=L;
j=0;
while(p&&(j<i)) //查找第i个节点,p指向该节点
{
p=p->next;
j++;
}
if(!p||(j>i)) //当i>n或i<1时,删除位置不合理
return false;
if(p->next) //如果p的后继节点存在
p->next->prior=p->next;
p->prior->next=p->next;
delete p; //释放被删除的节点空间
return true;
}
4.循环链表
循环链表和普通链表的区别是最后一个节点的后继指向了头节点。
5.线性表的应用
1.合并有序顺序表
题目:将两个有序(非递减)顺序表La和Lb合并为一个新的有序(非递减)顺序表。
算法代码
void MergeSqlist(SqList La,SqList Lb,SqList &Lc)
{
int i,j,k;
i=j=k=0;
Lc.length=La.length+Lb.length;
Lc.elem=new int[Lc.Length];
while(i<La.length&&j<Lb.length)
{
if(La.elem[i]<=Lb.elem[j])
Lc.elem[k++]=La.elem[i++];
else
Lc.elem[k++]=Lb.elem[i++];
}
while(i<La.length)
Lc.elem[k++]=La.elem[i++];
while(j<Lb.length)
Lc.elem[k++]=Lb.elem[i++];
}
完整代码
#include<bits/stdc++.h>
using namespace std;
#define Maxsize 100
typedef int ElemType;
typedef struct{
ElemType *elem;//顺序表的基地址
int length;//顺序表的长度
}SqList;
bool InitList(SqList &L) //初始化
{
L.elem = new int[Maxsize]; //为顺序表动态分配Maxsize个空间
if(!L.elem) return false; //分配空间失败
L.length = 0; //顺序表的长度为0
return true;
}
void MergeSqlist(SqList La,SqList Lb,SqList &Lc)//顺序表合并函数
{
int i,j,k;
i=j=k=0;
Lc.length=La.length+Lb.length;
Lc.elem=new int[Lc.length];
while(i<La.length&&j<Lb.length)
{
if(La.elem[i]<=Lb.elem[j])
Lc.elem[k++]=La.elem[i++];
else
Lc.elem[k++]=Lb.elem[j++];
}
while(i<La.length)
Lc.elem[k++]=La.elem[i++];
while(j<Lb.length)
Lc.elem[k++]=Lb.elem[j++];
}
void printList(SqList L) //输出函数
{
for(int i=0;i<L.length;i++)
cout<<L.elem[i]<<" ";
cout<<endl;
}
void scanfList(SqList &L) //读入函数
{
cout<<"请输入顺序表的长度:"<<endl;
cin>>L.length;
cout<<"请输入顺序表的元素:"<<endl;
for(int i=0;i<L.length;i++)
cin>>L.elem[i];
}
int main()
{
SqList La;InitList(La);
scanfList(La);
SqList Lb;InitList(Lb);
scanfList(Lb);
SqList Lc;InitList(Lc);
cout<<"顺序表La的元素为:"<<endl;
printList(La);
cout<<"顺序表Lb的元素为:"<<endl;
printList(Lb);
MergeSqlist(La,Lb,Lc);
cout<<"顺序表Lc的元素为:"<<endl;
printList(Lc);
}
2.合并有序链表
题目:将两个有序(非递减)单链表La和Lb合并作为一个新的有序(非递减)单链表。
算法代码
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next;
q=Lb->next;
Lc=La;
r=Lc;
while(p&&q)
{
if(p->data<=q->data)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;
delete Lb;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;
bool InitList_L(LinkList &L) //构造一个空的单链表L
{
L=new LNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成头节点失败
L->next=NULL; //头节点的指针域空
return true;
}
void CreateList_R(LinkList &L)
{
int n;
LinkList s,r;
L=new LNode;
L->next=NULL;
r=L;
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"依次输入n个元素:"<<endl;
cout<<"尾插法创建单链表..."<<endl;
while(n--)
{
s=new LNode;
cin>>s->data;
s->next=NULL;
r->next=s;
r=s;
}
}
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next;
q=Lb->next;
Lc=La;
r=Lc;
while(p&&q)
{
if(p->data<=q->data)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;
delete Lb;
}
void printLinkList(LinkList L)
{
LinkList p;
p=L->next;
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
int main()
{
LinkList La;InitList_L(La);
CreateList_R(La);
LinkList Lb;InitList_L(Lb);
CreateList_R(Lb);
cout<<"有序链表La为:";
printLinkList(La);
cout<<"有序链表Lb为:";
printLinkList(Lb);
LinkList Lc;InitList_L(Lc);
mergelinklist(La,Lb,Lc);
cout<<"有序链表Lc为:";
printLinkList(Lc);
return 0;
}
3.就地逆置单链表
题目:将带有头节点的单链表就地逆置,即元素的顺序逆转,而辅助空间复杂度为O(1)。
算法代码
void reverselinklist(LinkList &L)
{
LinkList p,q;
p=L->next;
L->next=NULL;
while(p)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;
bool InitList_L(LinkList &L) //构造一个空的单链表L
{
L=new LNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成头节点失败
L->next=NULL; //头节点的指针域空
return true;
}
void CreateList_R(LinkList &L)
{
int n;
LinkList s,r;
L=new LNode;
L->next=NULL;
r=L;
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"依次输入n个元素:"<<endl;
cout<<"尾插法创建单链表..."<<endl;
while(n--)
{
s=new LNode;
cin>>s->data;
s->next=NULL;
r->next=s;
r=s;
}
}
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next;
q=Lb->next;
Lc=La;
r=Lc;
while(p&&q)
{
if(p->data<=q->data)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;
delete Lb;
}
void printLinkList(LinkList L)
{
LinkList p;
p=L->next;
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
void reverselinklist(LinkList &L)
{
LinkList p,q;
p=L->next;
L->next=NULL;
while(p)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
int main()
{
LinkList La;InitList_L(La);
CreateList_R(La);
LinkList Lb;InitList_L(Lb);
CreateList_R(Lb);
cout<<"有序链表La为:";
printLinkList(La);
cout<<"有序链表Lb为:";
printLinkList(Lb);
LinkList Lc;InitList_L(Lc);
mergelinklist(La,Lb,Lc);
cout<<"有序链表Lc为:";
printLinkList(Lc);
reverselinklist(Lc);
cout<<"转置后有序链表Lc为:";
printLinkList(Lc);
return 0;
}
4.查找链表的中间节点
题目:带有头结点的单链表L,设计一个尽可能高效的算法求L的中间节点。
算法代码
LinkLsit findmiddle(LinkList L)
{
LinkList p,q;
p=L;
q=L;
while(p!=NULL&&p->next!=NULL)
{
p=p->next->next;
q=q->next;
}
return q;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;
bool InitList_L(LinkList &L) //构造一个空的单链表L
{
L=new LNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成头节点失败
L->next=NULL; //头节点的指针域空
return true;
}
void CreateList_R(LinkList &L)
{
int n;
LinkList s,r;
L=new LNode;
L->next=NULL;
r=L;
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"依次输入n个元素:"<<endl;
cout<<"尾插法创建单链表..."<<endl;
while(n--)
{
s=new LNode;
cin>>s->data;
s->next=NULL;
r->next=s;
r=s;
}
}
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next;
q=Lb->next;
Lc=La;
r=Lc;
while(p&&q)
{
if(p->data<=q->data)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;
delete Lb;
}
void printLinkList(LinkList L)
{
LinkList p;
p=L->next;
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
void reverselinklist(LinkList &L)
{
LinkList p,q;
p=L->next;
L->next=NULL;
while(p)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
int findmiddle(LinkList L)
{
LinkList p,q;
p=L;
q=L;
while(p!=NULL&&p->next!=NULL)
{
p=p->next->next;
q=q->next;
}
return q->data;
}
int main()
{
LinkList La;InitList_L(La);
CreateList_R(La);
LinkList Lb;InitList_L(Lb);
CreateList_R(Lb);
cout<<"有序链表La为:";
printLinkList(La);
cout<<"有序链表Lb为:";
printLinkList(Lb);
LinkList Lc;InitList_L(Lc);
mergelinklist(La,Lb,Lc);
cout<<"有序链表Lc为:";
printLinkList(Lc);
cout<<"Lc的中间值为:";
int mid=findmiddle(Lc);
cout<<mid<<endl;
return 0;
}
LinkList findk(LinkList L,int k)
{
LinkList p,q;
p=L->next;
q=L->next;
while(p->next!=NULL)
{
if(--k<=0)
q=q->next;
p=p->next;
}
if(k>0)
return NULL;
else
return q;
}
5.删除链表中的重复元素
算法代码
void Deleterep(LinkList &L)
{
LinkList p,q;
int x,n;
int *flag=new int[n+1];
for(int i=0;i<n+1;i++)
flag[i]=0;
p=L;
while(p->next!=NULL)
{
x=abs(p->next->data);
if(flag[x]==0)
{
flag[x]=1;
p=p->next;
}
else
{
q=p->next;
p->next=q->next;
delete q;
}
}
delete []flag;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}LNode,*LinkList;
bool InitList_L(LinkList &L) //构造一个空的单链表L
{
L=new LNode; //生成新节点作为头节点,用头指针L指向头节点
if(!L)
return false; //生成头节点失败
L->next=NULL; //头节点的指针域空
return true;
}
void CreateList_R(LinkList &L)
{
int n;
LinkList s,r;
L=new LNode;
L->next=NULL;
r=L;
cout<<"输入元素个数n:"<<endl;
cin>>n;
cout<<"依次输入n个元素:"<<endl;
cout<<"尾插法创建单链表..."<<endl;
while(n--)
{
s=new LNode;
cin>>s->data;
s->next=NULL;
r->next=s;
r=s;
}
}
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next;
q=Lb->next;
Lc=La;
r=Lc;
while(p&&q)
{
if(p->data<=q->data)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;
delete Lb;
}
void printLinkList(LinkList L)
{
LinkList p;
p=L->next;
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
void reverselinklist(LinkList &L)
{
LinkList p,q;
p=L->next;
L->next=NULL;
while(p)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
int findmiddle(LinkList L)
{
LinkList p,q;
p=L;
q=L;
while(p!=NULL&&p->next!=NULL)
{
p=p->next->next;
q=q->next;
}
return q->data;
}
void Deleterep(LinkList &L)
{
LinkList p,q;
int x,n;
int *flag=new int[n+1];
for(int i=0;i<n+1;i++)
flag[i]=0;
p=L;
while(p->next!=NULL)
{
x=abs(p->next->data);
if(flag[x]==0)
{
flag[x]=1;
p=p->next;
}
else
{
q=p->next;
p->next=q->next;
delete q;
}
}
delete []flag;
}
int main()
{
LinkList La;InitList_L(La);
CreateList_R(La);
LinkList Lb;InitList_L(Lb);
CreateList_R(Lb);
cout<<"有序链表La为:";
printLinkList(La);
cout<<"有序链表Lb为:";
printLinkList(Lb);
LinkList Lc;InitList_L(Lc);
mergelinklist(La,Lb,Lc);
cout<<"有序链表Lc为:";
printLinkList(Lc);
Deleterep(Lc);
cout<<"删除重复的数字的有序链表Lc为:";
printLinkList(Lc);
return 0;
}
6.线性表学习秘籍
1.顺序表秘籍
顺序表解题时需要注意几个问题。
1)位序和下标差1,第i个元素的下标为i-1。
2)移动元素时,特别注意先后顺序,以免覆盖。
3)交换元素、有序合并需要借助辅助空间。
2.链表解题秘籍
链表解题时需要注意几个问题。
1)赋值语句两端的含义。
2)修改指针顺序。
3)建立链表的两种方法:头插法、尾插法。头插法是逆序建表,尾插法是正序建表。
4)链表逆置、归并不需要额外空间,属于就地操作。
5)快慢指针法:快慢指针可以解决很多问题,如链表中间节点、倒数第k个节点、判断链表是否有环、环的起点、公共部分起点等。