对于没有指针的编程语言,可以用数组替代指针,来描述链表。让数组的每个元素由data和cur两部分组成,其中cur相当于链表的next指针,这种用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。我们对数组的第一个和最后一个元素做特殊处理,不存数据。让数组的第一个元素cur存放第一个备用元素(第一个未被占用的元素)下标,而数组的最后一个元素cur存放第一个有值的元素下标,相当于头结点作用。空的静态链表如下图
当存放入一些数据时("甲""乙""丁""戊""己""庚"),静态链表为:
静态链表的插入操作
在静态链表第三个位置插入"丙"后,结果如下:
静态链表中要解决的是:如何用静态模拟动态链表结构的存储空间的分配,需要时申请,无用时释放。
其主要的思想:(以我们如果要在乙 和 丁 中间插入一个丙 )
我们自己定义一个内存分配函数,如Malloc_SLL(StaticLinkList space)
1 //插入元素时,分配空间的下标(从备用链表中去取出)
2 int Malloc(StaticLinkList L)
3 {
4 int i = L[0].cur; //获取备用链表的下表
5 if (i) //链表不为空
6 L[0].cur = L[i].cur; //将第一位的游标改成备用链表的下表+1
7 return i;
8 }
然后再修改游标表示,如上图
代码附上:
1 //静态链表中i位置插入一个元素
2 bool StaticLinkListInsert(StaticLinkList L, int i, int key)
3 {
4 //判断插入点是否合理
5 if (i<1 || i>StaticLinkListLength(L)+1)
6 {
7 return false;
8 }
9 int j = Malloc(L);
10 int k = MAXSIZE - 1;
11 if (j)
12 {
13 for (int l = 1; l <= j - 1; l++)
14 {
15 k = L[k].cur;
16 }
17 L[j].data = key;
18 L[j].cur = L[k].cur;
19 L[k].cur = j;
20 return true;
21 }
22 return false;
23
24 }
静态链表的删除操作
删除操作其实就是插入操作的逆操作
我们同样要自己定义一个Free函数
void Free(StaticLinkList L, int k)
{
L[k].cur = L[0].cur;
L[0].cur = k;
}
获取要删除的元素下标后,开始修改游标
1 bool StaticLinkListDelete(StaticLinkList L,int i, int *key)
2 {
3 if (i < 1 || i >= StaticLinkListLength(L))
4 {
5 return false;
6 }
7 int k = MAXSIZE - 1;
8 for (int l = 1; l <= i-1; l++)
9 {
10 k = L[k].cur;
11 }
12 int j = L[k].cur;
13 *key = L[j].data;
14 L[k].cur = L[j].cur;
15 Free(L, j);
16 return true;
17 }
删除"甲"后,静态链表如下:
总结一下静态链表的优缺点:
优点:在插入和删除时,只需要修改游标,不需要移动元素。从而改进了在顺序存储结构中的插入和删除需要移动大量元素的特点。
缺点:没有解决连续存储分配带来的表长难以确定的问题;失去了顺序存储结构随机存储的特性。
Copy 了大佬一些些文字和图片:
https://www.cnblogs.com/zhaoxy/p/7754906.html
静态链表实现的一些基本操作:(附上代码)
1 #include<stdio.h> 2 3 #define MAXSIZE 7 4 #define true 1 5 #define false 0 6 7 typedef int bool; 8 9 typedef struct 10 { 11 int data; 12 int cur; 13 }StaticLinkList[MAXSIZE]; 14 15 //初始化静态链表,0位置cur指向1位置,1位置cur指向2位置...MAXSIZE-1位置指向0位置 16 void InitStaticLinkList(StaticLinkList L)//本来想用小写l的,但是小写l看起来太怪了 17 { 18 for (int i = 0; i < MAXSIZE - 2; i++) 19 { 20 L[i].cur = i + 1;//最后一个元素指向的是尾节点 21 } 22 L[MAXSIZE - 2].cur = 0;//备用链表的最后一个空元素的cur指向0,这一行不能省。 23 L[MAXSIZE - 1].cur = 0; 24 } 25 26 27 //求静态链表中元素个数,不包括头尾节点 28 int StaticLinkListLength(StaticLinkList L) 29 { 30 int i = L[MAXSIZE - 1].cur; 31 int j = 0; 32 while (i) 33 { 34 j++; 35 i = L[i].cur; 36 } 37 return j; 38 } 39 40 //插入元素时,分配空间的下标 41 int Malloc(StaticLinkList L) 42 { 43 int i = L[0].cur; 44 if (i) 45 L[0].cur = L[i].cur; 46 return i; 47 } 48 49 50 //静态链表中i位置插入一个元素 51 bool StaticLinkListInsert(StaticLinkList L, int i, int key) 52 { 53 //判断插入点是否合理 54 if (i<1 || i>StaticLinkListLength(L)+1) 55 { 56 return false; 57 } 58 int j = Malloc(L); 59 int k = MAXSIZE - 1; 60 if (j) 61 { 62 for (int l = 1; l <= j - 1; l++) 63 { 64 k = L[k].cur; 65 } 66 L[j].data = key; 67 L[j].cur = L[k].cur; 68 L[k].cur = j; 69 return true; 70 } 71 return false; 72 73 } 74 75 76 void Free(StaticLinkList L, int k) 77 { 78 L[k].cur = L[0].cur; 79 L[0].cur = k; 80 } 81 82 //删除第i个元素 83 84 bool StaticLinkListDelete(StaticLinkList L,int i, int *key) 85 { 86 if (i < 1 || i >= StaticLinkListLength(L)) 87 { 88 return false; 89 } 90 int k = MAXSIZE - 1; 91 for (int l = 1; l <= i-1; l++) 92 { 93 k = L[k].cur; 94 } 95 int j = L[k].cur; 96 *key = L[j].data; 97 L[k].cur = L[j].cur; 98 Free(L, j); 99 return true; 100 } 101 102 //遍历 103 void StaticLinkListTraverse(StaticLinkList L) 104 { 105 int k = MAXSIZE - 1; 106 while (L[k].cur) 107 { 108 k = L[k].cur; 109 printf("%d ", L[k].data); 110 } 111 printf("\n"); 112 } 113 114 int main(void) 115 { 116 StaticLinkList L; 117 printf("初始化链表:\n"); 118 InitStaticLinkList(L); 119 printf("初始化链表之后,链表的长度为:%d\n", StaticLinkListLength(L)); 120 printf("插入1,2,3,4,5\n"); 121 StaticLinkListInsert(L, 1, 1); 122 StaticLinkListInsert(L, 1, 2); 123 StaticLinkListInsert(L, 1, 3); 124 StaticLinkListInsert(L, 1, 4); 125 StaticLinkListInsert(L, 1, 5); 126 printf("遍历链表:\n"); 127 StaticLinkListTraverse(L); 128 printf("链表长度为:%d\n", StaticLinkListLength(L)); 129 printf("删除第二个元素:\n"); 130 int key; 131 StaticLinkListDelete(L, 2, &key); 132 printf("删除的元素值为:%d\n", key); 133 printf("遍历链表:\n"); 134 StaticLinkListTraverse(L); 135 136 }
并且附上代码来源:
https://blog.csdn.net/hhhhhyyyyy8/article/details/81027728
循环链表
1、循环链表:
这个就是在单链表的基础上头尾相接了,将最后一个结点的指针指向了L->next;这里我们也不多做赘述,它的大部分操作和单链表是相似的。
还有一点要注意的就是判断一个循环链表是否为空条件是看头结点是否指向其本身。
所谓双向就是说有两个方向,其实就是在单链表的基础上,多了一个前驱的指针域,用来指向前驱元素,其解决了单链表单向性这一缺点(只能顺着后继元素遍历)
双向循环链表,与循环链表类似,带头结点的双向循环空链表如下图(左),非空如下图(右边)
那么入定义一个双向链表呢?
1 typedef struct DulNode
2 {
3 struct DulNode *prior; //前驱指针
4 struct DulNode *next; //后继指针
5 ElemType e; //数据域
6 }DulNode,*DuLinkList;
插入操作
对比单链表学习,复杂并且灵活
插入关键代码:
1 s->prior = p; //把p赋值给s的前驱,s的前驱指向p
2 s->next = p->next; //把p->next赋值给s的后继
3 p->next->prior = s; //把s赋值给p-next的前驱
4 p->next = s; //把s赋值给p的后继
删除操作
删除操作关键代码:
1 P->prior->next = p->next; //把p->next的值赋给p->prior的后继
2 p->next->prior = P->prior;//把p=>prior赋值给p->next的前驱
复习回顾:
我们对线性表的两大结构进行了学习,先是学习了比较容易的顺序存储结构,,指的是用一段地址连续的存储单元依次存储线性表中的元素,我们通常使用数组来实现。
后面则是学习了链式存储结构。它具有不收存储空间的限制,可以比较快捷的插入和删除的特点被广为使用。然后还学习了动态链表的单链表、双链表、循环链表,以及最后我们还学习了如何不使用指针处理链式结构的数据关系---静态链表。
总的来说,线性表就包含了顺序存储结构和链式存储结构两大结构。
原文:https://www.cnblogs.com/liuzeyu12a/p/10301672.html