数据结构——线性表(静态链表、循环链表以及双向链表)
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然后更新结果。这…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...