3、线性表
1、线性表(List):由零个或多个数据元素组成的有限序列。
2、特点:
(1)首先它是一个序列,也就是说元素之间是有个先来后到的
(2)若元素存在多个,则第一个元素无前驱,而最后一个元素无后继,其他元素都有且只有一个前驱和后继。
(3)线性表强调是有限的,事实上无论计算机发展到多强大,它所处理的元素都是有限的。
3、若将线性表记为(a1,…,ai-1,ai,ai+1,…an),则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。
4、所以线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
5、数据类型:是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。
例如很多编程语言的整型,浮点型,字符型这些指的就是数据类型。
6、在C语言中,按照取值的不同,数据类型可以分为两类:
原子类型:不可以再分解的基本类型,例如整型、浮点型、字符型等。
结构类型:由若干个类型组合而成,是可以再分解的,例如整型数组是由若干整型数据组成的。
7、抽象:是指抽取出事物具有的普遍性的本质。它要求抽出问题的特征而忽略非本质的细节,是对具体事物的一个概括。抽象是一种思考问题的方式,它隐藏了繁杂的细节。
8、抽象数据类型(Abstract Data Type,ADT)是指一个数学模型及定义在该模型上的一组操作。 抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示和实现无关。
9、描述抽象数据类型的标准格式:
ADT 抽象数据类型名
Data
数据元素之间逻辑关系的定义
Operation
操作
endADT
10、
线性表的抽象数据类型定义:
ADT 线性表(List)
Data
线性表的数据对象集合为{a1,a2,…,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
InitList(*L): 初始化操作,建立一个空的线性表L。
ListEmpty(L): 判断线性表是否为空表,若线性表为空,返回true,否则返回false。
ClearList(*L): 将线性表清空。
GetElem(L,i,*e): 将线性表L中的第i个位置元素值返回给e。
LocateElem(L,e): 在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
ListInsert(*L,i,e): 在线性表L中第i个位置插入新元素e。
ListDelete(*L,i,*e): 删除线性表L中第i个位置元素,并用e返回其值。
ListLength(L): 返回线性表L的元素个数。
endADT
11、例:要实现两个线性表A、B的并集操作,即要使得集合A=A∪B。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/********************************************************************************************* * * 求并集AUB * *********************************************************************************************/ void unionL(List *La, List Lb) { int La_len, Lb_len, i; //定义长度和循环变量 ElemType e;//定义一个同类型的变量 La_len = ListLength(La);//获取La的长度 Lb_len = ListLength(Lb);//获取Lb的长度 for(i=0;i<La_len;i++) { GetElem(Lb,i,*e);//获取b中的元素,返回给e while(!LocateElem(La,e))//在a中查找是否有对应的元素,如果没有,则执行 { InsertList(La,++La_len,e);//如果没有,则在后面插入 } } }
12、线性表有两种物理存储结构:顺序存储结构和链式存储结构。
13、线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。
14、物理上的存储方式事实上就是在内存中找个初始地址,然后通过占位的形式,把一定的内存空间给占了,然后把相同数据类型的数据元素依次放在这块空地中。
15、线性表顺序存储的结构代码:
#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length; // 线性表当前长度
} SqList;
16、顺序存储结构封装需要三个属性:
存储空间的起始位置,数组data,它的存储位置就是线性表存储空间的存储位置。
线性表的最大存储容量:数组的长度MaxSize。
线性表的当前长度:length。
17、假设ElemType占用的是c个存储单元(字节),那么线性表中第i+1个数据元素和第i个数据元素的存储位置的关系是(LOC表示获得存储位置的函数):LOC(ai+1) = LOC(ai) + c
所以对于第i个数据元素ai的存储位置可以由a1推算得出:LOC(ai) = LOC(a1) + (i-1)*c
18、获取线性表中的元素函数GetElem
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 获取线性表中的元素,GetElem.c 4 * 5 *********************************************************************************************/ 6 7 8 9 #define ok 1 10 #define error 0 11 12 typedef int status 13 14 status GetElem(SqList L, int i, ElemType *e ) 15 { 16 if(L.length ==0 || i > L.length || i<1) 17 { 18 return error; 19 } 20 21 *e = L.data[i-1]; 22 23 return ok; 24 }
19、线性表插入函数ListInsert
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 线性表插入元素,ListInsert.c 4 * 5 *********************************************************************************************/ 6 7 8 #define ERROR 0 9 #define OK 1 10 11 typedef int Status 12 13 14 Status ListInsert(SqList *L, int i, ElemType e) 15 { 16 int k; 17 18 if(i<1 || i>L.length+1)//插入位置不在范围 19 return ERROR; 20 21 if(L.length = MAXSIZE)//已满,不能插 22 return ERROR; 23 24 if(i != L.length)//如果不在尾部 25 { 26 27 for(k=L.length-1;k>=i-1;k--) //i及它后面的均向后移一位,从0开始,所以都要减1 28 { 29 L.data[k+1]=L.data[k]; 30 } 31 } 32 33 L.data[i-1]=e;//插入元素 34 35 L.length++; 36 37 return OK; 38 }
20、线性表删除函数ListDelete
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 线性表删除元素,ListDelete 4 * 5 *********************************************************************************************/ 6 7 8 #define error 0 9 #define ok 1 10 11 typedef int Status 12 13 14 Status ListDelete(SqList *L, int i, ElemType *e) 15 { 16 int k;//k表示位置 17 18 if(i<1 || i>L.length)//删除位置不对 19 return eroor; 20 21 if(L.length==0)//如果线性表本为空 22 return error; 23 24 *e = L.data[i-1];//获取删除值,从0开始计数,所以,第i个,数据是data[i-1] 25 26 if(i!=L.length)//如果不是在尾部删除,则需要移动 27 { 28 for(k=i;k<L.length;k++) 29 L.data[k-1] = L.data[k];//i后面的,向前移动一位 30 } 31 32 L.length--; 33 34 return ok; 35 36 }
21、线性表的链式存储结构
(1)线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以存在内存中未被占用的任意位置。
(2)比起顺序存储结构每个数据元素只需要存储一个位置就可以了。现在链式存储结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址(指针)。
也就是说除了存储其本身的信息外,还需存储一个指示其直接后继的存储位置的信息。
(3)我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成数据元素称为存储映像,称为结点(Node)。
(4)n个结点链接成一个链表,即为线性表(a1, a2, a3, …, an)的链式存储结构。因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
(5)我们把链表中的第一个结点的存储位置叫做头指针,最后一个结点指针为空(NULL)。
(6)头指针
a. 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
b. 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
c. 无论链表是否为空,头指针均不为空。 头指针是链表的必要元素。
(7)头结点
a. 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
b. 有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
c. 头结点不一定是链表的必须要素。
(8)在C语言中可以用结构指针来描述单链表。
typedef struct Node
{
ElemType data; // 数据域
struct Node* Next; // 指针域
} Node;
typedef struct Node* LinkList;
(9)我们看到结点由存放数据元素的数据域和存放后继结点地址的指针域组成。
(10)单链表存储结构
假设p是指向线性表第i个元素的指针,则该结点ai的数据域我们可以用p->data的值是一个数据元素,结点ai的指针域可以用p->next来表示,p->next的值是一个指针。
那么p->next指向谁呢?当然指向第i+1个元素!也就是指向ai+1的指针。
(10)单链表的读取
对于单链表实现获取第i个元素的数据的操作函数GetElem
获得链表第i个数据的算法思路:
a. 声明一个结点p指向链表第一个结点,初始化j从1开始;
b. 当j<i时,就遍历链表,让p的指针向后移动,不断指向一下结点,j+1; 若到链表末尾p为空,则说明第i个元素不存在; 否则查找成功,返回结点p的数据。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 单链表查找第i个元素,GetElem() 4 * 5 *********************************************************************************************/ 6 7 #define error 0 8 #define ok 1 9 10 11 typedef int Statues 12 13 14 Statues GetElem(LinkList L, int i, ElemType *e) 15 { 16 17 int j=1;//遍历变量 18 LinkList p; 19 20 p=L->next;//指针p赋值 21 22 while(p && j<i)//依次查找,一直到i 23 { 24 p=p->next; 25 j++; 26 } 27 28 if(!p || j>i)//如果没有查找到 29 return error; 30 31 *e = p->data;//查找到了,获取值 32 33 return ok; 34 35 }
(11)单链表的插入
s->next = p->next;
p->next = s;
单链表第i个数据插入结点的算法思路:
a. 声明一结点p指向链表头结点,初始化j从1开始;
b. 当j<1时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
c. 若到链表末尾p为空,则说明第i个元素不存在; 否则查找成功,在系统中生成一个空结点s;
d.将数据元素e赋值给s->data; 单链表的插入刚才两个标准语句; 返回成功。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 单链表的插入, ListInsert 4 * 5 *********************************************************************************************/ 6 7 8 #define error 0 9 #define ok 1 10 11 typedef int Status 12 13 14 Status ListInsert(LinkList *L, int i, ElemType e) 15 { 16 17 int j=1; 18 19 LinkList p, s; //p是指向头节点,s是插入节点 20 21 p = *L;//P赋值头节点 22 23 while(p && j<i)//遍历查找,一直到i 24 { 25 p = p->next; 26 j++; 27 } 28 29 if(!p || j>i)//如果没有查找到,返回错误 30 return error; 31 32 //s赋值 33 s=(LinkList)malloc(sizeof(Node));//获得内存空间,强制转换 34 s->data = e; 35 36 //插入 37 s->next = p->next; 38 p->next=s; 39 40 return ok; 41 42 }
(12)单链表的删除
假设元素a2的结点为q,要实现结点q删除单链表的操作,其实就是将它的前继结点的指针绕过指向后继结点即可。可以这样:p->next = p->next->next;
单链表第i个数据删除结点的算法思路:
a. 声明结点p指向链表第一个结点,初始化j=1;
b.当j<1时,就遍历链表,让P的指针向后移动,不断指向下一个结点,j累加1;
c. 若到链表末尾p为空,则说明第i个元素不存在; 否则查找成功,将欲删除结点p->next赋值给q;
d.单链表的删除标准语句p->next = q->next; 将q结点中的数据赋值给e,作为返回; 释放q结点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 单链表的删除,ListDelete 4 * 5 *********************************************************************************************/ 6 7 8 9 #define error 0 10 #define ok 1 11 12 typedef int Statues 13 14 Statues ListDelete(Linklist *L, int i, ElemType *e) 15 { 16 int j=1; 17 LinkList p,q; 18 19 p=*L;//p赋值为头节点 20 21 while(p && j<i)//遍历查找 22 { 23 p=p->next; 24 j++; 25 } 26 27 if(!p || j>i)//如果查找不到 28 return error; 29 30 q=p->next;//取出删除的节点 31 32 p->next=p->next->next;//删除 33 34 *e=q->data;//获取值 35 free(q);//释放q 36 37 return ok; 38 }
(13)头插法建立单链表
头插法从一个空表开始,生成新结点,读取数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到结束为止。
简单来说,就是把新加进的元素放在表头后的第一个位置: 先让新节点的next指向头节点之后 然后让表头的next指向新节点
头插法建立链表虽然算法简单,但生成的链表中结点的次序和输入的顺序相反。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 头插法建立单链表 4 * 5 *********************************************************************************************/ 6 7 void CreateListHead(LinkList *L, int n) 8 { 9 int i; 10 LinkList p;//定义一个结点,循环插入 11 12 srand(time(0));//初始化随机数种子,为rand获取随机数作准备 13 14 *L=(LinkList)malloc(sizeof(Node));//空链表,获取空间 15 (*L)->next=NULL;//头结点指向空 16 17 for(i=0;i<n;i++) 18 { 19 p=(LinkList)malloc(sizeof(Node));//生成新结点,获取空间 20 p->data=rand()%100+1;//赋值 21 22 //插入结点p,往前插入 23 p->next=(*L)->next; 24 (*L)->next=p 25 } 26 }
(14)把新结点都插入到最后,这种算法称之为尾插法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 尾插法建立单链表 4 * 5 *********************************************************************************************/ 6 7 8 void CreateListTail(LinkList *L, int n) 9 { 10 int i; 11 LinkList p,s;//插入结点 12 13 srand(time(0);//初始化随机种子,为后面生成随机数rand作准备 14 15 //初始化空链表 16 s=*L; 17 s=(LinkList)malloc(sizeof(Node));//获取地址 18 19 for(i=0;i<n;i++)//循环尾部插入 20 { 21 //初始化p 22 p=(LinkList)malloc(sizeof(Node));//获取空间 23 p->data=rand%100+1;//随机赋值 24 25 //尾部插入 26 s->next=p;//先将P放入尾部 27 s=p;//s再指向尾部,s一直指向尾部 28 } 29 30 s->next=NULL;//插完之后最后一个尾部指向空 31 }
(15)单链表的整表删除
其实也就是在内存中将它释放掉,以便于留出空间给其他程序或软件使用。
单链表整表删除的算法思路如下:
a.声明结点p和q;
b.将第一个结点赋值给p,下一结点赋值给q;
c.循环执行释放p和将q赋值给p的操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 单链表的整表删除 4 * 5 *********************************************************************************************/ 6 7 8 #define error 0 9 #define ok 1 10 11 typedef int Status 12 13 Status ClearList(LinkList *L) 14 { 15 16 LinkList p,q; 17 18 p=(*L)->next; 19 20 while(p) 21 { 22 q=p->next;//获取下一个 23 free(p);//释放 24 p=q;//指向下一个 25 } 26 27 p->next=NULL;//指向空 28 29 return ok; 30 }
(16)静态链表
用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。
线性表的静态链表存储结构
#define MAXSIZE 1000
typedef struct
{
ElemType data; // 数据
int cur; // 游标(Cursor)
} Component, StaticLinkList[MAXSIZE];
对静态链表进行初始化相当于初始化数组:
Status InitList(StaticLinkList space)
{
int i;
for( i=0; i < MAXSIZE-1; i++ ) space[i].cur = i + 1;
space[MAXSIZE-1].cur = 0;
return OK;
}
我们对数组的第一个和最后一个元素做特殊处理,他们的data不存放数据。
我们通常把未使用的数组元素称为备用链表。
数组的第一个元素,即下标为0的那个元素的cur就存放备用链表的第一个结点的下标。
数组的最后一个元素,即下标为MAXSIZE-1的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用。
静态链表获得空闲变量:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 静态链表获得空闲变量 4 * 5 *********************************************************************************************/ 6 7 8 int Malloc_SLL(StaticLinkList space) 9 { 10 int i=space[0].cur;//数组第0个的游标是存放后面第一个为空的,最后一个的游标存放第一个不为空的,这里也就是取的第一个为空的数组的下标位置i 11 12 if(space[0].cur)//如果第0个非空,执行下面, 也就是说,第0个不是第一个空的,i!=0 13 space[0].cur=space[i].cur;//把第一个空的那个(数组下标是i)的游标,给第0个的游标, 现在第0个的游标就是第一次空的那个的游标, 第一个空的游标也就是它需要执行的下一个的下标,这里用来作备用 14 15 return i;//返回到第一个为空的下标位置i 16 } 17 18 19
静态链表的插入操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 静态链表的插入操作 4 * 5 *********************************************************************************************/ 6 7 8 9 #define error 0 10 #define ok 1 11 12 13 typedef int Status 14 15 16 Status ListInsert(Staticlist L, int i, ElemType e) 17 { 18 int j,k,m; 19 20 if(i<1 || i>ListLength(L)+1)//位置不合适 21 return error; 22 23 j=Malloc_SLL(L);//获取第一个为空的数组元素数组下标号j, 并且执行后,第0个元素的下一个执行元素(游标)值为第j个元素下一个执行的元素(第j个游标)的值 24 25 if(j)//如果不是第0个 26 { 27 L[j].data=e;//先赋值 28 29 for(m=1;m<i-1;m++)//i,在数组中,标号也就是i-1 30 { 31 k=L[k].cur;//不断指向下一个,一直到第i个,也就是i-1 32 } 33 34 L[j].cur=L[k].cur; 35 L[i].cur=Lj; 36 37 return ok; 38 } 39 40 error; 41 }
静态链表删除操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 删除L中的第i个元素 4 * 5 *********************************************************************************************/ 6 7 8 9 #define error 0 10 #define ok 1 11 12 13 typedef int Status 14 15 16 Status ListDelete(StaticLinkList L, int i) 17 { 18 int j,k; 19 20 if(i<1 || i>ListLength(L))//判断查找范围合理 21 return error; 22 23 k=MAXSIZE-1;//数组下标最大处,保存的是第一个非空的元素,所以应该从这个元素开始,按照链表(游标)依次向下寻找 24 25 for(j=1;j<=i-1;j++)//依次向下取,一直找到第i个元素 26 k=L[k].cur;//现在,第i-1个元素的游标是k,也就是i个元素的下标 27 28 //删除 29 j=L[k].cur;//获取第i个元素的游标 30 L[k].cur=L[j].cur;//直接从第i-1个元素,链接到第i+1个元素 31 32 Free_SLL(L,j);//将下标为j的空闲结点回收到备用链表 33 34 return ok; 35 36 37 } 38 39 40 41 42 //将下标为k的空闲结点回收到备用链表 43 void Free_SLL(StaticLinkList space, int k) 44 { 45 space[k].cur=space[0].cur; 46 space[0].cur=k; 47 } 48 49 50 51 //返回L中数据元素的个数 52 int ListLength(StaticLinkList L) 53 { 54 int j=0;//计数变量 55 int i=L[MAXSIZE-1].cur;//数组下标最大处,保存的是第一个非空的元素,所以应该从这个元素开始,按照链表(游标)依次向下寻找 56 57 while(i) 58 { 59 i=L[i].cur;//依次查询 60 j++;//计数 61 } 62 63 return j; 64 }
22、快速找到未知长度单链表的中间节点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 题目:快速找到未知长度单链表的中间节点。 4 * 说明:利用快慢指针原理:设置两个指针*search、*mid都指向单链表的头节点。 5 * 其中* search的移动速度是*mid的2倍。 6 * 当*search指向末尾节点的时候,mid正好就在中间了。这也是标尺的思想。 7 * 8 * 9 *********************************************************************************************/ 10 11 12 13 #define error 0 14 #define ok 1 15 16 17 typedef int Status 18 19 20 Status GetMidNode(LinkList L, Elemtype *e) 21 { 22 LinkList search, mid; 23 search=mid=L; 24 25 while( search->next->next !=NULL ) 26 { 27 if( search->next->next !=NULL ) 28 { 29 search=search->next->next;//search速度是mid速度两倍 30 mid=mid->next; 31 } 32 else 33 search=search->next; 34 } 35 36 *e=mid->data; 37 38 return ok; 39 }
23、循环链表
(1)将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表成为单循环链表,简称循环链表。
(2)其实循环链表的单链表的主要差异就在于循环的判断空链表的条件上,原来判断head->next是否为null,现在则是head->next是否等于head。
(3)初始化循环链表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 初始化循环链表,插入循环链表 4 * 5 *********************************************************************************************/ 6 7 void ds_init(node **pNode) 8 { 9 int item;//插入结点的数据 10 node *temp;//插入结点指针 11 node *target;//原来最后一个结点 12 13 while(1) 14 { 15 printf("请输入插入的数(输入为0时初始化完成): "); 16 17 scanf("%d",&item);//输入待插入结点的数值 18 19 if(!item)//如果输入的为0,则完成初始化退出 20 return; 21 22 else//如果输入的不是0,则插入结点 23 { 24 if(*pNode==NULL)//如果原循环链表为空 25 { 26 *pNode=(node*)malloc(sizeof(struct CLinkList));//生成节点 27 28 if(!*pNode)//如果没有申请成功,则退出 29 exit(0); 30 31 *pNode->data=item;//结点数值赋值 32 *pNode->next=*pNode;//结点指针赋值 33 } 34 35 else//如果原循环链表非空 36 { 37 for(target=*pNode;target->next!=*pNode;target=target->next);//循环链表依次遍历,一直到最后一个结点,最后一个结点target 38 39 temp=(node*)malloc(sizeof(struct CLinkList));//生成插入结点 40 41 if(!temp)//如果生成节点失败,退出 42 exit(0); 43 44 temp->data=item;//插入的节点的数值赋值 45 temp->next=*pNode;//插入结点的指针 46 target->next=temp;//结点插入 47 } 48 } 49 } 50 }
(4) 结点插入
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 循环链表插入结点,在具体的位置插入 4 * 5 *********************************************************************************************/ 6 7 void ds_insert(node **pNode, int i) 8 { 9 int item;//插入结点的数值 10 int j=1;//遍历变量 11 node *temp;//待插入的节点指针 12 node *target;//插入结点之前结点的指针 13 node *p;//插入结点之后结点的指针 14 15 16 printf("请输入插入结点的值:"); 17 18 scanf("%d",&item);//输入插入结点的值 19 20 if(i==1)//如果插入的是头结点 21 { 22 temp=(node*)malloc(sizeof(struct CLinkList));//获取空间,生成插入结点 23 24 if(!temp)//如果获取空间失败,退出 25 exit(0); 26 27 for(target=*pNode;target->next!=*pNode;target=target->next);//遍历链表,一直到尾结点 28 29 target->next=temp;//插入链表的前一个 30 temp->next=*pNode;//插入链表的后一个 31 *pNode=temp;//头结点移位 32 } 33 34 else//如果插入的不是头结点 35 { 36 temp=(node*)malloc(sizeof(struct CLinkList));//获取空间,生成插入结点 37 38 if(!temp)//如果获取空间失败,则退出 39 exit(0); 40 41 for(target=*pNode;j<i-1;j++,target=target->next);//遍历链表,一直到插入结点之前的那个target 42 43 p=target->next;//p为插入结点之后的那个结点 44 45 target->data=item;//数值,插入结点数值 46 target->next=temp;//指针,插入结点之前的 47 temp->next=p;//指针,插入结点之后的 48 49 } 50 }
(4)结点查找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 查找结点,返回结点所在的位置 4 * 5 *********************************************************************************************/ 6 int ds_search(node *pNode, int elem) 7 { 8 int i; 9 node *target; 10 11 for(target=pNode;target->next!=pNode && target->data!=elem; i++, target=target->next);//遍历查询 12 13 if(target->next==pNode) 14 return 0; 15 16 else 17 return i; 18 }
(5)删除结点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /********************************************************************************************* 2 * 3 * 删除结点 4 * 5 *********************************************************************************************/ 6 7 void ds_delete(node *pNode, int i) 8 { 9 node *target;//遍历结点指针 10 node *temp;//删除的节点指针 11 int j=1;//遍历变量 12 13 14 if(j==1)//是头结点 15 { 16 for(target=pNode; target->next!=pNode; target=target->next);//遍历循环链表,一直到尾结点target 17 18 temp=*pNode;//删除头结点 19 20 target->next=pNode->next; 21 pNode=pNode->next; 22 23 free(temp);//释放删除的结点 24 } 25 26 27 else//如果不是头结点 28 { 29 for(target=pNode; j<i; j++, target=target->next);//遍历到删除结点的前一个结点target 30 31 temp=target->next;//获取删除的结点 32 33 target->next=target->next->next;//删除后结点指向 34 35 free(temp); 36 } 37 }
24、
(1)约瑟夫问题
在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /************************************************************************************************** 2 * 3 * 约瑟夫问题: 4 * 在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也 5 * 不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3 6 * 人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 7 * 8 *************************************************************************************************/ 9 10 11 #include"stdio.h" 12 #include"stdlib.h" 13 #include"conio.h" 14 15 16 17 18 19 20 /*创建一个循环链表结构*/ 21 typedef struct node 22 { 23 int data; 24 struct node *next; 25 }node; 26 27 28 29 30 31 32 /*新建一个循环链表*/ 33 node *CreatCLinkList(int n)//链表中共有n个结点 34 { 35 node *head;//头结点 36 node *tail;//尾结点 37 node *temp;//零时结点 38 int i; 39 40 head=(node*)malloc(sizeof(node));//生成头结点 41 42 tail=head;//初始,尾结点等于头结点 43 44 45 if(n!=0)//循环链表非空 46 { 47 for(i=1; i<=n; i++) 48 { 49 temp=(node*)malloc(sizeof(node));//生成新结点 50 51 temp->data=i;//数值,新结点 52 tail->next=temp;//指针,新结点前面 53 tail=temp;//尾结点移动 54 } 55 56 tail->next=head->next;//结点添加完之后,跳过初始的head,串接 57 58 } 59 60 free(head);//释放头结点空间,删除原来的头结点 61 62 return tail->next;//返回新的头结点 63 } 64 65 66 67 68 69 70 71 72 /*主函数,完成功能*/ 73 int main(void) 74 { 75 int n;//循环链表的节点个数 76 int m;//每m个取依次 77 int i; 78 int j=1;//第j次删除的节点 79 node *head;//链表头结点 80 node *temp;//临时结点 81 node *pre;//待删除结点的前一个结点 82 83 printf("请输入总的人数:"); 84 scanf("%d",&n);//输入总人数 85 86 printf("\n请输入出局的人报的数:"); 87 scanf("%d",&m);//数到m的人出局 88 89 head=CreatCLinkList(n);//生成新的循环链表,头结点指针为head 90 91 pre=head;//最初开始,指向头结点 92 93 while(pre->next!=pre)//只要不到最后一个 94 { 95 for(i=1;i<m%n-1;i++)//向后查找第m个结点前面的一个结点pre 96 pre=pre->next; 97 98 temp=pre->next;//获取待删除的节点,便于从循环链表中删除后释放空间 99 100 pre->next=pre->next->next;//连接删除的节点前后的节点,也就是完成从链表中删除结点的功能 101 102 pre=pre->next;//删除之后,pre重新放在删除之后的那个结点的位置 103 104 printf("\n第%d次删除的节点:%d",j,temp->data);//显示删除的节点 105 106 free(temp);//释放删除结点的空间 107 108 j++; 109 } 110 111 printf("\n最后剩下的结点:%d",pre->data); 112 113 getch(); 114 getch(); 115 getch(); 116 117 return 0; 118 }
(2)
25、双向链表
(1)双向链表结点结构
typedef struct DualNode
{
ElemType data; struct DualNode *prior; //前驱结点
struct DualNode *next; //后继结点
} DualNode, *DuLinkList;
(2)双向链表的插入操作
s->next = p;
s->prior = p->prior;
p->prior->next = s;
p->prior = s;
(3)双向链表的删除操作
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);