第二章 线性表
循环双链表
以下四句代码任意一个为真,都可以说明循环双链表
为空
head->next==head;
head->prior==head;
head->next==head&&head->prior==head;
head->next==head||head->prior==head;
4种链表用4种道路来形象的比喻:
静态链表
顺序表插入和删除的时间复杂度计算过程
代码
#define maxSize 100 //定义一个整型常量
LNode *a=(LNode *)malloc(sizeof(LNode)); //分配内存空间
注:这里a包含了两个东西:结点和指向这个结点的指针。a既是指针名又是结点名
2.2顺序表操作
初始化顺序表
void init(Sqlist &L){
L.length=0;
}
结构体定义
typedef struct{
int data[maxSize]; //存放元素的数组
int length; //存放元素个数(顺序表长度)
}Sqlist; //定义顺序表类型
考试中用的多的
int a[maxSize]; //存放元素的数组
int n;
插入元素:查找位置,插入
查找算法:找第一个等于e的元素的位置
int find(Sqlist L,int e){
int i;
for(i=0;i<L.length;i++){
if(e==L.data[i])
return i; //找到
}
return -1; //没找到,失败标记
}
插入数据元素算法:在p的位置(等同于上面找到的i)上插入e;p的位置存在就插入,不存在返回0
int insert(Sqlist &L,int p,int e){ //L会发生变化,所以要用引用型
int i;
//p不对或没位置
if(p<0||p>L.length||L.length==maxSize)
return 0;
for(i=L.length-1;i>=p;--i)
L.data[i+1]=L.data[i];//从后往前往后移动
L.data[p]=e;
++(L.length); //加表长
return 1;
}
删除元素
int delete(Sqlist &L,int p,int &e){//删除的结点元素也会改变
int i;
if(p<0||p>L.length-1)
return 0;
e=L.data[p]; //删除的p位置上的元素赋给e
for(i=p;i<L.length-1;++i)
L.data[i]=L.data[i+1];
--(L.length);
return 1;
}
2.3单链表操作(带头结点)
typedef struct LNode{
int data;
struct LNode *next; //结构体要定义指针的话上面就得加一个名称,像LNode一样
}LNode;
2.4双链表操作
typedef struct LNode{
int data;
struct LNode *prior;
struct LNode *next;
}LNode;
查找结点
DLNode* find(DLNode *c,int x){
DLNode *p=c-next;
while(p!=NULL){
if(p-data==x)
break;
p=p->next;
}
return p; //找到,p中的内容是结点地址
}
插入结点(p后插入s)
s->next=p->next;
s->prior=p;
p-next=s;
s->next->prior=s; //p如果指向最后一个结点,这行可以去掉
删除结点p的后继结点
q=p->next;
p->next=q->next;
q->next->prior=p;
free(q);
真题演练
1.一个顺序表数组A[],元素存储在下标0到m+n-1的范围,前m个元素递增有序,后n个元素也递增有序,设计一个算法,使得整个顺序表有序。
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,并在关键之处给出注释。
(3)说明你所设计的算法的时间复杂度和空间复杂度。
(1)算法的基本设计思想
1.将数组A[]中的m+n个元素(假设元素为int型)看成两个顺序表:表L(由A[]中前m个元素构成),表R(由A[]中后n 个元素构成)。
2.将数组当前状态看作起始状态,将表R中的元素逐个插入表L中的合适位置即可。
3.插入过程:取表R中的第一个元素A[m]存入 辅助变量temp 中,让 temp逐个与A[m-1],…,A[0]进行比较,当temp<A[j](0≤j≤m-1)时,
将A[j]后移一位,temp>A[j](0≤j≤m-1)时,将temp存入A[j+1]中。重复上述过程,继续插入A[m+1],A[m+2],·…,A[m+n-1],最终A[]中元素整体有序。
(2)
void insert(int A[],int m,int n){
int i,j;
int temp;
for(i=m;i<m+n;++i){ //R中的元素插入到L中
temp=A[i];
for(j=i-1;j>=0&&temp<A[j];--j)
A[j+1]=A[j]; //L中的元素后移,给temp腾位置
A[j+1]=temp;
}
}
(3)算法的时间和空间复杂度
1.本题的规模由m和n共同决定。取最内层循环中A[j+1]=A[j];这一句作为基本操作,其执行次数在最坏的情况下
为R中的每个元素都小于L中的所有元素;又因R中元素递增有序,所以对于每个R中的元素,要将其插入正确位置
都必须进行m次移动,R中共有n个元素,因此有:
f(m,n)=mn
由此可见,本算法的时间复杂度为O(mn)。
2.算法所需额外存储空间与数据规模m和n无关,变化属于常量级,因此空间复杂度为0(1)。
2.递增有序的单链表A、B(元素个数分别为m、n,且都带有头结点)分别存储了一个集合,请设计算法,求出A和B的差集A-B(仅由在A中出现而不在B中出现的元素所构成的集合)。将差集保存在单链表A中,并保持元素的递增有序性。
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,并在关键之处给出注释。
(3)说明你所设计的算法的时间复杂度。
(1)只需从A中删去A与B中共有的元素即可。由于两个链表中的元素是递增有序的,因此可以这么做:设置两个指针p、q开始时分别指向A和B的开始结点。
循环进行以下判断和操作:如果p所指结点的值小于q所指结点的值,则p后移一位看下一位元素的大小;如果q所指结点的值小于p所指结点的值(说明A
中已经有这一位元素了),则q后移一位看下一位元素的大小;如果两者所指结点的值相同,则删除p所指结点。最后,p与q任一指针为NULL时算法结束。
(2)
void difference(LNode *A,LNode *B){
LNode *p=A->next,*q=B->next;//pq都是指向开始结点的指针
LNode *pre=A; //pre为A中p所指结点的前驱结点的指针,pre应该是指向头指针的
LNode *r;
while(p!=NULL&&q!=NULL){
if(p->data<q->data){
pre=p;
p=p->next;
}
else if(p->data>q->data)
q=q->next;
else{ //相等的情况,删除相同的结点
//删除p
pre->next=p->next; //pre->next是头指针的下一个结点是开始结点更新为p->next
r=p;//要删除的结点放在了r
p=p->next;//向后移动指针看下一个结点的大小
free(r); //释放结点空间
}
}
}
(3)算法的时间复杂度分析
由算法描述可知,算法规模由m和n共同确定。算法中有一个单层循环,循环内的所有操作都是常数级的,因此可以用循环执行的次数作为基本操作执行的次数。可见循环执行的次数即为p、q两指针沿着各自链表移动的次数,考虑最坏的情况,即p、q都走完了自己所在的链表,循环执行 m+n次,因此时间复杂度为O(m+n)。