文章目录
线性表
-
定义: 一个线性表是n个数据元素的有限序列。
除第一个元素之外,每个元素有且仅有一个直接前驱;除最后一个元素以外,每个元素有且仅有一个直接后继。 -
性质:
个数有限;具有逻辑上的顺序性,表中元素有先后次序;都是数据元素,每个元素都是单个元素;数据类型都相同,每个元素占有相同大小的存储空间;具有抽象性,仅讨论元素间的逻辑关系,而不考虑元素究竟表示什么内容
线性表的顺序表示和实现
- 顺序表的定义
用一组地址连续的存储单位依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
静态分配:
大小和空间都实现固定!!
#define MaxSize 10
typedef struct{
Elemtype data[MaxSize];
int length;
}SqList;
动态分配:
存储数据的空间在程序执行的过程中通过动态存储语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间。
#define InitSize 100
typedef struct{
ElemType *data;
int MaxSize,length;
}SeqList;
初始化:
Status InitList_Sq(SqList &L){
L.elem=(ElemType*)malloc(sizeof(ElemType)*InitSize);
L.length=0;
return OK;
}
特点:
1.随机访问,通过首地址和元素序号可在时间 O ( 1 ) O(1) O(1)内找到指定的元素。
2.存储密度高,每个结点只存储数据元素。
3.逻辑上相邻的元素物理上也相邻,插入和删除操作需要移动大量的元素。
4.拓展不方便
- 顺序表的基本操作
插入数据元素:
1
≤
i
≤
L
i
s
t
L
e
n
g
t
h
_
S
q
+
1
1 \leq i \leq ListLength\_Sq+1
1≤i≤ListLength_Sq+1
Status ListInsert_Sq(SqList &L,int i,ElemType e){
//在顺序线性表L中第i个位置之前插入新的元素e
if(i<1||i>L.length+1) //判断i的范围是否有效
return false;
if(L.length>=L.listsize){//当前存储空间已满,增加分配
newbase=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase) //存储分配失败
exit(OVERFLOW);
L.elem=newbase; //新基址
L.listsize=+=LISTINCREMENT;//增加存储容量
}
q=&(L.elem[i-1]);
for(p=L.elem[L.length-1];p>=q;--p)
L.data[p]=L.data[p-1];
L.data[p-1]=e;
L.length++;
return true;
}
最好情况:在表尾插入 ( i = n + 1 ) (i=n+1) (i=n+1),元素后移语句将不执行,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:在表头插入 ( i = 1 ) (i=1) (i=1),元素后移语句将执行n次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)
删除数据元素:
删除第
i
(
1
≤
i
≤
n
)
i(1\leq i \leq n)
i(1≤i≤n)个元素时,需将从第
i
+
1
i+1
i+1个元素至第
n
n
n个元素依次向前移动一个位置。
Status ListDelete_Sq(SqList &L, int i,ElemType &e){
//在顺序线性表L中删除第i个元素,并用e返回其值
if(i<1||i>L.length)
return false;
e=L.data[i-1];
for(int j=i;j<L.length;j++)
L.data[j-1]=L.data[j];
L.length--;
return true;
}
最好情况:在删除表尾 ( i = n ) (i=n) (i=n),无元素移动,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:在删除表头 ( i = 1 ) (i=1) (i=1),元素移动语句执行 n − 1 n-1 n−1次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)
查找数据元素:(顺序查找)
在顺序表
L
L
L中查找第一个元素值等于e的元素,并返回其位序。
int LocateElem(SqList L,ElemType e){
int i;
for(i=0;i<L.length;i++){
if(L.data[i]==e)
return i+1; //下标为i的元素值等于e,返回其位序i+1
}
return 0; //退出循环,说明查找失败
}
最好情况:查找的元素在表头,仅此比较一次,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:查找的元素在表尾(或不存在时),需要比较n次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)
线性表的链式表示和实现
单链表
- 定义
指通过一组任意的存储单元来 存储线性表中的 数据元素。
//线性表的单链表存储结构
typedef struct LNode{ //定义单链表结点类型
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
上述的表达等同于:
typedef struct LNode{
ElemType data;
struct LNode *next;
}
typedef struct LNode LNode;
typedef struct LNode *LinkList;
返回带有头结点的单链表第 i i i个值的:
Status GetElem(LinkList L,int i,ElemType &e){
//L为带有头结点的单链表的头指针
//当第i个存在时,其值赋给e并返回OK,否则返回ERROR
p=L->next;j=1; //初始化,p为第一个结点,j为计数器
while(p&&j<i){ //顺时针查找,直到p指向第i个元素,或者为空
p=p->next;++j;
}
if(!p||j>i) return ERROR;
e=p->data;
return OK;
}
头结点:
定义:在单链表第一个节点之前附加一个节点,称为头结点。
引入后的优点:
1)第一个数据结点的位置被存放在头结点的指针域中,因此在链表中所有系欸但的操作都是一致的,无需进行特殊处理。
2)无论链表是否空,其头指针都指向头结点的非空指针,因此空表和非空表的处理也是一致的。
- 基本操作
初始化一个单链表(带头结点)
bool InitList(LinkList &L){
L=(LNode*)malloc(sizeof(LNode)); //分配头结点
if(L==NULL) //内存不足,分配失败
return false;
L->next=NULL; //头结点之后,暂时还没有节点
return true;
}
判断是否为空:
bool Empty(LinkList L){
if(L->next==NULL)
return true;
else
return false;
}
初始化一个单链表(不带头结点)
bool InitList(LinkList &L){
L=NULL; //空表,暂时还没有节点(防止脏数据)
return true;
}
判断是否为空:
bool Empty(LinkList L){
if(L==NULL)
return true;
else
return false;
}
bool Empty(LinkList L){
return(L==NULL);
}
插入结点(带头结点)
Status ListInsert(LinkList &L,int i,ElemType e){
//带头结点的单链线性表L中第i个位置之前插入元素e
p=L;j=0;
while(p&&j<i-1){
p=p->next;
++j;
} //寻找第i-1个结点
if(!p||j>i-1) //i小于1或者大于表长加1
return ERROR;
s=(LinkList)malloc(sizeof(LNode)); //生成新节点
s->data=e;s->next=p->next; p->next=s; //插入L中
return OK;
}
删除结点(带头结点)
Status ListDelete(LinkList &L,int i,ElemType &e){
//带头结点的单链线性表L中,删除第i个元素,并由e返回其值
p=L;j=0;
while(p->next&&j<i-1){ //寻找第i个结点,并令p指向其前驱
p=p->next;
++j;
}
if(!(p->next)||j>i-1) //删除位置不合适
return ERROR;
q=p->next;p->next=q->next; //删除并释放结点
e=q->data;free(q);
return OK;
}
复杂度为 O ( n ) O(n) O(n),因为必须找到第 i − 1 i-1 i−1个结点,在删除或插入第 i i i个结点之前。
头插法建立单链表
void CreateListHead(LinkList &L,int n){
//逆序位输入n个元素的值,建立带表头结点的单链线性表L
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL; //先建立一个带头结点的单链线性表
for(i=n;i>0;--i){
p=(LinkList)malloc(sizeof(LNode)); //生成新的结点
scanf(&p->data); //输入元素值
p->next=L->next;L->next=p; //插入到表头
}
}
尾插法建立单链表
void CreateListTail(LinkList &L){
//正向建立单链线性表
L=(LinkList)malloc(sizeof(LNode));
LNode *s,*r=L; //r为表尾指针
scanf(L->data); //输入结点的值
while((L->data)!=9999){
s=(LNode*)malloc(sizeof(LNode)); //生成新的结点
s->data=L->data;r->next=s;
r=s; //r指向新的表尾结点
scanf(L->data); //输入结点的值
}
r->next=NULL; //尾结点指针置空
return L;
}
求表长
定义:计算单链表中数据结点(不含头结点)的个数。每访问一个结点,计数器加1,直到访问到空结点为止。
双链表
结点定义:
typedef struct DNode{ //定义双链表的结点类型
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode)); //分配一个头结点
if(L==NULL) //内存不足,分配失败
return false;
L->prior=NULL;
L->next=NULL;
return true;
}
双链表的插入
//在p结点后插入s结点
bool InsertNextDNode(DNode *p,DNode *s){
if(p==NULL||s==NULL)
return false;
s->next=p->next; //1
if(p->next!=NULL){ //如果p结点有后继结点
p->next->prior=p; //2
}
s->prior=p; //3
p->next=s; //4
return true;
}
第1、2步必须在第4步之前!!!
双链表的删除
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p){
if(p==NULL)
return false;
DNode *q=p->next; //找到p的后继结点q
if(q==NULL)
return false; //p没有后继结点
p->next=q->next;
if(q->next!=NULL) //q结点不是最后一个结点
q->next->prior=p;
free(q);
return true;
}
void DestoryList(DLinklist &L){
//循环释放各个数据结点
while(L->next!=NULL)
DeleteNextDNode(L);
free(L); //释放头结点
L=NULL; //头指针指向NULL
}
双链表的遍历
//后向遍历
while(p!=NULL){
p=p->next;
}
//前向遍历
while(p!=NULL){
p=p->prior;
}
//前向遍历(跳过头结点)
while(p->prior!=NULL){
p=p->prior;
}
循环链表
循环单链表
判断是否是空的条件:头结点的指针是否等于头指针。
循环单链表通常仅设尾指针,从而使得效率更高。因为设r是尾指针,那么 r − > n e x t r->next r−>next即为头指针,对表头和表尾都仅需要 O ( 1 ) O(1) O(1)的时间复杂度。
循环双链表
//初始为空的循环双链表
bool InitDLinkList(Dlinklist &L){
L=(DnOde *)malloc(sizeof(DNode));
if(L==NULL)
return false;
L->prior=p;
L->next=p;
return true;
}
//判断循环双链表是否为空
bool Empty(DLinlist L){
if(L->next==L)
return true;
else
return flase;
}
//判断结点p是否为循环双链表的表尾结点
bool isTail(DLinkList L,DNode *p){
if(p->next==L)
return true;
else
return false;
}
静态链表
定义:借助数组来描述线性表的链式存储结构。指针域是节点的呃相对位置(数组下标),又称游标。
#define MaxSize 50
typedef struct{
Elemtype data;
int next;
}SLinkList[MaxSize];
当next=-1,作为结束的标志。