个人感觉用不到,当个了解知识吧,遇到再说....写了非常久,感觉是所有类型链表里面最难的!!接近花了接近半天,有很多逻辑还是比较绕的,写了静态链表才发现,C语言的指针真是个好东西!!!pS:双向静态链表不写了,真的脑子要炸开了.....
一、静态链表的 结点/链表
- 结点与单向链表结点一致
- 链表多了一个free游标指向下一个赋值的位置
- 相较于动态链表,静态链表的结点/空间是固定好的,所以定义了一个MAX_SIZE表示空间量
typedef struct {
int data; // 数据域
int next; // 指针域(索引)
} ListNode;
typedef struct {
ListNode nodes[MAX_SIZE]; // 节点数组
int head; // 头节点的索引
int len; // 当前链表的长度
int free; // 游标,指向下一个空闲位置
} StaticLinkedList;
二、初始化静态链表
2.1、核心代码实现:
- head置空,len置空,空闲结点置0
- 将所有位置连接为一个空闲链表 相当于规划了一整块的连续内容空间来实现
- 最后一个空闲节点指向 -1,表示没有更多空闲位置
//初始化静态链表
void Init_StaticLinkedList(StaticLinkedList* list)
{
list->head = -1; // 初始时链表为空,head为-1表示 无有效元素
list->len = 0;
list->free = 0; // 空闲节点从数组的第0个元素开始
for (int i = 0; i < MAX_SIZE - 1; i++)
{
list->nodes[i].next = i + 1; // 将所有位置连接为一个空闲链表 相当于规划了一整块的连续内容空间来实现
}
list->nodes[MAX_SIZE - 1].next = -1; // 最后一个空闲节点指向-1,表示没有更多空闲位置
}
三、销毁链表
- head=-1(头指针置空)
- 长度置0
- 游标置空(注意置为0,这样下次使用时,赋值仍然是以0开始)
//销毁链表
void Destroy_StaticLinkedList(StaticLinkedList* list)
{
list->head = -1; //无有效元素,相当于头指针置空
list->len = 0; //长度置空
list->free = 0; //游标置空,相当与头指针next置空
}
四、创建节点
4.1、核心代码实现:
- 因为在静态链表中不存在 开辟空间 这样的概念,所以创建节点 就是给数组中的(以游标作为下标的)元素元素赋值即可
- 直接返回游标的值即可
- 注意返回时还应该刷新一次游标的值,使得下一次游标的值合法
//创建节点
//因为在静态链表中不存在开辟空间这样概念,所以创建节点就是 给 数组中的元素赋值即可
int Malloc_Node(StaticLinkedList* list)
{
if (list->free == -1)
{
printf("内存不足!\n");
return -1;
}
int index = list->free; // 表明获取下一个 空闲位置的数组下标
list->free = list->nodes[index].next; //再规定一下我插入这个元素数组下标的游标
return index; //返回下标即可
}
4.2、算法图解:
五、释放节点
5.1、核心代码实现:
//删除链表中的节点后,将该节点归还给空闲链表(即恢复该节点可被再次使用的状态)
void Free_Node(StaticLinkedList* list, int index)
{
list->nodes[index].next = list->free; // 将该节点的 next 指针指向当前空闲节点链表的头
list->free = index; //更新 free 指针,指向这个刚刚释放的节点
}
5.2、算法图解:
ps:此时的节点3应该与1/2都无任何联系,图忘记改了...
- 这样,节点 3 被成功归还到空闲链表中,且成为新的头节点。
- 这个函数的作用类似于动态链表中的 free 函数,用于释放节点但不是释放内存,而是将节点放回空闲节点池中
六、插入元素
6.1、核心代码实现
// 在链表的第i个位置插入一个元素
void Insert_StaticLinkedList(StaticLinkedList* list, int i, int value)
{
if (i < 0 || i > list->len)
{
printf("插入失败,位置不合法!\n");
return;
}
int newIndex = Malloc_Node(list); // 从空闲链表中取出一个下一个赋值节点的下标
if (newIndex == -1) return; // 若分配失败,退出
list->nodes[newIndex].data = value; // 设置新节点的值
if (i == 0)
{ // 头插法
list->nodes[newIndex].next = list->head; //让新节点指向头节点
list->head = newIndex; //让头节点指向新节点
}
else
{
int current = list->head; //定义一个迭代器指向头节点
for (int j = 0; j < i - 1; j++)
{
current = list->nodes[current].next; //找到插入元素的前一个节点
}
list->nodes[newIndex].next = list->nodes[current].next;//让新的节点指向当前节点的后一个节点,
list->nodes[current].next = newIndex; //让当前节点指向新节点
}
list->len++;
}
6.2、算法图解
6.2.1、头插法
- 创建一个节点(即获取游标节点下标index),给index下标的节点赋值
- 更新index节点的next,使其指向头节点
- 头节点移动到index上
6.2.2、尾插法
-
创建一个节点(即获取游标节点下标index),给index下标的节点赋值
-
定义一个迭代器cur指向头节点,并且让它移动到要插入元素下标的前一个结点
-
新结点index指向cur的下一个结点
-
让cur指向index结点
ps:有小伙伴询问,3/4步骤是否可以调换?? 答案当然是不行的
如果你先让cur去指向index的话,就会导致index无法找到它应该连接的下一个结点,也就是找不到结点4,因为此时2->next已经被修改成了7,自然就错啦!!!!
七、删除元素:
7.1、核心代码实现:
//删除元素
void Delete_StaticLinkedList(StaticLinkedList* list, int i)
{
if (i < 0 || i >= list->len)
{
printf("删除失败,位置不合法!\n");
return;
}
int toDelete; //定义一个toDelete,用来释放和检索要删除的下标
if (i == 0) //删除头节点
{
toDelete = list->head; //让删除下标指向头节点
list->head = list->nodes[toDelete].next; //头节点往后移动一位
}
else
{
int current = list->head; //定义一个迭代器,指向头节点
for (int j = 0; j < i - 1; j++)
{
current = list->nodes[current].next; //找到删除元素的前一个节点
}
toDelete = list->nodes[current].next; //让删除检索指向要删除的节点
list->nodes[current].next = list->nodes[toDelete].next;//跃过删除节点
}
Free_Node(list, toDelete);
list->len--;
}
7.2、算法图解
7.2.1、头删法
- 定义一个删除迭代器toDelete ,并让其指向index=0 (用来释放和检索要删除的下标)
- 让头节点移动到删除节点的next节点(即节点1,这样节点1就变成了首节点)
- 释放toDelete所指向的节点
7.2.2、尾删法
- 定义一个删除迭代器toDelete (用来释放和检索要删除的下标)
- 定义一个迭代器cur让它指向头节点,并且遍历到找到要删除元素的前一个结点
- 让删除检索指向要删除的结点(cur->next)
- 让cur的next越过toDelete指向toDelete的下一个结点
- 释放toDelete结点
八、根据下标查找元素
// 根据下标查找节点
int Find_Index_StaticLinkedList(StaticLinkedList* list, int i)
{
if (i < 0 || i >= list->len)
{
printf("查找失败,位置不合法!\n");
return -1;
}
int current = list->head; // 从头节点开始
for (int j = 0; j < i; j++)
{
current = list->nodes[current].next; // 按照 next 索引遍历
}
return current; // 返回找到的节点索引
九、根据元素来查找元素
// 根据值查找节点
int Find_Value_StaticLinkedList(StaticLinkedList* list, int value)
{
int current = list->head; // 从头节点开始
while (current != -1) // 当遍历到 -1 时,说明链表结束,
{
if (list->nodes[current].data == value)
{
return current; // 找到匹配值的节点,返回其索引
}
current = list->nodes[current].next; // 继续往下查找
}
return -1; // 没有找到匹配的值
}
十、遍历打印
//遍历打印
void Print_StaticLinkedList(StaticLinkedList* list)
{
int current = list->head;
while (current != -1)
{
printf("%d -> ", list->nodes[current].data);
current = list->nodes[current].next;
}
printf("NULL\n");
}
十一、完整代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#define MAX_SIZE 100 //静态链表的最大容量
typedef struct {
int data; // 数据域
int next; // 指针域(索引)
} ListNode;
typedef struct {
ListNode nodes[MAX_SIZE]; // 节点数组
int head; // 头节点的索引
int len; // 当前链表的长度
int free; // 游标,指向下一个空闲位置
} StaticLinkedList;
//初始化静态链表
void Init_StaticLinkedList(StaticLinkedList* list)
{
list->head = -1; // 初始时链表为空,head为-1表示 无有效元素
list->len = 0;
list->free = 0; // 空闲节点从数组的第0个元素开始
for (int i = 0; i < MAX_SIZE - 1; i++)
{
list->nodes[i].next = i + 1; // 将所有位置连接为一个空闲链表 相当于规划了一整块的连续内容空间来实现
}
list->nodes[MAX_SIZE - 1].next = -1; // 最后一个空闲节点指向 -1,表示没有更多空闲位置
}
//销毁链表
void Destroy_StaticLinkedList(StaticLinkedList* list)
{
list->head = -1; //无有效元素,相当于头指针置空
list->len = 0; //长度置空
list->free = 0; //游标置空,相当与头指针next置空
}
//创建节点
//因为在静态链表中不存在开辟空间这样概念,所以创建节点就是 给 数组中的元素赋值即可
int Malloc_Node(StaticLinkedList* list)
{
if (list->free == -1)
{
printf("内存不足!\n");
return -1;
}
int index = list->free; // 表明获取下一个 空闲位置的数组下标
list->free = list->nodes[index].next; //再规定一下我插入这个元素数组下标的游标
return index; //返回下标即可
}
//释放链表中的节点后,将该节点归还给空闲链表(即恢复该节点可被再次使用的状态)
void Free_Node(StaticLinkedList* list, int index)
{
list->nodes[index].next = list->free; // 将该节点的 next 指针指向当前空闲节点链表的头
list->free = index; //更新 free 指针,指向这个刚刚释放的节点
}
//这样,节点 3 被成功归还到空闲链表中,且成为新的头节点。
//这个函数的作用类似于动态链表中的 free 函数,用于释放节点但不是释放内存,而是将节点放回空闲节点池中。
// 在链表的第i个位置插入一个元素
void Insert_StaticLinkedList(StaticLinkedList* list, int i, int value)
{
if (i < 0 || i > list->len)
{
printf("插入失败,位置不合法!\n");
return;
}
int newIndex = Malloc_Node(list); // 从空闲链表中取出一个下一个赋值节点的下标
if (newIndex == -1) return; // 若分配失败,退出
list->nodes[newIndex].data = value; // 设置新节点的值
if (i == 0)
{ // 头插法
list->nodes[newIndex].next = list->head; //让新节点指向头节点
list->head = newIndex; //让头节点指向新节点
}
else
{
int current = list->head; //定义一个迭代器指向头节点
for (int j = 0; j < i - 1; j++)
{
current = list->nodes[current].next; //找到插入元素的前一个节点
}
list->nodes[newIndex].next = list->nodes[current].next;//让新的节点指向当前节点的后一个节点,
list->nodes[current].next = newIndex; //让当前节点指向新节点
}
list->len++;
}
//删除元素
void Delete_StaticLinkedList(StaticLinkedList* list, int i)
{
if (i < 0 || i >= list->len)
{
printf("删除失败,位置不合法!\n");
return;
}
int toDelete; //定义一个toDelete,用来释放和检索要删除的下标
if (i == 0) //删除头节点
{
toDelete = list->head; //让删除下标指向头节点
list->head = list->nodes[toDelete].next; //头节点往后移动一位
}
else
{
int current = list->head; //定义一个迭代器,指向头节点
for (int j = 0; j < i - 1; j++)
{
current = list->nodes[current].next; //找到删除元素的前一个节点
}
toDelete = list->nodes[current].next; //让删除检索指向要删除的节点
list->nodes[current].next = list->nodes[toDelete].next;//跃过删除节点
}
Free_Node(list, toDelete);
list->len--;
}
// 根据下标查找节点
int Find_Index_StaticLinkedList(StaticLinkedList* list, int i)
{
if (i < 0 || i >= list->len)
{
printf("查找失败,位置不合法!\n");
return -1;
}
int current = list->head; // 从头节点开始
for (int j = 0; j < i; j++)
{
current = list->nodes[current].next; // 按照 next 索引遍历
}
return current; // 返回找到的节点索引
}
// 根据值查找节点
int Find_Value_StaticLinkedList(StaticLinkedList* list, int value)
{
int current = list->head; // 从头节点开始
while (current != -1) // 当遍历到 -1 时,说明链表结束,
{
if (list->nodes[current].data == value)
{
return current; // 找到匹配值的节点,返回其索引
}
current = list->nodes[current].next; // 继续往下查找
}
return -1; // 没有找到匹配的值
}
//遍历打印
void Print_StaticLinkedList(StaticLinkedList* list)
{
int current = list->head;
while (current != -1)
{
printf("%d -> ", list->nodes[current].data);
current = list->nodes[current].next;
}
printf("NULL\n");
}
int main()
{
StaticLinkedList list;
Init_StaticLinkedList(&list);
Insert_StaticLinkedList(&list, 0, 10);
Insert_StaticLinkedList(&list, 1, 20);
Insert_StaticLinkedList(&list, 2, 30);
Insert_StaticLinkedList(&list, 3, 40);
printf("Original List:\n");
Print_StaticLinkedList(&list);
// 根据下标查找
int index = Find_Index_StaticLinkedList(&list, 2);
if (index != -1)
{
printf("Index 2 节点的值是: %d\n", list.nodes[index].data);
}
// 根据值查找
int found = Find_Value_StaticLinkedList(&list, 30);
if (found != -1)
{
printf("找到值为 30 的节点,位置是: %d\n", found);
}
else
{
printf("没有找到值为 30 的节点\n");
}
system("pause");
return 0;
}
链表系列全部结束,后面还会有栈/队列奥,欢迎大家勘误