算法与数据结构之又臭又长的表

admin 2026-04-02 05:29:45 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文主要介绍了线性表中的两种存储方式:顺序表和链表,并重点讲解了单链表。文中提供了Python和C语言的单链表实现代码,涵盖了节点定义、初始化、判空、长度计算、遍历、头插法、尾插法、指定位置插入、删除节点和查找等核心操作。 综合评分: 60 文章分类: 其他


cover_image

算法与数据结构之又臭又长的表

原创

海鸥 海鸥

书中自有代码来

2026年3月29日 16:36 四川

线性表

类似的列表称为有限列表,其中:

是的直接前驱,但没有,是的直接后继,但没有,长度为,长度为0时称为空表。

两种存储方式

顺序表

逻辑、物理位置均相邻的,称为顺序表。

示例:python的list、tuple,其他语言的数组

链表

类别

1.单链表

示例代码分析:

  1. python版:
###############################  定义单链表的结点    ###############################
classNode(object):                  # 定义节点类
    def__init__(self, elem):        # 定义构造函数,传入相应参数,初始化链表
        self.elem = elem             # 给数据域赋值,即将elem赋值给self.elem
        self.next = None             # 初始设置下一节点为空
###############################  定义单链表相关函数  ###############################
classSingleLinkList(object):        # 创建单链表
    def__init__(self, node=None):   # 使用一个默认参数,传入头结点接收;没有传入时,默认头结点为空
        self.__head = node           # 将传入的node值赋值给self.__head,即链表的头部
    defis_empty(self):              # 编写相关函数便于判断链表是否为空
        returnself.__head == None   # 返回self.__head是否为None的布尔值,如果为True,则为空链表
    deflength(self):                # 编写相关函数计算链表长度
        cur = self.__head            # 初始化cur游标,用来移动遍历节点,起始位置位于self.__head
        count = 0                    # 初始化用于记录变量的count的值
        while cur != None:           # 当cur获取的值不为None时,即后续还有结点,进入计数循环
            count += 1               # 记录不为空的结点数,总记录+1
            cur = cur.next           # 移动游标,指向下一个结点的位置
        return count                 # 循环完成,即cur为None,后续无结点时,返回count的值
    deftravel(self):                # 定义函数遍历整个列表,输出元素内容
        cur = self.__head            # 初始化cur游标,用来移动遍历节点,起始位置位于self.__head
        while cur != None:           # 当cur获取的值不为None时,即后续还有结点,进入遍历循环
            print(cur.elem, end=' ') # 打印结点的数据域的内容,结尾设置为空格便于输出大量内容
            cur = cur.next           # 移动游标,指向下一个结点的位置
        print("\n")                  # 遍历完成,输出换行符,便于后续输出
    defadd(self, item):             # 定义链表头部添加元的相关函数
        node = Node(item)            # 使用Node类初始化node结点,传入参数item
        node.next = self.__head      # 将该结点的尾部指针域指向原链表的头部,即将node.next赋值为self.__head
        self.__head = node           # 将新链表的头部指向该元素,即self.__head指向node结点
    defappend(self, item):          # 定义向链表尾部添加元素
        node = Node(item)            # 由于特殊情况当链表为空时没有next,所以在前面要做个判断
        ifself.is_empty():          # 使用之前判断空链表的的函数进行判断是否为空链表
            self.__head = node       # 如果是,则将该结点设置为首结点
        else:                        # 否则
            cur = self.__head        # 移动游标,遍历链表
            while cur.next != None:  # 遍历链表,直到结尾
                cur = cur.next       # 移动游标到下一个结点
            cur.next = node          # 将原链表尾部结点的指针域指向新的结点
    definsert(self, pos, item):     # 编写函数实现在指定位置添加元素
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;pos <=&nbsp;0: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 如果pos位置在0,当做头插法
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.add(item) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 使用之前编写的头插法函数
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;elif&nbsp;pos >&nbsp;self.length() -&nbsp;1:# 如果pos位置比原链表长,那么都当做尾插法来做
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.append(item) &nbsp; &nbsp; &nbsp; &nbsp;# 使用之前编写的尾插法函数
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 如果都不是
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; per =&nbsp;self.__head &nbsp; &nbsp; &nbsp; &nbsp;# 将插入点移动到链表头部
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count =&nbsp;0&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 初始化计数变量,记录游标走过的数量
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;count < pos -&nbsp;1: &nbsp;&nbsp;# 如果计数结果小于插入点,则继续移动游标
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count +=&nbsp;1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 计数器自增
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; per = per.next&nbsp; &nbsp; &nbsp; &nbsp;# 移动游标到下一个位置,即当循环退出后,pre指向pos-1位置
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; node = Node(item) &nbsp; &nbsp; &nbsp; &nbsp;# 初始化结点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; node.next&nbsp;= per.next&nbsp; &nbsp; &nbsp;# 将目标插入点的后面一个元素的位置保存到新节点的指针域,即新节点的下一个结点是插入点的下一个元素
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; per.next&nbsp;= node &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 插入结点的数据域,让前一个结点指向新节点
&nbsp; &nbsp;&nbsp;defremove(self, item): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 编写函数实现删除节点的功能
&nbsp; &nbsp; &nbsp; &nbsp; cur =&nbsp;self.__head &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 将游标移动到开头
&nbsp; &nbsp; &nbsp; &nbsp; pre =&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 定义删除点变量,初始化为None
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;cur !=&nbsp;None: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 遍历链表
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;cur.elem == item: &nbsp; &nbsp;&nbsp;# 如果数据匹配,找到该结点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;cur ==&nbsp;self.__head:# 如果游标在头节点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.__head = cur.next# 更改头节点的指向,指向原链表头节点的下一个结点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 否则,继续遍历
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pre.next&nbsp;= cur.next#修改前一个结点的指针域指向,跳过要删除的结点,达到删除的效果
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 完成操作后,跳出循环
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 如果没找到,继续遍历,直到找到
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pre = cur &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 移动待删除的结点的标记
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cur = cur.next&nbsp; &nbsp; &nbsp; &nbsp;# 移动游标到下一个结点
&nbsp; &nbsp;&nbsp;defsearch(self, item): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 编写函数实现查找节点是否存的功能
&nbsp; &nbsp; &nbsp; &nbsp; cur =&nbsp;self.__head &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 移动游标到链表的开头
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;whilenot&nbsp;cur: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 当游标没有在末尾时,继续遍历
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;cur.elem == item: &nbsp; &nbsp;&nbsp;# 如果成功查找到结点
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 返回布尔值True,代表找到
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 否则
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cur = cur.next&nbsp; &nbsp; &nbsp; &nbsp;# 移动游标到下一个元素,继续查找
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnFalse&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 如果遍历整个链表都没找到,返回布尔值False,代表未找到该元素
############################### &nbsp;运行代码 &nbsp;###############################
if&nbsp;__name__ ==&nbsp;"__main__": &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 程序入口,开始执行代码
&nbsp; &nbsp; ll = SingleLinkList() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 实例化类,初始化链表ll
&nbsp; &nbsp;&nbsp;print(ll.is_empty()) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 判断是否为空列表
&nbsp; &nbsp;&nbsp;print(ll.length()) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 获取链表长度
&nbsp; &nbsp; ll.append(3) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 向链表尾部添加结点,值为3
&nbsp; &nbsp; ll.add(999) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 向链表头部添加结点,值为999
&nbsp; &nbsp; ll.insert(-3,&nbsp;110) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 向链表添加一个结点,位置是-3,即倒数第三个前面,值为110
&nbsp; &nbsp; ll.insert(99,&nbsp;111) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 向链表添加一个结点,位置是99,但没有99个结点,则添加到链表末尾,值为111
&nbsp; &nbsp;&nbsp;print(ll.is_empty()) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 再次判断链表是否为空链表
&nbsp; &nbsp;&nbsp;print(ll.length()) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 再次获取链表长度
&nbsp; &nbsp; ll.travel() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 遍历并打印当前链表的所有结点的值
&nbsp; &nbsp; ll.remove(111) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 移除值为111的结点
&nbsp; &nbsp; ll.travel() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 再次遍历,查看删除是否成功
  1. c语言版:
