静态链表
使用静态链表存储数据,需要预先申请足够大的一整块内存空间,也就是说,静态链表存储数据元素的个数从其创建的那一刻就已经确定,后期无法更改。
比如,如果创建静态链表时只申请存储 10 个数据元素的空间,那么在使用静态链表时,数据的存储个数就不能超过 10 个,否则程序就会发生错误。
不仅如此,静态链表是在固定大小的存储空间内随机存储各个数据元素,这就造成了静态链表中需要使用另一条链表(通常称为”备用链表”)来记录空间存储空间的位置,以便后期分配给新添加元素使用,如图 2 所示。
这意味着,如果你选择使用静态链表存储数据,你需要通过操控两条链表,一条是存储数据,另一条是记录空闲空间的位置。
动态链表
使用动态链表存储数据,不需要预先申请内存空间,而是在需要的时候才向内存申请。也就是说,动态链表存储数据元素的个数是不限的,想存多少就存多少。
同时,使用动态链表的整个过程,你也只需操控一条存储数据的链表。当表中添加或删除数据元素时,你只需要通过 malloc 或 free 函数来申请或释放空间即可,实现起来比较简单。
共同点
静态链表和动态链表的共同点是,数据之间”一对一”的逻辑关系都是依靠指针(静态链表中称”游标”)来维持,仅此而已。
划重点
:想进一步学习静态链表的构造思想,可参考
数据结构-静态链表
这篇博客,博主也是通过这篇blog对静态链表的结构和实现过程有了充分的学习
代码:静态链表
使用std::vector创建的静态链表代码如下
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Blog: https://blog.csdn.net/weixin_41234001
*
* Author: DoBetter
*
* Time: 2019.12.02
*
* Describe: 使用std::vector实现一个静态链表,包含插入,删除,查找功能
* 主要思路:通过在vector中定义两个链表,一个为备用链表:用来存储释放的结点空间,一个为数据链表:用来存储数据
* vector的第一个元素,即下标为0的元素的next就存放备用链表的第一个结点的下标;
* 而vector的最后一个元素的next则存放第一个有数值的结点的下标,相当于单链表的头节点作用,当整个链表为空时,则为mynull,表示无指向
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#include <iostream>
#include <vector>
#include <algorithm>
#define mynull -1//定义空值
//定义静态链表结构
typedef int dataType;
const int maxn = 1000;//静态链表的默认大小
struct node
{
dataType data;//数据域
int next;//指针域
};
//将链表中各分量链成备用链表
//list[0]为备用链表的头指针,list[maxn-1]为数据链表的头指针,“-1”表示为空
void initList(std::vector<node> &list)
{
printf("链表初始化\n");
for (int i=0;i<maxn-2;i++)
{
list[i].next = i + 1;
}
list[maxn - 2].next = mynull;//因为最后一个元素为链表头结点,所以其前一个结点的指针域为空
list[maxn - 1].next = mynull;//数据链表的初始头结点的next为空
return;
}
//返回备用链表的中的第一个结点的位置
int mallocEmptyNode(std::vector<node> &list)
{
int k = list[0].next;//备用链表的下一个结点地址
if (k!=mynull)//表示当前有空闲区域进行存储
{
list[0].next = list[k].next;//获取到下一个备用结点的地址
}
return k;
}
//将下标为pos的空闲结点回收到备用链表中
void freeEmptyNode(std::vector<node> list,int pos)
{
list[pos].next = list[0].next;
list[0].next = pos;
}
//获取链表长度
int listLength(std::vector<node> list)
{
int len = 0;
int p = list[maxn - 1].next;//第一个结点的地址
while (p!=mynull)
{
len++;
p = list[p].next;//后移
}
return len;
}
//插入结点到链表中
//在array中第pos元素之前插入新的数据元素elem
void listInsert(std::vector<node> &list, int pos, dataType elem)
{
//printf("插入元素:\n");
if (pos<1||pos>listLength(list)+1)
{
printf("插入位置不合法");
return;
}
int q = mallocEmptyNode(list);//获取分配的备用链表的空间
if (q!=mynull)
{
int p = maxn - 1;//数据链表的头结点位置
for (int i = 0; i < pos - 1; i++)//寻找插入位置
{
p = list[p].next;
}
list[q].next = list[p].next;
list[p].next = q;
list[q].data = elem;//赋值
}
}
//删除
//删除第pos个元素
void listDelete(std::vector<node> &list, int pos)
{
//printf("删除元素\n");
if (pos<1 || pos>listLength(list) + 1)
{
printf("删除位置不合法或当期没有元素\n");
return;
}
int p = maxn - 1;
for (int i=0;i<pos-1;i++)//寻找删除位置的前驱结点的地址
{
p = list[p].next;
}
int delPos = list[p].next;
list[p].next = list[list[p].next].next;//结点相连
freeEmptyNode(list, delPos);//释放结点
}
//遍历链表
void listTraverse(std::vector<node> list)
{
int p =list[maxn - 1].next;
while (p!=mynull)
{
printf("%d ", list[p].data);
p = list[p].next;//后移
if (p==mynull)
{
printf("\n");
}
}
}
//查找元素
void listSearch(std::vector<node> list,dataType elem)
{
int p = list[maxn - 1].next;
while (p != mynull)
{
if (elem==list[p].data)
{
printf("找到了元素\n");
return;
}
//printf("%d ", list[p].data);
p = list[p].next;//后移
}
printf("没找到\n");
}
int main()
{
//声明链表
std::vector<node> list(maxn);
int n;
initList(list);//初始化链表
//插入元素
printf("插入元素:\n");
for (int i=1;i<=20;i++)
{
listInsert(list, i, i * i);
}
//输出
listTraverse(list);
//查找元素
printf("查找元素4:\n");
listSearch(list, 4);//有该元素
printf("查找元素99:\n");
listSearch(list, 99);//无该元素
//删除元素
printf("删除第二个元素:\n");
listDelete(list, 2);
//输出
listTraverse(list);
return 0;
}
代码:动态链表
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Blog: https://blog.csdn.net/weixin_41234001
*
* Author: DoBetter
*
* Time: 2019.11.30
*
* Describe: 动态链表
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#include <iostream>
#include <algorithm>
#include <stdlib.h>
using namespace std;
typedef int dataType;
struct node
{
dataType data;//数据域
node* next;//指针域
};
//创建链表
node* create(int Array[])
{
node* p, * pre, * head;//p为新建结点,pre保存当前结点的前驱结点,head为头结点
head = new node;//创建头结点
head->next = NULL;//头结点不需要数据域,指针域初始为NULL
pre = head;//记录pre为head
for (int i=0;i<5;i++)
{
p = new node;//新建结点
//将Array[i]赋给新建的结点作为数据域,也可以scanf输入
p->data = Array[i];
p->next = NULL;//后继结点设为空
pre->next = p;//前驱结点的指针设为当前新建结点的地址
pre = p;//将pre移到新建结点上
}
return head;//返回头结点
}
//查找
void search(node* root, dataType x)
{
root = root->next;//从第一个结点开始
while (root!=NULL)
{
if (root->data==x)
{
printf("找到了该元素\n");
return;
}
root = root->next;//移动到第二个结点
}
printf("不存在该元素");
}
//插入
void insert(node* head, int pos, int x)//pos是位置,x为插入的元素
{
node* p = head;
for (int i=0;i<pos-1;i++)
{
p=p->next;//pos-1是要插入的位置前一个元素
}
node* temp=p->next;//保存p的后继结点
node* q = new node;//增加的新结点
q->data = x;//赋值
p->next = q;//p的下一个结点为新建结点q
q->next = temp;//新建结点的下一个结点为p的后继结点
}
//删除——删除链表中的所有x
void del(node* head, int x)
{
node* p, *pre;
pre = head;//指向p指向的结点的前驱结点
p = head->next;//从第一个结点开始找
while (p!=NULL)
{
if (p->data==x)//找到了元素x
{
pre->next = p->next;//pre的后继结点为p的后继结点
delete(p);//删除p所指向的结点
p = pre->next;//p指向pre的后继结点
}
else//未找到,则一直往后找
{
pre = p;
p = p->next;
}
}
}
//输出
void printList(node* head)
{
node* p = head->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;//切换到下一个结点
}
}
int main()
{
int Array[5];
for (int i=0;i<5;i++)
{
Array[i] = i * i;
}
//创建链表——该链表具有头结点
node* root = create(Array);
cout << "链表中的原始数据为" << endl;
//输出
printList(root);
cout << endl;
//插入
insert(root, 2, 666);
//输出
cout << "插入666后" << endl;
printList(root);
cout << endl;
//查找
search(root, 0);
//删除
del(root, 1);
//输出
cout << "删除1后" << endl;
//输出
printList(root);
return 0;
}
参考资料
版权声明:本文为weixin_41234001原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。