L线性表
线性结构
线性结构的基本特点就是,除了第一个元素无直接前驱以及最后一个元素无直接后续以外,其他所有元素都有一个前驱和后继。并且线性表是最基本的一种线性结构
线性表的定义
ADT List{
数据对象: ....
数据关系: ....
基本操作: ....
}ADT List
顺序表
顺序存储方法:用一组地址连续的存储单元依次存储线性表的元素,可通过**数组V[n]**来实现。
顺序表的类型定义
#define MAXSIZE 100
typedef struct{
ElemType *elem; //指向数据元素的基地址
int length; //线性表的当前长度
}SqList;
顺序表的基本实现
初始化线性表L(参数用引用的方式)
typedef int Status;//用于返回构造顺序表的一个结果类型
Status InitList_Sq(Sqlist &L) //一个构造顺序表的函数方法,函数名为InitList_Sq,返回值类型为Status
{
L.elem = new ElemType[MAXSIZE];//为顺序表分配空间
if(!L.elem)
exit(OVERFLOW);//分配失败,退出程序
//分配成功继续运行
L.length=0;//表长为0
return OK;
}
初始化线性表(参数用指针的方式)
typedef int Status;
Status InitList_Sq(SqList *L)
{
L->elem = new ElemType[MAXSIZE];
if(!L->elem)
exit(OVERFLOW);
L->length=0;
return OK;
}
销毁线性表L
void DestoryList(SqList &L)
{
if(L.elem)
delete[]L.elem;//回收指针空间;
}
清空线性表L
void ClearList(SqList &L)
{
L.length=0; //将线性表的表长重置为0
}
求线性表L的长度
int GetLength(SqList L)//因为只要对线性表的长度进行一个获取,不需要对表本身进行任何操作,所以不需要进行将参数进行引用处理
{
return L.length;
}
判断线性表L是否为空
int IsEmpty(SqList L)
{
if(L.length==0)
return 1;
return 0;
}
获取线性表L中的某个数据元素的内容
#define OK 1
#define ERROR 0
//根据指定位置,获取相应位置数据元素的内容
int GetElem(SqList L,int i,ElemType &e)
{
if(i<1||i>L.length) //先判断查找位置是否存在
return ERROR;
e = L.elem[i-1] //因为数组是从0开始的,所以第i个位置对应于数组第i-1个元素
return OK;
}
在线性表L中查找值为e的数据元素
int LocateElem(SqList L,ElemType e)
{
for(int i=0;i<L.length;i++)
{
if(L.elem[i] == e)
return i+1;
}
return 0;
}
在线性表中指定位置前插入一个元素
在一个长度为n的线性表插入一个元素后,就变成了长度为n+1的线性表
就需要将第i至第n共**(n-i+1)**个元素后移。
并且在进行数组操作的时候要从最后一个元素开始移动
首先我们要判断以下几个问题
(1)判断插入位置i是否合法。
(2)判断顺序表的存储空间是否已满。
(3)将第n至第i位的元素依次向后移动一个位置,空出第i个位置。
(4)将要插入的新元素e放入第i个位置。
(5)表长加1,插入成功返回OK。
typedef int Status;
#define OK 1
#define ERROR 0
#define MAXSIZE 100
Status ListInsert_Sq(SqList &L,int i,ElemType e)
{
if(i<1||i>L.length+1)//插入位置可以是最后一个元素的后面也就是i+1这个位置。
return ERROR;
if(L.length==MAXSIZE)//判断存储空间是否满
return ERROR;
for(j=L.length-1;j>=i-1;j--)
{
L.elem[j+1]=L.elem[j];
}
L.elem[i-1]=e;
L.length++;
return OK;
}
对于对应位置的删除操作
ListDelete(&L,i,&e)
其中要删除第i个位置上的元素,则其后元素共n-i个元素要从前往后移。
(1)判断删除位置i是否合法。(合法值为1<=i<=n)
(2)将欲删除的元素保留在e中。
(3)将第i+1至第n位的元素依次向前移动一个位置。
(4)表长减1,删除成功返回OK
Status ListDelete_Sq(SqList &L,int i,ElemType &e)
{
if(i<1||i>L.length)//判断位置是否合法
{
return ERROR;
}
e=L.elem[i-1];//将删除的值赋予e,达到一个返回的作用。
for(j=i;j<=L.length-1;j++)
{
L.elem[j-1]=L.elem[j];
}
L.length--;
return OK;
}
线性表的优缺点
优点
存储密度大
可以随机存取表中任一元素
缺点
在插入、删除某一元素时,需要移动大量元素
浪费存储空间
属于静态存储形式,数据元素的个数不能自由扩充
链表
节点的组成
各结点由两个域组成
数据域 | 存储元素数值数据 |
---|---|
指针域 | 存储直接后继结点的存储位置 |
链表分类
单链表:结点只有一个指针域的链表,称为单链表或者线性链表。
双链表:有两个指针域的链表,称为双链表。
循环链表:首尾相接的链表称为循环链表。
头指针、头结点和首元结点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5duBz1w-1632402892852)(C:\Users\Lenovo\Desktop%9BQ}X3E@6$0]C_3XV_7UEB.jpg)]
头指针:是指向链表中第一个结点的指针。
首元结点:是指链表中存储第一个数据元素a1的结点。
头结点:是在链表的首元结点之前附设的一个结点;数据域内只存放空表标志和表长等信息。
在链表中设置头结点的好处
1.便于首元结点的处理:
首元结点的地址保存在头结点的指针域中,所以在链表中的第一个位置上的操作和其它位置一致,无须进行特殊处理。
2.便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就是统一了。
链表的优缺点
优点
1.数据元素的个数可以自由扩充。
2.插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高。
缺点
1.存储密度小。
2.存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问。
单链表的定义和实现
单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名。
若头指针名是L,则把链表称为表L。
typedef struct LNode{
ElemType data;//数据域
struct LNode *next;//指针域
}LNode,*LinkList;
//*LinkList 为LNode类型的指针
//其中 LNode *p 和 LinkList p 是等价的
单链表基本操作的实现
初始化(构造一个空表)
算法分析:生成新结点作为头结点,用头指针L指向头结点。
头结点的指针域置空。
typedef int Status;
Status InitList_L(LinkList &L)
{
L=new LNode; //生成头结点
L->next=NULL;
return OK;
}
销毁操作
Status DestoryList_L(LinkList &L)
{
LinkList p;
while(L)
{
p=L;
L=L->next;
delete p;
}
return OK;
}
清空链表
Status ClearList(LinkList &L)
{
LinkList p,q;
p=L->next;//p指向首元结点
while(p)//p指针非空,可以继续进行操作。
{
q=p->next;
delete p;
p=q;
}
L->next=NULL;//将头结点指针域设置为空
return OK;
}
求表长
int ListLength_L(LinkList L)
{
LinkList p;
p=L->next;
int i=0;
while(p)
{
i++;
p=p->next;
}
return i;
}
判断链表是否为空
int ListEmpty(LinkList L)
{
if(L->next)
return 0;//为非空
else
return 1;//为空
}
查找操作
按照序号进行查找
Get(L,i,&e)
查找在链表L中第i个元素的值,如果找到,将其赋值给e进行返回操作。
Status GetElem_L(LinkList L,int i,ElemType &e)
{
LinkList p;
p=L->next;
j=1;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i)
return ERROR;
else
{
e=p->data;
return OK;
}
}
按照值进行查找
LNode *LocateElem_L(LinkList L,Elemtype e)
{
LinkList p;
p=L->next;
while(p&&p->data!=e)
{
p=p->next;
}
return p;//如果找到就返回p指针,如果没有找到的返回值就为NULL
}
插入操作
案例:将s节点插入到ai节点前,则需要将p指针移动到ai-1节点的位置进行操作。
//在L中第i个元素之前插入数据元素e
Status ListInsert_L(LinkList &L,int i,ElemType e)
{
LinkList p;
p=L;
int j=0;
while(p&&j<i-1)//寻找i-1个节点,所以p指针要在头结点的位置,而不是首元结点。
{
j++;
p=p->next;
}
if(!p||j>i-1)//i的位置为非法位置,无法进行寻找
return ERROR;
s=new LNode;
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}//让新节点s和第i个位置的节点建立联系,然后让第i-1个节点和新节点s建立联系,并同时把第i-1个节点和第i个节点之前的联系断开。
删除操作
Status ListDelete_L(LinkList &L,int i,ElemType &e)
{
LinkList p;
p=L;
int j=0;
while(p->next&&j<i-1)//寻找第i-1个结点,并且令p指向其前驱结点
{
p=p->next;
j++;
}
if(!(p->next)||j>i-1)//要删除的位置i非法
return ERROR;
LinkList q;
q=p->next;
p->next=q->next;
e=q->data;
delete q;
return OK;
}
单链表的建立
前插法
创建一个空表
生成新结点
将读入的数据存放到新结点的数据域中
将该新结点插入到链表的前端(头结点的后面)
void CreatList_F(LinkList &L,int n)
{
L=new LNode;
L->next=NULL;
for(int i=n;i>0;i--)
{
p=new LNode;
cin>>>p->data;
p->next=L->next;
L->next=p;
}
}
尾插法
void CreatList_L(LinkList &L,int n)
{
L=new LNode;
L->next=NULL;
r=L;
for(int i=0;i<n;i++)
{
p=new LNode;
cin>>p->data;
p->next=NULL;
r->next=p;//插入到表尾
r=p;//将r指向新的表尾
}
}
两个循环链表的合并操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GmrlDUX-1632402892872)(C:\Users\Lenovo\Desktop\QQ图片20210923172916.jpg)]
LinkList *CONNECT(LinkList *ra,LinkList *rb)
{
LinkList *p;
p=ra->next;
ra->next=rb->next->next;
free(rb->next);
rb->next=p;
return rb;
}
双向链表
创建格式
typedef struct DulNode{
ElemType data;
struct DulNode *prior;
struct DulNode *next;
}DulNode,*DuLinkList;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8YNYAIpj-1632402892880)(C:\Users\Lenovo\Desktop\QQ图片20210923183859.jpg)]
Status ListInsert_DuL(DuLinkList &L,int i,ElemType e)
{
if(!(p=GetElemP_Dul(L,i)))//查找是否存在第i个值在L表中
return ERROR;
s=new DuLNode;
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return OK;
}
双向链表的删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvvpUcXn-1632402892885)(C:\Users\Lenovo\Desktop\QQ图片20210923185525.jpg)]
Status ListDelete_Dul(DuLinkList &L,int i,ElemType &e)
{
if(!(p=GetElemP_Dul(L,i)))
return ERROR;
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
delete p;
return OK;
}
线性表的应用
无序线性表的合并
利用顺序表进行操作
#include<iostream>
#include<windows.h>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef struct
{
ElemType * elem;
int Length;
}SqList;
Status InitList(SqList &L)
{
L.elem = new ElemType[MAXSIZE];
if(!L.elem)
return ERROR;
else
{ L.Length=0;
return OK;
}
}
int List_Length(SqList L)
{
return L.Length;
}
void Creat_List(SqList &L)
{
int n;
cout<<"请输入要顺序表的元素个数"<<endl;
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个元素的值为: ";
cin>>L.elem[i];
L.Length++;
}
}
void Combine(SqList &La,SqList &Lb)
{
for(int i=0;i<Lb.Length;i++)
{
int flag=0;
for(int j=0;j<La.Length;j++)
{
if(La.elem[j]==Lb.elem[i])
{
flag++;
}
}
if(!flag)
{
La.elem[La.Length++]=Lb.elem[i];
}
}
}
void TravelList(SqList &L)
{
cout<<"顺序表的值为:";
for(int i=0;i<L.Length;i++)
{
cout<<L.elem[i]<<" ";
}
}
int main()
{
SqList A,B;
if(InitList(A))
{
cout<<"顺序表A初始化成功!"<<endl;
Creat_List(A);
TravelList(A);
}
else
{
cout<<"顺序表A初始化失败!"<<endl;
exit(0);
}
if(InitList(B))
{
cout<<"顺序表B初始化成功!"<<endl;
Creat_List(B);
TravelList(B);
}
else
{
cout<<"顺序表B初始化失败!"<<endl;
exit(0);
}
Sleep(100);
cout<<endl<<"正在将A和B顺序表进行结合!!!"<<endl;
Combine(A,B);
cout<<"结合后的表长为"<<A.Length<<endl;
TravelList(A);
}
进行有序的线性表的合并
利用顺序表操作
要求:已知线性表La和Lb中的数据元素按值非递增有序排列,现要求将La和Lb归为一个新的线性表Lc,且Lc中的数据元素扔按值非递增有序排列。
#include<iostream>
using namespace std;
#define ERROR 0
#define OK 1
#define MAXSIZE 100
typedef int Status;
typedef int ElemType;
int x=0;
typedef struct
{
ElemType *elem;
int length;
}SqList;
Status InitList(SqList &L)
{
L.elem = new ElemType[MAXSIZE];
if(!L.elem)
return ERROR;
else
{
L.length=0;
return OK;
}
}
int LengthList(SqList &L)
{
return L.length;
}
void CreatList(SqList &L)
{
int n;
cout<<"请输入第"<<x+1<<"个顺序表的元素个数"<<endl;
x++;
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"顺序表第"<<i+1<<"个值为:";
cin>>L.elem[i];
L.length++;
}
}
void TravelList(SqList &L)
{
cout<<"顺序表的值为:";
for(int i=0;i<L.length;i++)
{
cout<<L.elem[i]<<" ";
}
}
void Union(SqList La, SqList Lb,SqList &Lc)
{
ElemType *pa = La.elem;
ElemType *pb = Lb.elem;
Lc.length=La.length+Lb.length;
Lc.elem = new ElemType[Lc.length];
ElemType *pc = Lc.elem;
ElemType *pa_last=La.elem+La.length-1;
ElemType *pb_last=Lb.elem+Lb.length-1;
while(pa<=pa_last&&pb<=pb_last)
{
if(*pa<=*pb)
{
*pc++=*pb++;
}
else
{
*pc++=*pa++;
}
}
while(pa<=pa_last)
{
*pc++=*pa++;
}
while(pb<=pb_last)
{
*pc++=*pb++;
}
}
int main()
{
SqList A,B;
if(InitList(A))
{
cout<<"顺序表A初始化成功!"<<endl;
CreatList(A);
TravelList(A);
}
else
{
cout<<"顺序表初始化失败!"<<endl;
exit(0);
}
if(InitList(B))
{
cout<<"顺序表B初始化成功!"<<endl;
CreatList(B);
TravelList(B);
}
else
{
cout<<"顺序表初始化失败"<<endl;
exit(0);
}
SqList C;
Union(A,B,C);
cout<<"顺序表C的表长为"<<C.length<<endl;
TravelList(C);
return 0;
}
利用链表操作进行有序的线性表合并
#include<iostream>
using namespace std;
#define ERROR 0
#define OK 1
typedef int Status;
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode * next;
}LNode,*LinkList;
Status InitList(LinkList &L)
{
L = new LNode;
L->next=NULL;
return OK;
}
void CreatList(LinkList &L)
{
int n;
cout<<"请输入要插入的结点数目:";
cin>>n;
LinkList p;
for(int i=0;i<n;i++)
{
p = new LNode;
cout<<"请输入第"<<i+1<<"个结点的值:";
cin>>p->data;
p->next=L->next;
L->next=p;
}
}
void TravelList(LinkList &L)
{
int i=0;
LinkList p;
p=L->next;
while(p)
{
if(!i){
cout<<"链表的值为:";
i++;
}
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
void MergeList(LinkList &la,LinkList &lb,LinkList &lc)
{
cout<<"进行有序链表排序"<<endl;
LinkList pa,pb,pc;
pa = la->next;
pb = lb->next;
pc=lc=la;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
pc->next=pa;
pc=pa;
pa=pa->next;
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
delete lb;
}
int main()
{LinkList A,B,C;
if(InitList(A))
{
cout<<"链表A初始化成功!"<<endl;
CreatList(A);
TravelList(A);
}
else
{
cout<<"链表初始化失败!"<<endl;
}
if(InitList(B))
{
cout<<"链表B初始化成功!"<<endl;
CreatList(B);
TravelList(B);
}
else
{
cout<<"链表初始化失败!"<<endl;
}
if(InitList(C))
{
cout<<"链表C初始化成功!"<<endl;
}
else
{
cout<<"链表初始化失败!"<<endl;
}
MergeList(A,B,C);
TravelList(C);
return 0;
}