#include&nbsp;<stdio.h>
#include&nbsp;<stdlib.h>
/*========定义链表结点结构========*/
typedefstruct&nbsp;node&nbsp;{
&nbsp; &nbsp;&nbsp;/*定义数据域,这里采用整型变量演示*/
&nbsp; &nbsp;&nbsp;int&nbsp;item;
&nbsp; &nbsp;&nbsp;/*定义指针域,链表一般指向下一个节点的位置*/
&nbsp; &nbsp;&nbsp;struct&nbsp;node&nbsp;*&nbsp;next;
} Node;

/*========初始化链表函数========*/
Node *&nbsp;initLinkList(int&nbsp;totalNode)
{
&nbsp; &nbsp;&nbsp;/*定义头指针,当前还没有结点,暂时为NULL*/
&nbsp; &nbsp; Node * head =&nbsp;NULL;
&nbsp; &nbsp;&nbsp;/*定义头节点,使用malloc开辟合适的空间*/
&nbsp; &nbsp; Node * headNode = (Node *)malloc(sizeof(Node));
&nbsp; &nbsp;&nbsp;/*为头节点的数据域填充数据*/
&nbsp; &nbsp; headNode->item =&nbsp;0;
&nbsp; &nbsp;&nbsp;/*将头节点的指针域指向下一个结点,当前没有,暂时为NULL*/
&nbsp; &nbsp; headNode->next =&nbsp;NULL;
&nbsp; &nbsp;&nbsp;/*将头指针指向头结点*/
&nbsp; &nbsp; head = headNode;
&nbsp; &nbsp;&nbsp;/*定义一个临时变量作为游标,初始化指向头节点*/
&nbsp; &nbsp; Node * cur = headNode;
&nbsp; &nbsp;&nbsp;/*根据传入的结点数初始化链表的每个结点*/
&nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;1; i < totalNode; i++) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*根据结点所占的大小分配空间*/
&nbsp; &nbsp; &nbsp; &nbsp; Node * eachNode = (Node *)malloc(sizeof(Node));
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*为了简便,我们直接使用循环变量赋值,当然也可以用其他的*/
&nbsp; &nbsp; &nbsp; &nbsp; eachNode->item = i;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*每个结点在当次循环中均是最后一个,所以next指向NULL*/
&nbsp; &nbsp; &nbsp; &nbsp; eachNode->next =&nbsp;NULL;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*将当前结点的位置赋值给上一个结点的指针域*/
&nbsp; &nbsp; &nbsp; &nbsp; cur->next = eachNode;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*修改游标,指向当前结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur->next;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;/*返回头节点的位置的指针*/
&nbsp; &nbsp;&nbsp;return&nbsp;head;
}
intinsectItem(Node * head,&nbsp;int&nbsp;item,&nbsp;int&nbsp;position)
{
&nbsp; &nbsp;&nbsp;/*定义一个插入结点,初始化指向空*/
&nbsp; &nbsp; Node * insectNode =&nbsp;NULL;
&nbsp; &nbsp;&nbsp;/*定义游标,初始化指向传入链表的头节点*/
&nbsp; &nbsp; Node * cur = head;
&nbsp; &nbsp;&nbsp;/*遍历链表,找到插入的位置所在的结点*/
&nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;1; i < position; i++) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*移动游标,指向下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur -> next;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*判断游标在给定位置范围内是否到头*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(NULL&nbsp;== cur) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*输出报错信息,跳出函数*/
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf_s("结点不存在!插入失败!\n");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return-1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;/*为插入的值建立结点,分配空间*/
&nbsp; &nbsp; insectNode = (Node *)malloc(sizeof(Node));
&nbsp; &nbsp;&nbsp;/*将传入的item的值赋值到新结点的数据域中*/
&nbsp; &nbsp; insectNode->item = item;
&nbsp; &nbsp;&nbsp;/*将插入的结点和后继结点连接上*/
&nbsp; &nbsp; insectNode->next = cur->next;
&nbsp; &nbsp;&nbsp;/*断开原链表前结点的连接,将前趋结点的下一个结点指向新节点*/
&nbsp; &nbsp; cur->next = insectNode;
&nbsp; &nbsp;&nbsp;return1;
}
intdeleteItem(Node * head,&nbsp;int&nbsp;item)
{
&nbsp; &nbsp;&nbsp;/*定义删除位置变量,游标变量指向链表头结点*/
&nbsp; &nbsp; Node * position,*cur = head;
&nbsp; &nbsp;&nbsp;/*定义变量记录是否查找到对应结点*/
&nbsp; &nbsp;&nbsp;int&nbsp;findOut =&nbsp;0;
&nbsp; &nbsp;&nbsp;/*遍历链表查找结点,直到结尾*/
&nbsp; &nbsp;&nbsp;while&nbsp;(cur ->next) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*判断游标的下一个结点的数据域是否是要查找的内容*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(item == cur->next->item) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*修改变量,标记为查找到对应结点,并跳出循环*/
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findOut =&nbsp;1;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*否则,指向下一个结点,继续遍历*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur ->next;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;/*如果未找到目标结点*/
&nbsp; &nbsp;&nbsp;if&nbsp;(0&nbsp;== findOut) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*输出报错信息并跳出函数*/
&nbsp; &nbsp; &nbsp; &nbsp; printf_s("结点不存在!删除失败!\n");
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return-1;
&nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*找到对应结点,将删除位置指向该节点*/
&nbsp; &nbsp; &nbsp; &nbsp; position = cur->next;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*将待删除结点的上一个结点的指针域指向待删除结点的下一个结点,断开待删除结点的两头连接*/
&nbsp; &nbsp; &nbsp; &nbsp; cur->next = cur->next->next;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*用free函数清理待删除结点的空间,删除对应结点*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;free(position);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return1;
&nbsp; &nbsp; }
}
intfindItem(Node * head,&nbsp;int&nbsp;item)
{
&nbsp; &nbsp;&nbsp;/*定义游标,指向头节点的下一个结点*/
&nbsp; &nbsp; Node * cur = head->next;
&nbsp; &nbsp;&nbsp;/*遍历链表查找指定元素*/
&nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;1; cur !=&nbsp;NULL; i++) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*如果游标所在结点的数据域和要查找的相同*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(cur->item == item) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*返回所在位置*/
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;i;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*移动游标到下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur->next;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return-1;
}
inteditItem(Node * head,&nbsp;int&nbsp;item,&nbsp;int&nbsp;newItem)
{
&nbsp; &nbsp;&nbsp;/*定义游标,指向头节点的下一个结点*/
&nbsp; &nbsp; Node * cur = head->next;
&nbsp; &nbsp;&nbsp;/*遍历链表查找指定元素*/
&nbsp; &nbsp;&nbsp;while&nbsp;(cur) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*如果游标所在结点的数据域和要查找的相同*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(cur->item == item) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*将数据域的数据替换为新数据*/
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cur ->item = newItem;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*返回所在位置*/
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*移动游标到下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur->next;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return-1;
}
voidtravel(Node * head)
{
&nbsp; &nbsp;&nbsp;/*定义游标,指向头节点的下一个结点*/
&nbsp; &nbsp; Node * cur = head->next;
&nbsp; &nbsp;&nbsp;/*遍历链表查找指定元素*/
&nbsp; &nbsp;&nbsp;while&nbsp;(cur) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*输出结点的数据域的数据*/
&nbsp; &nbsp; &nbsp; &nbsp; printf_s("%d ",cur->item);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*移动游标到下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur = cur->next;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;/*遍历输出完成,换行继续下一个函数*/
&nbsp; &nbsp; printf_s("\n");
}
voidfreeLinkList(Node * head)
{
&nbsp; &nbsp;&nbsp;/*定义变量,遍历链表进行清理*/
&nbsp; &nbsp; Node * freeNode =&nbsp;NULL;
&nbsp; &nbsp;&nbsp;/*定义游标,指向头节点的下一个结点*/
&nbsp; &nbsp; Node * cur = head->next;
&nbsp; &nbsp;&nbsp;/*当游标未到达链表尾部时*/
&nbsp; &nbsp;&nbsp;while(cur ->next)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*待清理的结点标记游标所在结点的下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; freeNode = cur->next;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*将游标的下一个结点指向原下一个结点的下一个结点*/
&nbsp; &nbsp; &nbsp; &nbsp; cur->next = cur->next->next;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/*调用free函数清理已标记的结点*/
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;free(freeNode);
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;/*当所有后继结点都清理完成后,清理头节点*/
&nbsp; &nbsp;&nbsp;free(head);
}
/*========程序入口========*/
intmain()
{
&nbsp; &nbsp; Node * headLinkListNode = initLinkList(5);
&nbsp; &nbsp; printf_s("初始化链表为:\n");
&nbsp; &nbsp; travel(headLinkListNode);
&nbsp; &nbsp;&nbsp;printf("在第 3 的位置上添加元素 6:\n");
&nbsp; &nbsp; insectItem(headLinkListNode,&nbsp;6,&nbsp;3);
&nbsp; &nbsp; travel(headLinkListNode);
&nbsp; &nbsp;&nbsp;printf("删除元素4:\n");
&nbsp; &nbsp; deleteItem(headLinkListNode,&nbsp;4);
&nbsp; &nbsp; travel(headLinkListNode);
&nbsp; &nbsp;&nbsp;printf("查找元素 2:\n");
&nbsp; &nbsp;&nbsp;printf("元素 2 的位置为:%d\n", findItem(headLinkListNode,&nbsp;2));
&nbsp; &nbsp;&nbsp;printf("更改元素 1 的值为 6:\n");
&nbsp; &nbsp; editItem(headLinkListNode,&nbsp;1,&nbsp;6);
&nbsp; &nbsp; travel(headLinkListNode);
&nbsp; &nbsp; freeLinkList(headLinkListNode);
&nbsp; &nbsp;&nbsp;return0;
}

