第二章 线性表
写在前边的话写代码好比写诗,数据结构就好比唐诗三百首,熟读并默写这是基本功能,所以要闲的没事可以在纸上多写
大纲
- 线性表的定义和基本操作
- 线性表的实现
2.1顺序存储结构
2.2链式存储结构
2.3线性表的应用
2.1 线性表的基本概念与实现
1. 线性表的逻辑特性
只有一个表头元素,只有一个表尾元素,表头元素没有前驱,表尾元素没有后继元素,其它的只有一个前驱和后继元素。
2. 线性表的存储结构
顺寻存储:顺序表
链式存储:链表
2.1顺序表
顺序表就是线性表中所有元素按照其逻辑顺序,依次存储到从指定位置开始的一块连续的存储空间中。
2.2链表
在链表存储中,每个结点不仅包含所存元素本身信息,还包含元素之间的逻辑关系信息,即前驱结点包含后继结点的地址信息。
2.3两种比较
顺序表:随机访问、静态分配、占用连续存储空间
链表:不支持随机访问、动态分配、结点存储空间利用率较顺序表低
顺序表插入一个元素时,需要移动多个元素;链表插入元素时,不需要移动元素。
3. 链表5种形式
3.1单链表
带头结点的 head->next==NULL 链表为空
不带头结点的 head == NULL 链表为空
3.2双链表
3.3循环单链表
带头结点的 head->next==head 链表为空
不带头结点的 head == NULL 链表为空
3.4循环双链表
带头结点的 head->next==head && head->prior==head 链表为空
不带头结点的 head == NULL 链表为空
3.4静态链表
2.2 线性表的基本操作
2.2.1 线性表的定义
具有相同特性的数据元素的一个有限序列。序列中所包含的元素个数叫做线性表的长度。
2.2.2 线性表的结构定义
#define maxSize 100
1、顺序表的结构定义
typedef struct{
int data[maxSize];
int length;
}Sqlist;
简写
int A[maxSize];
int n;
2、单链表结点定义
typedef struct LNode{
int data;
struct LNode * next;
}LNode;
3、双链表结点的定义
typedef struct DLNode{
int data;
struct DLNode * prior;
struct DLNode * next;
}DLNode;
2.2.3 顺序表的算法操作
1、定位 x
int locateElem(Sqlist L,int x){
for(int i=1;i<=L.length;i++)
if(x<L.data[i]){
return i;
}
return i;
}
2、插入元素 x
void insert(Sqlist &L,int x){
int p = locateElem(x);
//移动顺序表
for(int i=L.length;i>=p;i--){
data[i+1] = data[i];
}
L.data[p]=x;
++L.length;
}
3、删除顺序表L中下标为p的元素,并将删除元素赋值给e
int listDelete(Sqlist &L,int p,int &e){
if(p<1||p>L.length) return 0;
e=L.data[p];
for(int i=p;i<=L.length;i++)
L.data[i] = L.data[i+1];
--L.length;
return 1;
}
4、初始化顺序表
void InitList(Sqlist &L){
L.length = 0;
}
5、用e返回L指定位置p上的元素
int GetElem(Sqlist L,int p,int &e){
if(p<1||p>L.length) return 0;
e=L.data[p];
return 1;
}
2.2.4 单链表的算法操作
1、有A、B两个带头结点的递增有序的单链表,将A和B归并为一个非递减有序的单链表C。
void merge(LNode *&A,LNode *&B,LNode *&C){
LNode *p=A->next; //因为A B链表都是带头结点的链表
LNode *q=B->next;
C=A;
C->next=NULL; //表示从A取得的头结点要作为新的头结点
LNode *r=C;
free(B);
while(p!=NULL&&q!=NULL){
if(p->data < q->data){
r->next=p;
p=p->next;
r=r->next;
}
else{
r->next=q;
q=q->next;
r=r->next;
}
}
r->next=NULL; //
if(p!=NULL) r->next=p;
if(q!=NULL) r->next=q;
}
2、使用尾插法创建链表
void CreatListR(LNode *&C,int a[],int n){
LNode *r,*s;
C=(LNode *)malloc(sizeof(LNode));
C->next=NULL;
r=C; //r指向头结点C
for(int i=0;i<n;i++){
s=(LNode *)malloc(sizeof(LNode));
s->data=a[i];
r->next=s;
r=r->next;
}
r->next=NULL;
}
3、使用头插法创建链表
void CreatListF(LNode *&C,int a[],int n){
LNode *s;
C=(LNode *)malloc(sizeof(LNode));
C->next=NULL;
for(int i=0;i<n;i++){
s=(LNode *)malloc(sizeof(LNode));
s->data=a[i];
s->next=C->next;
C->next=s;
}
}
头插法最后得到的链表顺序和原数组顺序是相反的
4、把之前的链表归并成一个递减有序的链表
使用头插法即可实现
void merge(LNode *&A,LNode *&B,LNode *&C){
LNode *p=A->next;
LNode *q=B->next;
LNode *s;
C=A;
C->next=NULL;
free(B);
while(p!=NULL&&q!=NULL){
if(p->data > q->data){
s=p;
p=p->next;
s->next=c->next;
c->next=s; //C作为一个头结点,其指针域始终存着尾点s
}else{
s=q;
q=q->next;
q->next=c->next;
c->next=s;
}
}
while(p!=NULL){
s=p;
p=p->next;
s->next=c->next;
c->next=s;
}
while(q!=NULL){
s=q;
q=q->next;
s->next=c->next;
c->next=s;
}
}
5、插入一个结点
->a->b 插入结点 s
s->next = a->next;
a->next = s;
6、删除一个结点
->a->b
q=a->next; //用q接收b,为的是删除之后将该结点空间释放
a->next=a->next->next;
free(q);
7、查找一个链表C(带头结点)中是否含有值为 x 的元素,如果存在则删除该结点并返回 1
int SearchAndDelete(LNonde *&C, int x){
LNode *p,*q;
p=C; //这里p指向的是C而不是C->next,因为是为了在查找的 时候得到的是要删除结点的前一个结点
//查找开始
while(p->next!=NULL){
if(p->next->data==x){
break;
}
p=p->next; //更新p
}
if(p->next==NULL){
return 0;
}
else{
q=p->next; //q指向了要删除的结点
p->next = p->next->next;
free(q);
return 1;
}
}
2.2.5 双联表的算法操作
1、采用尾插法建立双联表
void CreatDlistR(DLNode *&L,int a[],int n){
DLNode *s,*r;
L=(DLNode *)malloc(sizeof(DLNode));
L->next=NULL;
r=L;
for(int i=0;i<n;i++){
s=(DLNode *)malloc(sizeof(DLNode));
s->data=a[i];
r->next=s;
s->prior=r; //首次只建立了当前结点的 prior 域,下次会建立 next 域
r=s;
}
r->next=NULL;
}
2、查找结点值为 x 的结点,存在返回结点指针,否则返回NULL
DLNode* Search(DLNode *C,int x){
DLNode *p =C->next;
while(p!=NULL){
if(p->data==x){
break;
}
p=p->next;
}
return p; //最后不管是找到还是没找到都返回p,因为如果没找到,那么终止while的时候p也是为NULL
}
3、插入结点算法
p<->q 插入结点 s
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
4、删除 p结点的后继结点 算法
p<->
q=p->next;
p->next = q->next;
q->next->prior = p;
free(q);
2.2.6 循环双联表的算法操作
判断 p 指针沿着循环链表走到表尾的条件是 p->next == head