链表可以用malloc/new和结构体加指针的方式来实现,那种实现方式实现的链表又被称为动态链表。但是我们还可以利用数组的方式来实现一个链表,这种实现方式称为静态链表。
静态单链表
我们知道,一个链表节点主要由两大部分组成:数值和一个指向下一个节点的next指针。
而静态链表是用两个数组来分别存储每一个节点左边的数值和右边的指针,其具体的实现方式是用一个数组存储每个链表中的数值,另外一个数组用来存储第一个数组中的每个数值对应的下标(除首节点)。下面是一个按顺序从头节点开始创建的静态链表数组:
有两个数组后我们还需要一个引子来找到存放数值的数组,这就是数组的头节点。而头节点是不算入链表中的,但是我们的首节点需要被指向,所以我们需要另开一个变量HeadNode来存储它,并且还需要另开一个index变量来表示当前用于存储的数组存储到的位置(当前位置)。
代码形式表达如下(我想创建的是一个最多有100个节点的链表):
const int N = 100;
int HeadNode, val[N], Node[N], index;
接下来第二步就是需要将这个链表先初始化,因为初始时我们还没有开始创建第一个元素,所以此时第一个数组val是空的,第二个存放next指针的数组也是空的(里面没有开始存放下标)。这里我们让初始化时HeadNode变量存放下标-1(链表还没有任何首节点),当前数组未开始开辟所以当前位置index的下标为0。
void init()
{
HeadNode = -1;
index = 0;
}
当我们初始化完链表后,接下来就可以在链表的头节点后加上一个真正的节点了。开辟一个新的节点分为4步,第一步是将数值n存放到val数组里面,而当前val数组存储到index位置。第二步是将创建的节点指向头节点之前指向的位置(当创建第一个节点时,第一个节点next指针部分存放的为-1,因为一个链表最后一个节点(包括没有其它节点,只有头节点时)存放的数组下标为-1)。第三部分就是将头节点指向第一个节点,也就是头节点存放第一个节点在Node里面的下标(记住index是指向当前节点、Node[index]是当前节点的next指针的指向,而index才算当前节点的位置)。第四步就是将当前值加加以便下次存放数据即可。
具体实现如下:
void Head_add(int n)
{
val[index] = n;
Node[index] = HeadNode;
HeadNode = index;
index++;
}
接下来可以实现链表的删除操作,例如删除k节点指向的下一个节点,则传递的参数为k-1,本质就是将k节点指向它的下下个节点。如果删除的是首节点,需要额外讨论。对应的关系图如下:
用代码实现如下:
//删除节点
void Delete(int n)
{
if (n == 0)
{
HeadNode = Node[HeadNode];
}
else
{
Node[n] = Node[Node[n]];
}
}
最后就是静态链表的插入问题,在下标为n的节点后面插入一个新节点,原理和在头节点后面新建一个节点类似,都是分为4步:1、创建新节点,将数值x赋予给新节点;2、将新节点的指向修改为下标为n的节点的指向(下标为n的节点的下一个节点);3、将下标为n的节点的指向改为新节点;4、将index加加。注意与删除类似,这里传递的参数为n-1。
//任意插入节点
void Insert(int n, int x)
{
val[index] = x;
Node[index] = Node[n];
Node[n] = index;
index++;
}
总代码:
#include<iostream>
using namespace std;
const int N = 100;
int HeadNode, val[N], Node[N], index;
//初始化静态链表
void init()
{
HeadNode = -1;
index = 0;
}
//在头节点后再插入一个节点
void Head_add(int n)
{
val[index] = n;
Node[index] = HeadNode;
HeadNode = index;
index++;
}
//删除节点
void Delete(int n)
{
if (n == 0)
{
HeadNode = Node[HeadNode];
}
else
{
Node[n] = Node[Node[n]];
}
}
//任意插入节点
void Insert(int n, int x)
{
val[index] = x;
Node[index] = Node[n];
Node[n] = index;
index++;
}
int main()
{
init();
Head_add(2);
Head_add(0);
Head_add(3);
//Delete(0);
for (int i = HeadNode; i != -1; i=Node[i])
{
//cout << val[i] << ' ';
cout << Node[i] << ' ';
}
return 0;
}
静态双链表
双链表与单链表不同,双链表每个节点都存储三个部分的内容,第一个部分是一个指向上一个节点的指针,第二个部分是数值,第三个部分是一个指向右边的指针;双链表还包括两个端节点,它们不存储数值,一个只存放指向下一个节点的指针,一个只存放指向上一个节点的指针。
要想模拟实现双链表,我们需要三个数组,一个用于存储上一个节点的位置,一个用于存储下一个节点的位置,最后一个用于存储数值。所以我们开辟三个数组存放数值的Val数组、存放左边位置的LeftNode数组和存放右边位置的RightNode数组,同时写一个初始化函数init。初始化先创立左右两个节点,左边端节点右边指向右边端节点,右边端节点左边指向左边端节点,所以三个数组,下标为0我设置为左边端节点,下标为1设置为右边端节点,由于它们都没有数值,所以Val[0]、Val[1]可以不用设置。
const int N = 100;
int index, Val[N], LeftNode[N], RightNode[N];
void init()
{
LeftNode[1] = 0;
RightNode[0] = 1;
index = 2;
}
接下来就可以实现双链表的插入操作了,由于双链表是可以往回走的,所以它会有两种插入方式:左边插入和右边插入,这里我以右边插入为例。
我们要在下标为n的节点后再插入一个节点,它的数值为x,整个操作分为5步。
- 第一步是将数值赋予给新的节点,也就是先赋值Val[index]。
- 第二步是将新节点的右边指向下标为n+1的节点。
- 第三步是将新节点左边指向下标为n的节点。
- 第四步是将原来下标为n+1的节点左边指向新节点。
- 第五步是将原来下标为n节点的右边指向新节点。
void RightInsert(int n, int x)
{
Val[index] = x;
RightNode[index] = RightNode[n];
LeftNode[index] = n;
LeftNode[RightNode[n]] = index;
RightNode[n] = index;
}
然后就是删除操作,例如删除第n个点(不是下标为n,还有左端点和右端点各算一个,且不能删除),删除操作总的来说就是第n个点左边的点(点1)的右边指向它原来右边的点(点3);然后让其(点2)右边的点(点3)左边指向它原来左边的点(点1)
具体到代码实现如下:
//删掉下标为n的点
void Delete(int n)
{
LeftNode[RightNode[n]] = LeftNode[n];
RightNode[LeftNode[n]] = RightNode[n];
}
下面是实现先在左端点右边插入数字1,再在左端点右边插入数字2,最后再在下标为2的数字右边插入数字5,最后删除第4个节点。插入后为215,删除后为21。
int main()
{
init();
RightInsert(0, 1);
RightInsert(0, 2);
RightInsert(2, 5);
for (int i = RightNode[0]; i != 1; i = RightNode[i])
cout << Val[i] << " ";
cout<<endl;
Delete(4);
for (int i = RightNode[0]; i != 1;i = RightNode[i])
cout << Val[i] << " ";
return 0;
}
总代码:
#include<iostream>
using namespace std;
const int N = 100;
int index, Val[N], LeftNode[N], RightNode[N];
void init()
{
LeftNode[1] = 0;
RightNode[0] = 1;
index = 2;
}
void RightInsert(int n, int x)
{
Val[index] = x;
RightNode[index] = RightNode[n];
LeftNode[index] = n;
LeftNode[RightNode[n]] = index;
RightNode[n] = index;
index++;
}
//删掉第n个点
void Delete(int n)
{
LeftNode[RightNode[n]] = LeftNode[n];
RightNode[LeftNode[n]] = RightNode[n];
}
int main()
{
init();
RightInsert(0, 1);
RightInsert(0, 2);
RightInsert(2, 5);
for (int i = RightNode[0]; i != 1; i = RightNode[i])
cout << Val[i] << " ";
cout<<endl;
Delete(4);
for (int i = RightNode[0]; i != 1;i = RightNode[i])
cout << Val[i] << " ";
return 0;
}