静态链表
一、存储结构
#define MAXSIZE 1000
typedef struct
{
ElemType data; // 数据
int cur; // 游标(Cursor)
} Component, StaticLinkList[MAXSIZE];
静态链表的存储结构.jpg
其中,有两对元素是特殊的:
第一对:
- 下标为0的元素,其数据不存放东西;
- 其游标是指向第一个没有存放数据的下标,此处为下标为5的元素。
第二对:
- 下标为maxsize-1的元素(即最后一个元素),其数据也不存放东西;
- 其游标指向第一个有数据的元素,此处是指向下标为1的元素。
其余:
- 除第一个和最后一个外,每一个元素的游标都是指向它的下一个元素;即游标为下一个元素的下标。
二、初始化静态链表
对静态链表进行初始化相当于初始化数组:
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则存放第一个有数值的元素的下标,相当于单链表中的头结点作用。
三、静态链表的插入操作
在A后边插入B:
插入操作.png 插入操作示范.png代码实现:
1.获得空闲分量的下标:
int Malloc_SLL(StaticLinkList space)
{
int i = space[0].cur;
if( space[0].cur )
space[0].cur = space[i].cur;
// 把它的下一个分量用来作为备用。
return i;
}
2.插入操作
/* 在静态链表L中第i个元素之前插入新的数据元素e */
Status ListInsert( StaticLinkList L, int i, ElemType e )
{
int j, k, l;
k = MAX_SIZE - 1; // 数组的最后一个元素
if( i<1 || i>ListLength(L)+1 )
{
return ERROR;
}
j = Malloc_SLL(L); //空闲分量的下标
if( j )
{
L[j].data = e;
for( l=1; l <= i-1; l++ )
{
k = L[k].cur;
}
L[j].cur = L[k].cur;
L[k].cur = j;
return OK;
}
return ERROR;
}
四、静态链表的删除操作
如图,要删除下标为2的元素:
删除操作.jpg代码实现:
/* 删除在L中的第i个数据元素 */
Status ListDelete(StaticLinkList L, int i)
{
int j, k;
if( i<1 || i>ListLength(L) )
{
return ERROR;
}
k = MAX_SIZE - 1;
for( j=1; j <= i-1; j++ )
{
k = L[k].cur; // k1 = 1, k2 = 5
}
j = L[k].cur; // j = 2
L[k].cur = L[j].cur;
Free_SLL(L, j);
return OK;
}
/* 将下标为k的空闲结点回收到备用链表 */
void Free_SLL(StaticLinkList space, int k)
{
space[k].cur = space[0].cur;
space[0].cur = k;
}
/* 返回L中数据元素个数 */
int ListLength(StaticLinkList L)
{
int j = 0;
int i = L[MAXSIZE-1].cur;
while(i)
{
i = L[i].cur;
j++;
}
return j;
}
五、静态链表的优缺点总结
优点:
- 在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。
缺点:
- 没有解决连续存储分配(数组)带来的表长难以确定的问题。
- 失去了顺序存储结构随机存取的特性。
总的来说,静态链表其实是为了给没有指针的编程语言设计的一种实现单链表功能的方法。
尽管我们可以用单链表就不用静态链表了,但这样的思考方式是非常巧妙的,应该理解其思想,以备不时之需。
题目1:快速找到未知长度单链表的中间节点。
方法一:
首先遍历一遍单链表以确定单链表的长度L。
然后再次从头节点出发循环L/2次找到单链表的中间节点。
算法复杂度为:O(L+L/2)=O(3L/2)。
方法二:利用快慢指针
快慢指针原理:
设置两个指针*search、*mid都指向单链表的头节点。
其中* search的移动速度是*mid的2倍。
当*search指向末尾节点的时候,mid正好就在中间了。
这也是标尺的思想。
代码实现:
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while (search->next != NULL)
{
//search移动的速度是 mid 的2倍
if (search->next->next != NULL)
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->data;
return OK;
}
题目2:实现随机生成20个元素的链表,快速查找中间结点的值并显示
#include "stdio.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList; /* 定义LinkList */
Status visit(ElemType c)
{
printf("%d ",c);
return OK;
}
/* 初始化顺序线性表 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */
if(!(*L)) /* 存储分配失败 */
{
return ERROR;
}
(*L)->next=NULL; /* 指针域为空 */
return OK;
}
/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next; /* p指向第一个结点 */
while(p)
{
i++;
p=p->next;
}
return i;
}
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(LinkList L)
{
LinkList p=L->next;
while(p)
{
visit(p->data);
p = p->next;
}
printf("\n");
return OK;
}
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化随机数种子 */
*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
r=*L; /* r为指向尾部的结点 */
for (i=0; i < n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
p->data = rand()%100+1; /* 随机生成100以内的数字 */
r->next=p; /* 将表尾终端结点的指针指向新结点 */
r = p; /* 将当前的新结点定义为表尾终端结点 */
}
r->next = NULL; /* 表示当前链表结束 */
// 创建有环链表
//r->next = p;
}
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while (search->next != NULL)
{
//search移动的速度是 mid 的2倍
if (search->next->next != NULL)
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->data;
return OK;
}
int main()
{
LinkList L;
Status i;
char opp;
ElemType e;
int find;
int tmp;
i=InitList(&L);
printf("初始化L后:ListLength(L)=%d\n",ListLength(L));
printf("\n1.查看链表 \n2.创建链表(尾插法) \n3.链表长度 \n4.中间结点值 \n0.退出 \n请选择你的操作:\n");
while(opp != '0')
{
scanf("%c",&opp);
switch(opp)
{
case '1':
ListTraverse(L);
printf("\n");
break;
case '2':
CreateListTail(&L,20);
printf("整体创建L的元素(尾插法):\n");
ListTraverse(L);
printf("\n");
break;
case '3':
//clearList(pHead); //清空链表
printf("ListLength(L)=%d \n",ListLength(L));
printf("\n");
break;
case '4':
//GetNthNodeFromBack(L,find,&e);
GetMidNode(L, &e);
printf("链表中间结点的值为:%d\n", e);
//ListTraverse(L);
printf("\n");
break;
case '0':
exit(0);
}
}
}
网友评论