目录
2、元素插入:O(ListLength(L)) → O(n)
5、合并“元素赋值”:O(La.length+Lb.length)
一、线性结构定义
线性结构的特点:
- 存在一个唯一的被称为“第一个”的数据元素
- 存在一个唯一的被称为“最后一个”的数据元素
- 除第一个元素外,每个元素均有唯一一个直接前驱 (Predecessor)
- 除最后一个元素外,每个元素均有唯一一个直接后继 (Successor)
线性表linear list:
定义:
由n(n≧0)个数据元素(结点)a
1
,a
2
, …a
n
组成的有限序列,且该序列中的所有结点具有相同的据类型
- 线性表长度:数据元素的个数n
- n=0:空表
- n>0:记作(a1,a2,…an)
- a1称为线性表的第一个(首)结点
- an称为线性表的最后一个(尾)结点
- a1,a2,…ai-1都是ai (2≦i≦n)的前驱,其中ai-1是ai的直接前驱
- ai+1,ai+2,…an都是ai (1≦ i ≦n-1)的后继,其中ai+1是ai的直接后继
- 若线性表中的结点是按值(或按关键字值)由小到大(或由大到小)排列的,称线性表是有序的
逻辑结构:
线性表中的结点:
- 单值元素
- 每个元素只有一个数据项
- 记录型元素
- 每个记录含有多个数据项,每个项称为结点的一个域
-
每个元素有一个可以唯一标识每个结点的数据项组,称为关键字
-
ADT定义:
ADT List{数据对象 :D = { a i | a i ∈ElemSet, i=1,2,…,n, n≧0 }数据关系 :R = {<a i-1 , a i > | a i-1 , a i ∈D, i=2,3,…,n }基本操作 :InitList( &L ) //初始化操作结果:构造一个空的线性表LDestroyList(&L) //销毁操作初始条件:线性表L已存在操作结果:销毁线性表LListEmpty(L) //线性表判空初始条件:线性表L已存在操作结果:若L为空表,则返回TRUE,否则返回FALSEListLength( L ) //求线性表长度初始条件:线性表L已存在操作结果:返回L中数据元素个数GetElem( L, i, &e ) //求某个数据元素初始条件:线性表L已存在,1≦i≦ListLength(L)操作结果:用e返回L中第i个数据元素的值LocateElem(L, e, compare) //定位数据元素初始条件:线性表L已存在, compare() 是数据元素判定函数操作结果:返回L中第一个与e满足关系compare()的数据元素的位置,若这样的数据元 素不存在,则返回值为0PriorElem(L, cur_e, &pre_e) //求前驱初始条件:线性表L已存在操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操 作失败,pre_e无定义NextElem(L, cur_e, &next_e) //求后继初始条件:线性表L已存在操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则 操作失败,next_e无定义ListTraverse(L, visit) //遍历线性表初始条件:线性表L已存在操作结果:依次对L的每个数据元素调用函数 visit() 。一旦visit()失败,则操作失败ClearList(&L) //将表置空初始条件:线性表L已存在操作结果:将L重置为空表ListInsert ( &L, i, e ) //插入数据元素初始条件:线性表L已存在,1≦i≦ListLength(L) +1;操作结果:在线性表L中的第i个位置之前插入元素e,L的长度加1;ListDelete(&L,i,&e) //删除数据元素初始条件:线性表L已存在且非空,1≦i≦ListLength(L)操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1} ADT List
ADT应用:
1、集合合并A=AUB
• 依次查看线性表 LB的每个数据元素GetElem(LB, i, &e)• 根据元素值在线性表 LA 中进行查找LocateElem(LA, e, equal)• 若不存在,则插入之ListInsert(&LA, n+1, e), n为LA的当前长度
void union(List &La, List Lb){
int La_len, Lb_len, i;
ElemType e;
La_len = ListLength(La);
Lb_len = ListLength(Lb);
for(i=1; i<=Lb_len; i++){
GetElem(Lb, i, &e);
if(!LocateElem(La, e, equeal)
ListInsert(La, ++La_len, e);
}
}
2、有序列表合并
功能:合并两有序列表(其值按非递减顺序排列)成一新有序列表1. 初始化 Lc 为空表;2. 分别从 La和Lb中取得当前元素 ai 和 b j ;3. (La、Lb不空)若 a i ≤b j ,则将 a i 插入到 Lc 中 ,否则,将bj 插入到 Lc中;4. 重复 2 和 3 两步,直至La或Lb中的元素被取完为止;5. 将La表或Lb表中剩余元素复制插入到Lc表中。
void MergeList(List La, List Lb, List &Lc){
int La_len, Lb_len, i=1, j=1, k=0;
ElemType ai, bj;
InitList(Lc);
La_len = ListLength(La);
Lb_len = ListLength(Lb);
//La Lb均非空
while( (i<=La_len) && (j<=Lb_len) ){
GetElem(La, i, &ai);
GetElem(Lb, j, &bj);
if (ai<=bj)
{ListInsert(Lc, ++k, ai); ++i;}
else
{ListInsert(Lc, ++k, bj); ++j;}
}
//La非空
while(i<=La_len){
GetElem(La, i++, &ai);//先用后加
ListInsert(Lc, ++k, ai);//先加后用
}
//Lb非空
while(j<=Lb_len){
GetElem(Lb, j++, &bj);//先用后加
ListInsert(Lc, ++k, bj);//先加后用
}
}
3、比较
•
前提条件
• 假如 GetElem 和 ListInsert 这两个操作的执行时间和表长无关
• LocateElem 的执行时间和表长成正比
•
时间复杂度
• 算法1:
O( ListLength(La) × ListLength(Lb) )
• 算法2:
O( ListLength(La) + ListLength(Lb) )
二、线性表的顺序表示和实现
表示
- 用一组地址连续的存储单元依次存储线性表的数据元素
-
设有非空的线性表 (a 1 , a 2 , …a n ) ,而 每个元素需占用 X 个存储单元, 则:
-
线性表中第 i+1个数据元素的存储位置LOC(ai+1)和第 i个数据元素的存储位置LOC(ai)之间满足下列关系: LOC(a i+1 )=LOC(a i )+X
-
线性表的第 i 个数据元素 ai的存储位置为:LOC(a i )= LOC(a 1 ) +(i-1)* X
-
实现
线性表的顺序实现:用动态分配的一维数组
#define LIST_INIT_SIZE 100 //线性表初始大小
#define LISTINCREMENT 10 //线性表增量大小
typedef struct{
ElemType *elem; //基地址
int length; //当前长度
int listsize; //当前分配的存储容量,以sizeof(ElemType)为单位
}SqList;
1、初始化:O(1)
Status InitList_Sq( SqList &L){
//构造一个空的、容量为LIST_INIT_SIZE的线性表L
L.elem=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));
//指向的第一个元素
if (!L.elem ) exit(OVERFLOW); //存储分配失败
L.length = 0; //空表长度为0
L.listsize = LIST_INIT_SIZE; //初始存储容量
return OK;
}
• malloc 是用于分配指定size的内存的库函数
• 将L.elem这个指针指向一块通过malloc函数分配的内存的地址
• 强制转换成ElemType类型的指针
2、元素插入:O(ListLength(L)) → O(n)
在第i个元素之前插入元素e
Status ListInsert_Sq( SqList &L, int i, ElemType e){
//i值不合法
if(i<1 || i>L.length+1) return ERROR;
//当前存储空间已满,增加容量
if(L.length>=L.listsize){
ElemType *newbase=(ElemType *) realloc(L.elem(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if (!newbase) return ERROR; // 存储分配失败
L.elem = newbase; //新的基地址
L.listsize += LISTINCREMENT; //新的存储容量
}
ElemType *p;
ElemType *q = &(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;--p){
*(p+1)=*p;
}
*q=e;
++L.length;
return OK;
}
realloc函数用于修改一个原先已经分配的内存块的大小,可以使一块内存扩大或缩小。
3、元素删除:O(n)
删除线性表的第i个元素
Status ListDelete_Sq(Sq_List &L, int i, ElemType &e){
ElemType *p, *q;
if ((i<1) || (i>L.length)) return ERROR; // i值不合法
p = &(L.elem[i-1]);// p为被删除元素的位置(下标从0开始)
e = *p;// 被删除元素的值赋给e
q = L.elem+L.length-1;// q为表尾元素的位置
for (++p;p<=q;++p)
*(p-1)=*p;
--L.length;
return OK;
}
比较插入和删除
- 在顺序存储结构的线性表中插入或删除元素
- 时间主要耗费在移动元素上 O(n)
- 移动元素的个数取决于插入或删除元素的位置
4、查找元素:O(L.length)
#define LESS -1
#define GREATER 1
Status (*compare)(ElemType a, ElemType b)
{
if (a<b) return LESS;
if (a>b) return GREATER;
return 0; //相等
}
int LocateElem_Sq(SqList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
int i;
ElemType *p;
i=1; // i的初值为第1个元素的位序 (其实只是一个计数)
p=L.elem; // p的初值为第1个元素的存储位置
while (i <= L.length && !(*compare)(*p++, e)) ++i;
// p先取后加,从第一个开始比较
// 相等时跳出循环,即0
if (i <= L.length) return i;
else return 0;
}
5、合并“元素赋值”:O(La.length+Lb.length)
void MergeList_Sq(SqList La, SqList Lb, SqList &Lc){
ElemType *pa, *pb, *pc, *pa_last, *pb_last;
pa = La.elem; pb = Lb,elem;
Lc.listsize = Lc.length = La.length+Lb.length;
pc = Lc.elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));
if (!Lc.elem) exit(OVERFLOW);// 存储分配失败
pa_last=La.elem+La.length-1;
pb_last=Lb.elem+Lb.length-1;
while(pa<=pa_last && pb<=pb_last){
if(*pa<=*pb) *pc++=*pa++; // 按值非递减插入
else *pc++=*pb++;
while(pa<=pa_last) *pc++=*pa++;
while(pb<=pb_last) *pc++=*pb++;
}
三、线性表的链式表示和实现
表示
-
指用一组 任意的 (连续的或不连续的)存储单元存储线性表中的数据元素
-
存储方式
- 在存储每个元素值的同时,存储指示其直接后继的地址,称为指针(pointer)或链(link),这两部分组成了链表中的结点
- 指针建立了数据元素之间的逻辑关系
- 每个结点由两个域组成:
- 数据域data:存放结点的值
- 指针域next:存放结点的直接后继的地址
链式存储
- 链表是通过每个结点的指针域将线性表的n个结点按其逻辑次序链接在一起的
- 线性链表/单链表:每一个结点每一个结点只包含一个指向直接后继的指针域
- 基于C指针实现的单链
- 基于C数组实现的单链表/静态链表
- 双向链表:每一个结点包含两个指针域,其一指向直接后继,另一指向直接前驱
- 循环链表:整个链表的指针域链接成一个环
- 双向循环链表:将头结点和尾结点链接起来的双向链表
四、单链表
- 为操作方便,总是在链表的第一个结点(首元结点)之前附设一个头结点
- 头结点的数据域可以不存储任何信息(或存储链表长度等信息)
- 头结点的指针域存储指向第一个结点的指针(即第一个结点的存储位置)
(1)C指针实现
-
结点的类型定义
typedef struct LNode{
ElemType data; /*数据域,保存结点的值 */
struct LNode *next; /*指针域*/
}LNode, *LinkList; /*结点的类型 */
-
结点的赋值
LNode *p;
p=(LNode*)malloc(sizeof(LNode));
p->data=20;p->next=NULL;
-
基本操作的实现
//取第i个元素
GetElem(L, i, &e)
//在第i个元素之前插入元素e
ListInsert(&L, i, e)
//删除第i个元素
ListDelete_L(&L, i, &e)
//生成n个元素的链表
CreateList(&L,n)
//两有序表合并成一新有序表
MergeList(La, Lb, &Lc)
链表不是随机存取 (random access)结构数组是一块物理上相连的存储空间,只要知道该数组的首元素的地址,之后的元素在此基础上加位置号,就可以找到想要的元素,这就是随机存取。即:不需要先找a,再找b,最后找到c; 而是直接利用“数组开始位置+2 ”就是c元素。
1、单链表的查找:O(n)
Status GetElem_L(LinkList L, int i, ElemType &e){
// L为带头结点的单链表的头指针
// 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
// 初始化,p指向第一个结点,j为计数器
LinkList p; p=L->next; int j=1;
while(p && j<i){
//顺指针向后查找,直到p指向第i个元素或p为空
p=p->next;
++j;
}
if(!p || j<i) return ERROR;
e=p->data;
return OK;
}
2、单链表的插入:O(n)
算法思想 :
- 将值为e的新结点插入到表的第i个结点位置上
- ①首先找到第i-1个结点p
- ②然后生成一个数据域为e的新结点s
- ③并将s结点作为p的直接后继结点
- 解决了顺序表的插入操作需要移动大量元素的问题
Status ListInsert_L(LinkList &L, int i, ElemType e){
//在带头结点的单链表L的第i个元素之前插入元素e
LinkList p,s;
p=L; //头结点
int j=0;
while(p && j<i-1){ // 寻找第i-1个结点
p=p->next;
++j;
}
// 第一次,p为(第一个)首结点,j=1
// 跳出循环时,j=i-1,p为第i-1个结点
// i小于1(导致j>i-1成立)或者大于表长(导致p为NULL)
if (!p || j > i-1) return ERROR;
s=(LinkList*)malloc(sizeof(LNode));//生成新结点
s->data=e;
s-next=p->next;
p->next=s;
return OK;
}
3、单链表结点的删除:O(n)
• 算法思想:
- 为了删除第i个结点ai ,必须找到结点的存储地址
- 该存储地址是在其直接前趋结点ai-1的next域中
- ①首先找到ai-1的存储位置p
- ②然后令p–>next指向ai 的直接后继结点,即把ai 从链上摘下
- ③最后释放结点ai 的空间
• 解决了顺序表的删除操作需要移动大量元素的问题
• 删去第i个结点仅当1≦i≦n时是合法的。
• 当i=n+1时,虽然被删结点不存在但其前趋结点却存在,是终结点,故判断条件之一是p–>next!=NULL,否则会删掉终结点
Status ListDelete_L(LinkList &L, int i, ElemType &e){
//在带头结点的单链表L中删除第i个元素,并由e返回其值
LinkList p,q;
p=L;
int j=0;
while (p->next && j<i-1){
// 寻找第i个结点,并令p指向其前趋
p = p->next;
++j;
}
// 初始状态,p为头结点,p->next非空,j=0
// 跳出循环时,j=i-1,p为第i-1个结点
if (!(p->next) || j>i-1) return ERROR;
//即要删除的结点i非空,且i的值合法
q=p->next;
p->next=q->next;
// 删除并释放结点
e=q->data;
free(q);
return OK;
}
4、创建单链表:O(ListLength(L))
- 逆序输入 n 个数据元素的值,建立带有头结点的单链表
- 具体操作:
- 建立一个空表
- 输入数据元素an,建立结点并插入
- 输入数据元素an-1,建立结点并插入
- 依次类推,直至输入a1为止
void CreateList_L(LinkList &L, int n){
//逆序输入随机生成的n个元素的值
//建立带表头结点的单链表L
LinkList p; int i;
//先建立一个带头结点的空单链表
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
for(i=n;i>0;--i){
p=(LinkList)malloc(sizeof(LNode));
p->data=random(200);
p->next=L->next;
L->next=p;
}
}
5、单链表的合并:O(m+n)
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc){
//合并 两非递减单链表La和Lb,成新的非递减单链表Lc
LinkList pa,pb,pc;
//pa, pb为两个链表的当前结点,pc为合并后链表的最后一个结点
pa=La->next;
pb=Lb->next;
Lc=pc=La;// 用La的头结点作为Lc的头结点
while(pa && pb){
if(pa->data<=pb->data){
pc->next=pa;
pc=pa;//更新pc
pa=pa->next;//更新pa
}
else{
pc->next=pb;
pc=pb;//更新pc
pa=pa->next;//更新pa
}
}
pc-next=pa?pa:pb;
free(Lb);// 释放Lb的头结点
}
6、应用:一元多项式
(2)C数组实现:静态链表
- 数组的一个分量表示一个结点
- 用游标(指示器cur)代替指针指示结点在数组中的相对位置
- 数组的第零分量可看成是头结点,其指针域指示链表的第一个节点
- 需要预先分配一个较大空间,但在插入和删除时不需要移动元素,仅需修改指针
- 保持了链式存储结构的主要优点
定义
#define MAXSIZE 1000
typedef struct SLinked{
ElemType data;
int cur;
}SLinkedList[MAXSIZE];
SLinkedList s;
-
在该存储数组中,可以包含一个空闲链表和多个静态链表
-
用(s,head)表示在s中存储的以head为起始位置的静态链表
基本操作的实现
//1. 静态链表的初始化:在存储数组中建立空闲链表
void InitList(SLinkedList space);
//2. 创建一个含有n个结点的静态链表,返回表头在存储数组的位置
int CreateList(SLinkedList space,int n);
//3. 在以head为表头的静态链表中,在第i个结点之前插入一个值为x
的新结点
int Insert(SLinkedList space, int head, int i, ElemType x);
//4. 在以head为表头的静态链表中,删除第i个结点
int Delete(SLinkedList space,int head,int i, ElemType *e);
//5. 在以head为表头的静态链表中,确定第一个值为x的结点的位置
int Locate(SLinkedList space, int head,ElemType x);
1、初始化
//将一维数组space中各分量链成一个空闲链表
//space[0].cur为空闲链表的头指针,0表示空指针
void InitList(SLinkedList space){
for(int i=0; i<MAXSIZE-1; i++)
space[i].cur = i+1;
space[MAXSIZE-1].cur = 0;
}
2、创建一个静态链表
//创建一个含有n个结点的静态链表,返回表头在存储数组的位置
int CreateList(SLinkedList space,int n){
int head, k, s, i;
k = MallocNode(space); //从空闲链表中取得一个空结点
head = k;
for (i = 1; i <= n; i++){
s = AllocNode (space);
scanf ("%d", &space[s].data);
space[k].cur = s;
k = s;
}
space[k].cur = 0;
return head;
}
3、分配和释放
// 若空闲链表非空,则返回分配的结点下标,否则返回0
int MallocNode(SLinkedList space){
int i;
i = space[0].cur;
if (i == 0) return 0;
space[0].cur = space[i].cur; //space中少了 i 这个结点
return i;
}
// 将下标为 i 的空闲结点回收到备用链表
void FreeNode(SLinkedList space, int i){
space[i].cur = space[0].cur; //先让space[i]指向space[0]的下一个
space[0].cur = i; //再让space[0]指向space[i]
} //相当于插入space[i]
4、插入结点
//在以head为表头的静态链表中,在第i个结点之前插入一个值为x的新结点
int Insert(SLinkedList space,int head,int i,ElemType x){
int j,k,m;
if(i<1) return 0;
k=head;
j=0;
while (k!=0 && j<i-1){ //查找第i-1个结点
j++;
k=space[k].cur;
}
if(k==0) return 0;
m=MallocNode(space); //从空闲链表中获取结点,m为该结点下标
if (m!=0){
space[m].data=x;
space[m].cur=space[k].cur; //原来k后面的链接到m上
space[k].cur=m; //m放在第i-1个结点后,即第i个结点上
return 1;
}
else return 0;
}
5、删除结点
//在以head为表头的静态链表中,删除第i个结点
int Delete(SLinkedList space,int head,int i, ElemType *e){
int j,k,m;
if(i<1) return 0;
k=head;
j=0;
while (k!=0 && j <i-1){ //查找第i-1个结点
j++;
k=space[k].cur;
}
if(k==0) return 0;
m=space[k].cur; //第i个结点
space[k].cur=space[m].cur; //第i个后面的结点
*e=space[m].data; //将取出的结点值存在e中
FreeNode(space,m); //释放
return 1;
}
6、查找结点
// 在以head为表头的静态链表中,确定第1个值为x的结点的位置
// 若找到,则返回它在存储数组中的位置,否则返回0
int Locate(SLinkedList space, int head,ElemType x){
int k;
k=space[head].cur; // k指示静态链表中的第一个结点
while(k!=0 && space[k].data!=x)
k=space[k].cur; //顺链查找,如果没找到也会返回0
return k;
}
7、应用:集合合并(只保留不相同的元素)
算法思想 :
建立表示集合A的静态链表S 在输入集合B的元素时,查找S表 若存在和B相同的元素,则从S表中删除掉; 否则将此元素插入S表
void MergeAB(SLinkedList A, int ha, SLinkedList B, int hb){
int i,j,k,m;
ElemType x;
j=B[hb].cur;
while(j!=0){ // 对hb中的每个结点,进行下面的步骤
x=B[j].data;
i=Locate(A,ha,x); //链表ha中查找有无hb中的元素
if(i==0) Insert(A,ha,1,x); //将该元素插入ha,成为ha的第一个元素
else { // ha有hb中的元素,则将该元素从ha中删除
m=0;
k=ha;
while(k!=i){m++;k=A[k].cur;}
Delete(A,ha,m,&x);
}
j=B[j].cur;
}
}
五、双向链表Double Linked List
定义:
- 双向链表:构成链表的每个结点中设立两个指针域
- 一个指向其直接前趋的指针域prior
- 一个指向其直接后继的指针域next
typedef struct node{
ElemType data;
struct node *prior, *next;
}DoubleLinkedList;
- 特性:DoubleLinkedList p;
- p->piror->next=p=p->next->prior
基本操作的实现:
//1. 创建长度为n的双向链表
DoubleLinkedList *CreateDoubleLinkedList(int n);
//2. 在双向链表中查找第1个值为e的结点
DoubleLinkedList *Locate(DoubleLinkedList *head, ElemType e);
//3. 在双向链表中查找第i个结点,返回指向该结点的指针
DoubleLinkedList *GetElemP(DoubleLinkedList *head,int i);
//4.在双向链表第i个结点之前插入元素e
int InsertElem(DoubleLinkedList *head,int i,ElemType e);
//5. 在双向链表中删除第i个结点,并返回结点的元素值
int DeleteNode(DoubleLinkedList *head,int i,ElemType *e);
1、创建
//长度为n
DoubleLinkedList *CreateDoubleLinkedList(int n){
DoubleLinkedList *head, *p, *s, int i;
//创建头结点
p=head=(DoubleLinkedList *)malloc(sizeof(DoubleLinkedList));
for(i=1;i<=n;i++){
s=(DoubleLinkedList *)malloc(sizeof(DoubleLinkedList));
scanf("%d",&s->data);
s->prior=p;
p->next=s;
p=s;
}
p-next=head;
head->prior=p;
return head;
}
2、查找e
// 查找第1个值为e的结点
DoubleLinkedList *Locate(DoubleLinkedList *head, ElemType e){
DoubleLinkedList *p;
p=head->next; // p指向第一个结点
while(p!=head && p->data!=e)
p=p->next;
if(p==head) return NULL;
else return p;
}
3、查找i
// 在双向链表中查找第i个结点
DoubleLinkedList *GetElemP(DoubleLinkedList *head, int i){
DoubleLinkedList *p;
int j;
if(i<1) return 0;
p=head->next;
j=1;
while(p!=head && j<i){
// 顺指针向后查找,直到p指向第i个结点或p为空
p=p->next;
j++;
}
if(p==head && j<i) return NULL;// 第i个元素不存在
return p;
}
4、插入
需要同时修改两个方向上的指针
若仅已知直接前驱结点p,拉链时必须注意先后次序 S=(DoubleLinkedList *)malloc(sizeof(DoubleLinkedList));S->data=e;(1) S->next=p->next;(2) p->next->prior=S;(3) p->next=S;(4) S->prior=p;
- 若同时给出直接前驱结点p和直接后继结点q,那么无须注意先后次序
S=(DoubleLinkedList *)malloc(sizeof(DoubleLinkedList));S->data=e;p->next=S; S->next=q;S->prior=p; q->prior=S;
int InsertElem(DoubleLinkedList *head, int i, ElemType e){
DoubleLinkedList *p,*q;
int j;
if(i<1) return 0;// i的合法值为1≤i≤表长+1
//找前驱
while(p->next!=head && j<i){
p=p->next;
j++;
}
if(p->next!=head || (p->next == head && j==i-1)){
q=(DoubleLinkedList *)malloc(sizeof(DoubleLinkedList));
q->data=e;
q->next=p->next;
q->prior=p;
p->next->prior=q;
p->next=q;
return 1;
}
else return 0; //第i个元素不存在
}
5、删除
需要同时修改两个方向上的指针
- 若删除的结点为p:
p->prior->next = p->next;p->next->prior = p->prior;free(p); 若删除结点的前驱结点为p:
p->next=p->next->next; p->next->prior=p;
int DeleteNode(DoubleLinkedList *head, int i, ElemType *e){
DoubleLinkedList *p;
if(i<1) return 0;
//在head中确定第i个结点的位置指针p
p=GetElemP(head,i);
if(!p) return 0;//不存在
*e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return 1;
}
六、循环链表Circular Linked List
单链循环链表
最后一个结点的指针域指向链表的头结点 对于单循环链表,除链表的合并外,其它的操作和单线性链表 基本上一致,仅仅需要在单线性链表操作算法基础上作以下简 单修改 :
判断是否是空链表:head->next==head 判断是否是表尾结点:p->next==head
- ???
- 设置头指针、尾指针的循环链表
- 例如:两个循环链表的合并
仅设置尾指针的循环链表
A尾→B头;B尾→A头
双向循环链表
- 具有指向前驱和后继的指针,这样,从循环链表的任意一个结点出发都可以找到链表中的其它结点,使得表处理更加方便灵活