Chapter 2 线性表和逆置问题
- 2.1 线性表的基本概念和实现
(1)什么叫线性表:线性表是有相同特性数据元素的一个有限序列。
(2)线性表的逻辑特性:只有一个表头元素,只有一个表尾元素,表头元素无前驱,表尾元素无后继;其余元素只有一个直接前驱和直接后继。
(3)线性表的存储结构:顺序存储结构和链式存储结构,分别用于顺序表和链表。
①顺序表:元素按逻辑顺序,依次存储到从指定的存储位置开始的一块连续的存储空间中。
②链表:每个结点不仅包含所存元素的信息,还包含了元素间逻辑关系的信息(next or prior)
③两种存储结构的比较:
顺序表:
a.随机访问特性:顺序表只要知道存储的起始位置就可以知道任意一个结点的位置。
b.要求占用连续的存储空间,存储分配只能预先进行,操作过程中始终不变。
c.插入操作需要移动多个元素。
链表:
a.链表不支持随机访问:必须从头开始访问。所以结点的存储空间利用率比顺序表低。
b.存储空间的动态分配:不需要一次性的划分所有结点的空间给链表。
c.插入操作无需移动元素,而是修改插入位置旁边两个结点的指针域。
(4)5种形式的链表(关于头结点的问题和判断表空的条件,head为头结点)
①单链表:有一个next的指针域指向后继结点。
带头结点的单链表表空条件为:head->next=NULL
不带头结点的单链表表空条件为:head=NULL
②双链表:在单链表上添加一个prior的指针域,指向当前结点的前驱。
表空条件和单链表一样。
③循环单链表:把单链表的最后一个结点的指针域指向第一个结点。
表空条件和单链表一样。
④循环双链表:将双链表的最后一个结点的next指针指向第一个结点且将第一个结点的prior指针指向最后一结点。
带头结点的循环双链表:head->next==head;head->prior=head;或者两者的与和或语句。
不带头结点的单链表表空条件为:head=NULL
⑤静态链表:用一维数组表示。数组中的每一个结点有两个分量,一个是数据data,一个是指向下一个元素地址的指针(这个指针就是一个存下一个元素地址的int型的整型变量不是用来存储内存地址的变量型)
- 2.2 线性表的结构体定义和基本操作
(1)顺序表结构体定义:
# define maxSize 50
typedef struct {
int data[maxSize]; //存放顺序表元素的数组,这里就体现了顺序表大小需要预定义
int length; //表示顺序表长度
}Sqlist; //顺序表类型
(2)单链表结构体定义:
typedef struct LNode {
//结构体名称
int data; //存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
(3)双链表结构体定义
typedef struct DLNode {
int data;
struct DLNode *next;
struct DLNode *prior;
} DLNode;
(4)定义一个结点:定义一个名字为A的指针指向这个结点
LNode *A = (LNode*)malloc(sizeof(LNode));
(5)顺序表的操作:(简单-略)
(6)单链表的操作:
- 插入操作:
例题2-1 A和B是两个带头结点的单链表,其中元素递增有序。设计一个算法,合并A和B为C,使C中元素值非递减有序。
void merge(LNode *A, LNode *B, LNode *&C) {
LNode *p = A->next //p为A的指针
LNode *q = B->next //q为B的指针
LNode *r; //r为C的指针
C=(LNode *)malloc(sizeof(LNode)); //申请C的头结点空间,或者用C=A(用A的头结点做C的头结点)
C->next=NULL;
free(B);
free(A);
r=C; //r指向C的头结点
while(p!=NULL && q!=NULL){
//如果A和B都还有结点进行比较,选二者中较小的结点与C相连,从该循环跳出来至少必有一个线性表元素已经全部插入完毕。
if(p->data <= q->data) {
r->next=p; //p指针在A表中所在的结点与C项链
p=p->next; //p指针后推
r=r->next; //r指针后推,指向C表新加入的结点中
}
else {
r->next=q;
q=q->next;
r=r->next;
}
}
r->next=NULL;
if(p!=NULL) r->next=p; //B中结点已经全加入C中,则直接把A中剩余结点加入C中
if(q!=NULL) r->next=q;
}
例题2-2:假设有n个元素已经存储在数组a中,用尾插法和头插法建立链表C。(重点)
①尾插法:
void createlistR(LNode *&C, int A[], int n){
LNode *s, *r; //s用来指向新申请的结点,r用来指向C的终端端点
int i;
C=(LNode *)malloc(sizeof(LNode));
C->next=NULL;
r=C;
for(i=0;i<n;i++){
s