数据结构——线性表(静态链表、循环链表以及双向链表)
1、静态链表
用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。
静态链表需要对数组的第一个和最后一个元素作为特殊元素处理,不存数据。
最后一个指向第一个有数据的下标地址,第一个游标指向第一个没有数据的下标地址。
我们对数组的第一个和最后一个元素做特殊处理,他们的data不存放数据;
-我们通常把未使用的数组元素称为备用链表;
-数组的第一个元素,即下标为0的那个元素的cur就存放备用链表的第一个结点的下标;
-数组的最后一个元素,即下标为MAXSIZE-1的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点作用。
1.1静态链表的初始化
bool InitList(StaticLinkList list)
{int i;for(i=0;i<MAXSIZE;i++)list[i].cur = i+1;list[MAXSIZE-1].cur = 0; //当前链表为空,所以最后一个元素的cur为0 return true;
}
1.2静态链表的插入操作
首先是获得空闲分量的下标:
int Malloc_SLL(StaticLinkList space)
{int i = space[0].cur; //将第一个游标赋值给i,也就是第一个空闲的结点的下标赋值i。if(space[0].cur)space[0].cur = space[i].cur;return i;
}// 这段代码是给静态链表的第i-1个元素后添加新元素
bool ListInsert(StaticLinkList list,int i,ElemType e)
{int i,j,l;k = MAXSIZE-1;if(i < 1 || i > ListLength() + 1)return false;j = Malloc_SSL(L); //获取空闲下标 if(j){list[i].data = e; //将数据值赋值给要空闲位置 for(l = 1;l<=i-1;l++)k = list[k].cur; //找到第i-1个元素 list[i].cur = list[k].cur; //把第i-1个元素的cur赋值给新元素的cur list[i].cur = j; //把新元素的下标赋值给第i-1个元素的cur; return true;}return false;
}
1.3静态链表的删除操作
Status ListDelete(StaticLinkList L,int i)
{int j,k;if(i<1||i>ListLength(L)){return ERROR;}k = Max_SIZE-1;for(j=0;j<=i-1;j++){k = L[k].cur; //找出要删除元素的前一个元素的下标}j = L[k].cur; //找到要删除元素的下标L[k].cur = L[j].cur; //把要删除元素的游标赋值给其前一个元素的游标Free_SLL(L,j); //释放删除元素的内存return OK;
}
void Free_SLL(StaticLinkList space,int j)
{space[j].cur = space[0].cur;space[0] = j; //删除元素后,该位置变为备用链表表头,数组第一个元素的游标存储第一个空闲结点的下表
}
int ListLength(StaticLinkList L)
{int j = 0;int i = L[MaxSIZE-1].cur; //取出有数据的第一个元素的下标while(i){i = L[i].cur; //有数据的最后一个元素的游标为0,即为数组第一个元素的下标j++;}return j;
}
1.4静态链表的优缺点总结
优点:
在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点;
缺点:
没有解决连续存储分配(数组)带来的表长难以确定的问题;
失去了顺序存储结构随机存取的特性。
静态链表其实是为了给没有指针的编程语言设计的一种实现单链表功能的方法。
2、循环链表
将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。
2.1循环链表初始化
/*循环单链表初始化*/
Status Init_CLinklist(CLinklist *L)
{*L=(CLinklist)malloc(sizeof(CLNode)); //创建头结点 if(!(*L)) return ERROR; //创建失败返回ERROR (*L)->next=*L; //头结点指针指向本身 return OK;
}
2.2循环链表尾插法构建
/*循环单链表的尾插法构建,输入data,0的时候结束输入*/
void Create_CLinklist(CLinklist *L)
{CLNode *p,*q; //定义链尾节点和新生节点 int flag=1,number; //设立旗帜, p = *L;while(flag){scanf("%d",&number);if( number!=0 ){q=(CLinklist)malloc(sizeof(CLNode)); //创建新生节点 if(!q) exit(0); //创建失败结束程序 q->data=number;p->next=q; p=q; //这里和单链表的尾插法相同 }else{p->next = *L; //尾指针指向头结点 flag=0;}}}
2.3循环链表插入
/*循环单链表的插入,插入错误返回0,插入成功返回1*/
Status Insert_CLinklist(CLinklist *L,int i,Elemtype e)
{int j=1; CLNode *p,*q; //定义链尾节点和新生节点 p = *L;if(i<1||p->next==*L||i>Length_CLinklist(L)) return ERROR; //插入位置小于1,空表或大于表长则返回0 while( p->next!=*L && j<i ){p=p->next;j++;}q=(CLinklist)malloc(sizeof(CLNode));q->data=e;q->next=p->next;p->next=q;return OK;
}
2.4循环链表删除
/*循环单链表的删除,删除错误返回0,删除成功返回1,用e返回被删除的元素*/
Status Delete_CLinklist(CLinklist *L,int i,Elemtype *e)
{int j=1; CLNode *p,*q;p = *L;if(i<1||p->next==*L||i>Length_CLinklist(L)) return ERROR; //删除位置小于1,空表或大于表长则返回0while( p->next != *L && j<i){p=p->next;j++;}q=p->next;*e=q->data;p->next=q->next;free(q); //释放掉删除结点 return OK;
}
2.5返回结点所在的位置
/*返回结点所在的位置*/
int search_CLinklist(node *pNode, int elem)
{node *target;int i = 1;for(target = pNode; target->data != elem && target != pNode; ++i){target = target->next;}if(target->next == pNode) //表中不存在该元素return 0;else return i;
}
2.6约瑟夫问题
-
算法思想:
- 根据总人数n创建n个结点的循环链表模拟约瑟夫环,每个人为链表中的一个结点
- 工作指针从循环链表表头向后移动至第k个结点
- 从当前开始报数
- 若不满足出列条件,工作指针后移,继续报数
- 如满足出列条件,删除当前结点
-
算法描述:
//n个人围圈报数,报m出列,最后剩下的是几号?
#include <stdio.h>
#include <stdlib.h>typedef struct node
{int data;struct node* next;
}node;node* create(int n)
{node* p = NULL, * head;head = (node*)malloc(sizeof(node));p = head;node* s;int i = 1;if (0 != n){while (i <= n) /*构建循环链表*/{s = (node *)malloc(sizeof(node));s->data = i++;p->next = s;p = s;}s->next = head->next;//s指向第一个结点,形成循环链表}free(head); //释放辅助的头结点return s->next;
}
int main()
{int n = 41;int m = 3;int i;node* p = create(n);node* temp;m %= n;while (p != p->next){for (i = 1; i < m - 1; i++){p = p->next;}printf("%d->",p->next->data);temp = p->next;p->next = temp->next;free(temp);p = p->next;}printf("%d\n",p->data);return 0;
}
2.7例题
1、实现将两个线性表(a1,a2,...,an)和(b1,b2,...,bm)连接成一个线性表(a1,...,an,b1,...,bm)的运算。
分析:
-若在单链表或头指针表示的单循环链表上做这种链接操作,都需要遍历第一个链表,找到结点an,然后将结点b1链到an的后面,其执行时间是O(n)。
-若在尾指针表示的单循环链表上实现,则只需修改指针,无需遍历,其执行时间是O(1)。
//假设A,B为非空循环链表的尾指针
LinkList Connect(LinkList A, LinkList B)
{LinkList p = A->next; //保存A表的头结点位置A->next = B->next->next;//B表的开始结点链接到A表表尾free(B->next);//释放B表的头结点B->next = p;return B; //返回新循环链表的尾指针
}
2、判断单链表中是否有环。
- 有环的定义是,链表的尾结点指向了链表中的某个结点。
判断单链表中是否有环,主要有以下两种方法。
方法一:
使用p、q两个指针,p总是向前走,但q每次都从头开始走,对于每个结点,看p走的步数是否和q一样。如图,当p从6走到3时,用了6步,此时若q从head出发,则只需两步就到3,因而步数不等,出现矛盾,存在环。

方法二:
使用p、q两个指针,p每次向前走一步,p每次向前走两步,若在某个时候p == q,则存在环。
3、魔术师发牌问题
魔术师手中有A、2、3……J、Q、K十三张黑桃扑克牌。在表演魔术前,魔术师已经将他们按照一定的顺序叠放好(有花色的一面朝下)。魔术表演过程为:一开始,魔术师数1,然后把最上面的那张牌翻过来,是黑桃A;然后将其放到桌面上;第二次,魔术师数1、2;将第一张牌放到这些牌的最下面,将第二张牌翻转过来,正好是黑桃2;第三次,魔术师数1、2、3;将第1、2张牌依次放到这些牌的最下面,将第三张牌翻过来正好是黑桃3……直到将所有的牌都翻出来为止。则原来牌的顺序为???
#include <stdio.h>
#include <stdlib.h>
#define CardNumber 13
struct card
{int num;struct card *next;
};
//创建循环链表
struct card *Createcard()
{struct card *head = NULL, *p = NULL;head = (struct card *)malloc(sizeof(struct card)); //已申请,不为空p = head;struct card *pp = NULL;for (int i = 1; i <= CardNumber; i++){pp = (struct card *)malloc(sizeof(struct card));pp->num = 0;p->next = pp;p = pp;}pp->next = head->next; //最后一个结点的下一个执行头结点的下一个,形成环free(head); //删除头结点(因为已经连起来了,且头结点本身又没有数据)return pp->next;
}
//定义发牌次序
void SendCard(struct card *p)
{int i;int CountNumber = 2;struct card *pp = NULL;pp = p;pp->num = 1; //正向第一个结点牌为1;while (1){//需要间隔几个数填上for (i = 0; i < CountNumber; i++){pp = pp->next;if (pp->num != 0) //已经放置过,且会优先于它被取出{i--; //跳过已经被取走的,即该位置有牌的话,则下一个位置}}//找到要填充的位置并赋值if (pp->num == 0){pp->num = CountNumber;CountNumber++;if (CountNumber == 14){break;}}}
}int main()
{struct card *p = Createcard();SendCard(p);printf("扑克牌次序为:\n");for (int i = 0; i < CardNumber; i++){printf("黑桃%d ", p->num);p = p->next;}return 0;
}
3、双向链表
双向链表结点结构

typedef struct DulNode
{ElemType Data;struct DulNode* prior; //前驱结点struct DulNode* next; //后继结点
}DuNode,*DuLinkList;
3.1双向链表的插入操作

代码实现:
s->next = p;
s->prior = p->prior;
p->prior->next = s;
p->prior = s;
3.2双向链表的删除操作

p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
p=NULL;
总结:双向链表相对于单链表来说,是要更复杂一点,每个结点多了一个prior指针,对于插入和删除操作的顺序大家要格外小心。
3.3双向循环链表实践
1、要求实现用户输入一个数使得26个字母的排列发生变化,例如用户输入3,输出结果:
DEFGHIJKLMNOPQRSTUVWXYZABC
同时需要支持负数,例如用户输入-3,输出结果:
XYZABCDEFGHIJKLMNOPQRSTUVW
#include <stdio.h>
#include <stdlib.h>#define OK 1
#define ERROR 0typedef char ElemType;
typedef int Status;
typedef struct DualNode
{ElemType data;struct DualNode* prior;struct DualNode* next;
}DualNode,*DuLinkList;Status InitList(DuLinkList* L)
{DualNode* p, * q;int i;*L = (DuLinkList)malloc(sizeof(DualNode));if (!(*L)){return ERROR;}(*L)->next = (*L)->prior = NULL;p = (*L);for (i = 0; i < 26; i++){q = (DualNode*)malloc(sizeof(DualNode));if (!q){return ERROR;}q->data = 'A'+i;q->prior = p;q->next = p->next;p->next = q;p = q;}p->next = (*L)->next;(*L)->next->prior = p;(*L) = p;return OK;
}void Caesar(DuLinkList* L, int i)
{if (i > 0){do{(*L) = (*L)->next;} while (--i);}if (i < 0){do{(*L) = (*L)->prior;} while (++i);}
}
int main()
{DuLinkList L;int i,n;InitList(&L);printf("请输入一个整数:");scanf_s("%d",&n);printf("\n");Caesar(&L,n);for (i = 0; i < 26; i++){L = L->next;printf("%c",L->data);}printf("\n");return 0;
}
相关文章:
数据结构——线性表(静态链表、循环链表以及双向链表)
1、静态链表 用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。 静态链表需要对数组的第一个和最后一个元素作为特殊元素处理,不存数据。 最后一个指向第一个有数据的下标地址,第一个游标指向第一个没有数据的下标地址。 我们对…...
vue3_对接腾讯_实时音视频
项目需要对接腾讯的实时音视频产品,我这里选择的是多人会议,选择其他实时音视频产品对接流程也一样,如何对接腾讯实时音视频的多人会议产品,从开通服务到对接完成,一 一讲解。 一、开通腾讯实时音视频 1.腾讯实时音视…...
一台电脑对应一个IP地址吗?探讨两台电脑共用IP的可能性
在当今数字化时代,IP地址作为网络世界中的“门牌号”,扮演着至关重要的角色。它负责在网络上唯一标识每一台设备,使得数据能够在庞大的互联网中准确无误地传输。然而,对于IP地址与电脑之间的对应关系,许…...
XInput手柄输入封装
功能全面地封装了XInput的输入, 1. 普通按钮按下, 按住, 弹起状态检查, 2. 摇杆4个方向的按下, 按住, 弹起检查 3. 按键状态变化检测并且记录按下触发时间, 按住保持时间, 方便用来完全自定义的输入功能 4. 多手柄输入合并 CXinputHelper.h #pragma once #include <win…...
NodeMCU-ESP8266+flash_download_tool_3.9.7 烧录
USB-TTL 接 NodeMCU的RXD0, TXD0, GND 例程hello_world: Eclipse编译信息: python /d/ESP/ESP8266_RTOS_SDK/ESP8266_RTOS_SDK/components/esptool_py/esptool/esptool.py --chip esp8266 --port COM6 --baud 115200 --before default_reset --after …...
首例开源的自动驾驶混合运动规划框架,手握“规划可解释”和“决策准确”两张王牌!
导读: 本文开发了一种新的混合运动规划方法,将环境和预测信息集成在Frenet坐标系中,提升了运动规划能力。本文将传统运动规划算法的可预测性和稳定性与RL的动态适应性相结合,从而形成了一个能够有效管理复杂情况并适应不断变化的环…...
数据结构之红黑树的 “奥秘“
目录: 一.红黑树概念 二. 红黑树的性质 三.红黑树的实现 四.红黑树验证 五.AVL树和红黑树的比较 一.红黑树概念 1.红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何 一条从根…...
【鸿蒙 HarmonyOS NEXT】使用EventHub进行数据通信
✨本人自己开发的开源项目:土拨鼠充电系统 ✨踩坑不易,还希望各位大佬支持一下,在GitHub给我点个 Start ⭐⭐👍👍 ✍GitHub开源项目地址👉:https://github.com/cheinlu/groundhog-charging-syst…...
大模型RAG实战|构建知识库:文档和网页的加载、转换、索引与存储
我们要开发一个生产级的系统,还需要对LlamaIndex的各个组件和技术进行深度的理解、运用和调优。本系列将会聚焦在如何让系统实用上,包括:知识库的管理,检索和查询效果的提升,使用本地化部署的模型等主题。我将会讲解相…...
江协科技stm32————11-5 硬件SPI读写W25Q64
一、开启时钟,开启SPI和GPIO的时钟 二、初始化GPIO口,其中SCK和MOSI是由硬件外设控制的输出信号,配置为复用推挽输出 MISO是硬件外设的输入信号,配置为上拉输入,SS是软件控制的输出信号,配置为通用推挽输出…...
网络编程day04(UDP、Linux IO 模型)
目录 【1】UDP 1》通信流程 2》函数接口 1> recvfrom 2> sendto 3》代码展示 1> 服务器代码 2> 客户端代码 【2】Linux IO 模型 场景假设一 1》阻塞式IO:最常见、效率低、不耗费CPU 2》 非阻塞 IO:轮询、耗费CPU,可以处…...
【android10】【binder】【2.servicemanager启动——全源码分析】
系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …...
Java实现简易计算器功能(idea)
目的:写一个计算器,要求实现加减乘除功能,并且能够循环接收新的数据,通过用户交互实现。 思路: (1)写4个方法:加减乘除 (2)利用循环switch进行用户交互 &…...
Parsec问题解决方案
Parsec目前就是被墙了,有解决方案但治标不治本,如果想稳定串流建议是更换稳定的串流软件,以下是一些解决方案 方案一:在%appdata%/Parsec/config.txt中,添加代理 app_proxy_address 127.0.0.1 app_proxy_scheme http…...
Swift 创建扩展(Extension)
类别(Category) 和 扩展(Extension) 的 用法很多. 常用的 扩展(Extension) 有分离代码和封装模块的功能,例如登陆页面有注册功能,有登陆功能,有找回密码功能,都写在一个页面就太冗余了,可以考虑使用 扩展(Extension) 登陆页面的方法来分离代码 本文介绍Swift 如何创建扩展(Ex…...
九月五日(k8s配置)
一、安装环境 环境准备:(有阿里云) k8s-master 192.168.1.11 k8s-node1 192.168.1.22 k8s-node2 192.168.1.33 二、前期准备 在k8s-master主机 [rootk8s-master ~]# vim /etc/hosts …...
某极验4.0 -消消乐验证
⚠️前言⚠️ 本文仅用于学术交流。 学习探讨逆向知识,欢迎私信共享学习心得。 如有侵权,联系博主删除。 请勿商用,否则后果自负。 网址 aHR0cHM6Ly93d3cyLmdlZXRlc3QuY29tL2FkYXB0aXZlLWNhcHRjaGE 1. 浅聊一下 验证码样式 验证成功 - …...
洛谷 P10798 「CZOI-R1」消除威胁
题目来源于:洛谷 题目本质:贪心,st表,单调栈 解题思路:由于昨天联练习了平衡树,我就用平衡树STL打了个暴力,超时得了30分 这是暴力代码: #include<bits/stdc.h> using name…...
Pow(x, n)
题目 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000示例 2: 输入:x 2.10000, n 3 输出:9.26100示…...
一文带你学会使用滑动窗口
🔥个人主页:guoguoqiang. 🔥专栏:leetcode刷题 209.长度最小的子数组 求最短长度之和等于目标值。 方法一: 暴力枚举(会超时) 从头开始遍历直到之和等于target然后更新结果。这…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