2.循环链表

3.双向链表

相关操作

1. 前移策略

前移策略是一种基于局部性原理的自组织链表优化算法,其核心思想是“最近被访问的节点在将来很可能再次被访问”,因此通过将刚访问过的节点直接移动到链表头部,使得频繁访问的热点数据始终位于链表前端,从而大幅减少后续查找这些数据的平均遍历长度,提升整体访问效率。

实现步骤

  1. 遍历链表查找目标节点;
  2. 若找到目标节点且该节点不是头节点,则将其从原位置摘除;
  3. 将该节点插入到链表头部,更新头指针指向该节点。

Python代码

class&nbsp;Node:
&nbsp; &nbsp;&nbsp;def__init__(self, data):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.data = data
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.next&nbsp;=&nbsp;None

classMoveToFrontList:
&nbsp; &nbsp;&nbsp;def__init__(self):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.head =&nbsp;None

&nbsp; &nbsp;&nbsp;defsearch_and_move(self, key):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifnotself.head:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnFalse
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifself.head.data == key:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue

&nbsp; &nbsp; &nbsp; &nbsp; current =&nbsp;self.head
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;current.nextand&nbsp;current.next.data != key:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current = current.next

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifnot&nbsp;current.next:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnFalse

&nbsp; &nbsp; &nbsp; &nbsp; target = current.next
&nbsp; &nbsp; &nbsp; &nbsp; current.next&nbsp;= target.next
&nbsp; &nbsp; &nbsp; &nbsp; target.next&nbsp;=&nbsp;self.head
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.head = target
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue

C代码

#include&nbsp;<stdio.h>
#include&nbsp;<stdlib.h>

typedefstruct&nbsp;Node&nbsp;{
&nbsp; &nbsp;&nbsp;int&nbsp;data;
&nbsp; &nbsp;&nbsp;struct&nbsp;Node*&nbsp;next;
} Node;

typedefstruct&nbsp;{
&nbsp; &nbsp; Node* head;
} MoveToFrontList;

voidinit_list(MoveToFrontList*&nbsp;list)&nbsp;{
&nbsp; &nbsp;&nbsp;list->head =&nbsp;NULL;
}

intsearch_and_move(MoveToFrontList*&nbsp;list,&nbsp;int&nbsp;key)&nbsp;{
&nbsp; &nbsp;&nbsp;if&nbsp;(!list->head)&nbsp;return0;
&nbsp; &nbsp;&nbsp;if&nbsp;(list->head->data == key)&nbsp;return1;

&nbsp; &nbsp; Node* current =&nbsp;list->head;
&nbsp; &nbsp;&nbsp;while&nbsp;(current->next && current->next->data != key) {
&nbsp; &nbsp; &nbsp; &nbsp; current = current->next;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;if&nbsp;(!current->next)&nbsp;return0;

&nbsp; &nbsp; Node* target = current->next;
&nbsp; &nbsp; current->next = target->next;
&nbsp; &nbsp; target->next =&nbsp;list->head;
&nbsp; &nbsp;&nbsp;list->head = target;
&nbsp; &nbsp;&nbsp;return1;
}

2. 交换策略

交换策略是另一种自组织链表优化方法,相比前移策略更加温和,它不将访问节点直接移到头部,而是仅与前驱节点交换位置,这样既能逐步将热点数据向链表前端移动,又能避免因单次偶然访问导致链表结构剧烈变动,适合访问频率分布相对均匀的场景。

实现步骤

  1. 遍历链表查找目标节点,同时记录其前驱节点;
  2. 若找到目标节点且该节点不是头节点,则交换目标节点与前驱节点;
  3. 若目标节点是头节点,则无需操作。

Python代码

class&nbsp;Node:
&nbsp; &nbsp;&nbsp;def__init__(self, data):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.data = data
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.next&nbsp;=&nbsp;None

classTransposeList:
&nbsp; &nbsp;&nbsp;def__init__(self):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.head =&nbsp;None

&nbsp; &nbsp;&nbsp;defsearch_and_transpose(self, key):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifnotself.head&nbsp;orself.head.data == key:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue

&nbsp; &nbsp; &nbsp; &nbsp; prev =&nbsp;self.head
&nbsp; &nbsp; &nbsp; &nbsp; current =&nbsp;self.head.next

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;current&nbsp;and&nbsp;current.data != key:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prev = current
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current = current.next

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifnot&nbsp;current:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnFalse

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 交换数据域
&nbsp; &nbsp; &nbsp; &nbsp; prev.data, current.data = current.data, prev.data
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue

C代码

#include&nbsp;<stdio.h>
#include&nbsp;<stdlib.h>

typedefstruct&nbsp;Node&nbsp;{
&nbsp; &nbsp;&nbsp;int&nbsp;data;
&nbsp; &nbsp;&nbsp;struct&nbsp;Node*&nbsp;next;
} Node;

typedefstruct&nbsp;{
&nbsp; &nbsp; Node* head;
} TransposeList;

voidinit_list(TransposeList*&nbsp;list)&nbsp;{
&nbsp; &nbsp;&nbsp;list->head =&nbsp;NULL;
}

intsearch_and_transpose(TransposeList*&nbsp;list,&nbsp;int&nbsp;key)&nbsp;{
&nbsp; &nbsp;&nbsp;if&nbsp;(!list->head ||&nbsp;list->head->data == key)&nbsp;return1;

&nbsp; &nbsp; Node* prev =&nbsp;list->head;
&nbsp; &nbsp; Node* current =&nbsp;list->head->next;

&nbsp; &nbsp;&nbsp;while&nbsp;(current && current->data != key) {
&nbsp; &nbsp; &nbsp; &nbsp; prev = current;
&nbsp; &nbsp; &nbsp; &nbsp; current = current->next;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;if&nbsp;(!current)&nbsp;return0;

&nbsp; &nbsp;&nbsp;// 交换数据域
&nbsp; &nbsp;&nbsp;int&nbsp;temp = prev->data;
&nbsp; &nbsp; prev->data = current->data;
&nbsp; &nbsp; current->data = temp;
&nbsp; &nbsp;&nbsp;return1;
}

3. 环路检测与环路断开

基于弗洛伊德判圈算法,利用快慢指针的速度差检测环路:若链表存在环,快指针(每次走两步)最终会在环内追上慢指针(每次走一步);若链表无环,快指针会先到达链表末尾(NULL),通过判断两指针是否相遇即可确定是否存在环路。

实现步骤

  1. 初始化快慢指针均指向链表头节点;
  2. 循环移动指针:慢指针每次前进一步,快指针每次前进两步;
  3. 若快指针或其下一节点为NULL,说明无环;
  4. 若快慢指针相遇,说明存在环路。

Python代码

class&nbsp;Node:
&nbsp; &nbsp;&nbsp;def__init__(self, data):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.data = data
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.next&nbsp;=&nbsp;None

defhas_cycle(head):
&nbsp; &nbsp;&nbsp;ifnot&nbsp;head&nbsp;ornot&nbsp;head.next:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnFalse

&nbsp; &nbsp; slow = head
&nbsp; &nbsp; fast = head

&nbsp; &nbsp;&nbsp;while&nbsp;fast&nbsp;and&nbsp;fast.next:
&nbsp; &nbsp; &nbsp; &nbsp; slow = slow.next
&nbsp; &nbsp; &nbsp; &nbsp; fast = fast.next.next

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;slow == fast:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;returnTrue

&nbsp; &nbsp;&nbsp;returnFalse

C代码

#include&nbsp;<stdio.h>
#include&nbsp;<stdlib.h>

typedefstruct&nbsp;Node&nbsp;{
&nbsp; &nbsp;&nbsp;int&nbsp;data;
&nbsp; &nbsp;&nbsp;struct&nbsp;Node*&nbsp;next;
} Node;

inthas_cycle(Node* head)&nbsp;{
&nbsp; &nbsp;&nbsp;if&nbsp;(!head || !head->next)&nbsp;return0;

&nbsp; &nbsp; Node* slow = head;
&nbsp; &nbsp; Node* fast = head;

&nbsp; &nbsp;&nbsp;while&nbsp;(fast && fast->next) {
&nbsp; &nbsp; &nbsp; &nbsp; slow = slow->next;
&nbsp; &nbsp; &nbsp; &nbsp; fast = fast->next->next;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(slow == fast) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;return0;
}

4. 链表反转

通过迭代方式逐个反转节点的指针方向,将原链表中每个节点的next指针从指向后继节点改为指向前驱节点,最终使整个链表的遍历方向完全颠倒,核心是维护三个指针分别记录当前节点、前驱节点和临时保存的后继节点,避免链表断裂。

实现步骤

  1. 初始化前驱指针为NULL,当前指针为头节点;
  2. 循环处理每个节点:保存当前节点的下一节点,将当前节点的next指向前驱节点,前驱指针和当前指针依次后移;
  3. 当当前指针为NULL时,前驱指针即为新的头节点。

Python代码

class&nbsp;Node:
&nbsp; &nbsp;&nbsp;def__init__(self, data):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.data = data
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.next&nbsp;=&nbsp;None

defreverse_list(head):
&nbsp; &nbsp; prev =&nbsp;None
&nbsp; &nbsp; current = head

&nbsp; &nbsp;&nbsp;while&nbsp;current:
&nbsp; &nbsp; &nbsp; &nbsp; next_temp = current.next
&nbsp; &nbsp; &nbsp; &nbsp; current.next&nbsp;= prev
&nbsp; &nbsp; &nbsp; &nbsp; prev = current
&nbsp; &nbsp; &nbsp; &nbsp; current = next_temp

&nbsp; &nbsp;&nbsp;return&nbsp;prev

C代码

#include&nbsp;<stdio.h>
#include&nbsp;<stdlib.h>

typedefstruct&nbsp;Node&nbsp;{
&nbsp; &nbsp;&nbsp;int&nbsp;data;
&nbsp; &nbsp;&nbsp;struct&nbsp;Node*&nbsp;next;
} Node;

Node*&nbsp;reverse_list(Node* head)&nbsp;{
&nbsp; &nbsp; Node* prev =&nbsp;NULL;
&nbsp; &nbsp; Node* current = head;

&nbsp; &nbsp;&nbsp;while&nbsp;(current) {
&nbsp; &nbsp; &nbsp; &nbsp; Node* next_temp = current->next;
&nbsp; &nbsp; &nbsp; &nbsp; current->next = prev;
&nbsp; &nbsp; &nbsp; &nbsp; prev = current;
&nbsp; &nbsp; &nbsp; &nbsp; current = next_temp;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;return&nbsp;prev;
}

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:书中自有代码来 海鸥 海鸥《算法与数据结构之又臭又长的表》

评论:0   参与:  0