数据结构和算法——线性结构

文章目录

  • 前言
  • 线性表
  • 顺序表
  • 链表
    • 合并有序链表
    • 反转链表
  • 队列
    • 循环队列
    • 双端队列
    • 资源分配问题
    • 共享栈
    • 表达式求值
    • 递归处理
    • 迷宫问题
    • 串的模式匹配
      • BF算法
      • KMP算法
        • next数组的求解
        • next数组的优化

前言

本文所有代码均在仓库中,这是一个完整的由纯C语言实现的可以存储任意类型元素的数据结构的工程项目:

在这里插入图片描述

线性表

线性表是具有相同类型 n n n个数据元素的有限序列
L = ( a 1 , a 2 , … , a n ) L=(a_1,a_2,\dots,a_n) L=(a1,a2,,an)
其中 n n n表长,当 n = 0 n=0 n=0时线性表是一个空表,数据元素在线性表的位置称为位序(从 1 1 1开始)。线性表的性质如下:

  • 线性表中存在唯一的第一元素也存在唯一的最后元素
  • 除第一元素外,其它元素有唯一的直接前驱,除最后元素外,其它元素有唯一的直接后继

顺序表

使用顺序存储方式实现的线性表称为顺序表。顺序表的特点如下:

  • 随机访问:可以在 O ( 1 ) O(1) O(1)时间内找到第 i i i个元素。
  • 存储密度高:每个节点只存储数据元素。
  • 扩展容量不方便。
  • 插入、删除元素不方便,需要移动大量元素。
struct SequenceList{ElementType * data;int length;
};

链表

使用链式存储实现的线性表称为链表。链表由若干个节点连接而成。每个节点包括两部分一部分是存储数据元素的数据域,另一部分是存储其它节点地址的指针域。链表的特点如下:

  • 链表的访问是顺序的,只能通过头指针进行。
  • 链表可以有一个头节点,也可以没有,头节点不存储任何数据,含有头节点的链表操作更加方便。
  • 除头节点外第一个存储数据的节点称为首元节点

只有一个指针域的链表称为单链表:

//SingleLinkedList.h
typedef struct Node Node, *SingleLinkedList;
//SingleLinkedList.c
struct Node {ElementType data;struct Node *next;
};

有两个指针域的链表称为双链表:

//DoubleLinkedList.h
typedef struct Node Node, *DoubleLinkedList;
//DoubleLinkedList.c
struct Node {ElementType data;struct Node *last;struct Node *next;
};

把单链表和双链表的第一元素和最后元素就变成了循环链表。

合并有序链表

SingleLinkedList mergeOrderedLinkedList(SingleLinkedList l1, SingleLinkedList l2) {//返回最小的节点if (l1 == NULL) {return l2;} else if (l2 == NULL) {return l1;} else if (l1->data < l2->data) {l1->next = mergeOrderedLinkedList(l1->next, l2);return l1;} else {l2->next = mergeOrderedLinkedList(l1, l2->next);return l2;}
}

反转链表

  • 反转整个链表
SingleLinkedList reverseList(SingleLinkedList list) {if (list->next==NULL){return list;} else{Node * head=reverseList(list->next);list->next->next=list;list->next=NULL;return head;}
}
  • 反转链表的前n个元素
public ListNode end;
public ListNode reverseListN(ListNode head,int n){if (n==1){end=head.next;return head;}else {ListNode newHead=reverseListN(head.next,n-1);head.next.next=head;head.next=end;return newHead;}
}

反转链表的任意一部分

public ListNode reverseBetween(ListNode head, int left, int right) {if (left==1){return reverseListN(head,right);}else {head.next=reverseBetween(head.next,left-1,right-1);return head;}
}

k个一组反转链表

public ListNode reverseKGroup(ListNode head, int k) {if (k==1){return head;}else {ListNode right=head;for (int i = 1; i < k; i++) {if (right==null){return head;}right=right.next;}ListNode newHead=reverse(head,right);if (right!=null&&right.next!=null){head.next=reverseKGroup(head.next,k);}return newHead;}
}
public ListNode end;
public ListNode reverse(ListNode left,ListNode right){if (left==null||right==null||left==right){return left;} {end=right.next;ListNode newHead = reverse(left.next, right);left.next.next=left;left.next=end;return newHead;}
}

队列

队列是一种只能在表头或表尾进行插入或删除的线性表,它的特点如下:

  • 插入元素的一端称为队尾
  • 删除元素的一端称为队头
  • 遵循先入先出原则

请添加图片描述
可以使用顺序存储或链式存储的方式实现队列:

//顺序存储
typedef struct SequenceQueue SequenceQueue;
struct SequenceQueue {ElementType data[MAX_SIZE];int front;int rear;
};//链式存储
#include "../linkList/SingleLinkedList.h"
typedef struct LinkedQueue LinkedQueue;
struct LinkedQueue {Node *front;Node *rear;
};

循环队列

双端队列

资源分配问题

栈是一种只能在表头或表尾进行插入和删除的线性表。它的特点如下:

  • 允许插入和删除的一端称为栈顶
  • 不允许插入和删除的一端称为栈底
  • 遵循先入后出原则
  • n n n个不同元素进栈,出栈元素不同排列个数为 1 n + 1 C 2 n n \frac{1}{n+1}C^n_{2n} n+11C2nn(卡特兰数)

请添加图片描述

可以通过顺序存储和链式存储的方式实现栈:

//顺序存储
typedef struct SequenceStack SequenceStack;
struct SequenceStack {ElementType data[MAX_SIZE];int top;
};//链式存储
typedef struct Node Node, *LinkedStack;
struct Node{ElementType data;struct Node * next;
};

共享栈

表达式求值

  • 中缀表达式
  • 后缀表达式(逆波兰表达式):
    • 左优先原则:只要左边的运算符能先算,就先算左边的
    • 先出栈的是右操作数
  • 前缀表达式(波兰表达式):
    • 右优先原则:只要右边的运算符能先算,就优先算右边的
    • 先出栈的是左操作数

递归处理

迷宫问题

串是一个数据元素只能是字符的线性表:
S = ′ a 1 a 2 … a n ′ S='a_1a_2\dots a_n' S=a1a2an
其中 n n n为串长,当 n = 0 n=0 n=0时串为空串。串中任意连续字符组成的子序列称为该串的子串,包含该子串的串称为主串,不包含串本身的子串称为真子串,空串是任意串的子串。某个字符在串中的序号称为该字符在串中的位置。子串在主串中的位置以子串的第一个字符在主串中的位置来表示。

//String.h
typedef struct String *String;//String.c
struct String {char *ch;int length;
};

串的模式匹配

子串在串中的定位称为串的模式匹配。通常有以下两种算法:

  • BF算法
  • KMP算法

BF算法

BF算法也称为简单匹配法,它的算法思想是:将主串中所有与模式串长度相同的子串和模式串对比,直到找到一个完全匹配的子串或所有的子串都不匹配为止。

int BF(String src, String target) {int i = 1, j = 1;while (i <= src->length && j <= target->length) {if (*(src->ch + i - 1) == *(target->ch + j - 1)) {i++;j++;} else {i = i - j + 2;j = 1;}}if (j > target->length) {return i - target->length;} else {return 0;}
}

KMP算法

与BP算法相比KMP算法可以利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配。首先弄清三个概念:

  • 前缀:除最后一个字符外,字符串的所有头部子串。
  • 后缀:除第一个字符外,字符串的所有尾部子串。
  • 部分匹配:字符串最长相等前后缀的长度。

下面通过以下两个串来描述算法:

//主串:aaabaaaabaa
String src=stringConstructor("aaabaaaabaa");
//模式串:aabaa
String target=stringConstructor("aabaa");

算法开始时先求出模式串中以各个字符为结尾的子串的部分匹配值,并将这些值放到数组partialMarch中:

//a:0
//aa:1
//aab:0
//aaba:1
//aabaa:2
int partialMarch[]={,0,1,0,1,2};//字符串的位置从1开始,数组下标从0开始,为了便于计算,舍弃数组的第一位

当匹配失败时,主串不再回溯而是保持当前位置不动,模式串也不再从头开始,而是相对于当前位置回退move位,回退之后继续匹配,move由以下公式计算:

//回退位数=匹配成功子串的字符个数-匹配成功子串的部分匹配值
//j为模式串当前匹配的位置
move=(j-1)-partialMarch[j-1]

partialMarch数组的角度看就是当前字符匹配失败就会找它前一个字符的部分匹配值,因此最后一个字符的部分匹配值将永远用不到,所以可以将partialMarch数组整体向右移动一个单位得到next数组(第一个元素以-1填充):

next[]={,-1,0,1,0,1};

那么move就变为:

move=(j-1)-next[j]

那么回退后的j就变为:

 j=j-move=next[j]+1

如果将next数组各个元素加1:

next[]={,0,1,2,1,2};

此时j为:

j=next[j]
next数组的求解

因此最主要的任务就是求解next数组,原理很简单,设i为模式串的当前位置,j为上一位置的next数组值,即j=next[i-1],那么:

  • i=1时,next[i]≡0
  • i≠1时:
    • 如果charAt(target, i) == charAt(target, j),那么next[i]=j+1
    • 如果charAt(target, i) == charAt(target, j),那么就让j=next[j],之后继续比较,直至比较相等或j=0
int *getNext(String target) {int *next = calloc(target->length + 1, sizeof(int));*(next + 1) = 0;int i = 1, j = 0;while (i < target->length) {if (j == 0 || charAt(target, i) == charAt(target, j)) {next[++i] = ++j;} else {j = next[j];}}
}
// 初始:i=1,j=0
// |   i  |1|2|3|4|5|
// |target|a|a|b|a|a|
// | next |0| | | | |// 第一轮:i=1,j=0
// |   i  |1|2|3|4|5|
// |target|a|a|b|a|b|
// i=2,j=1
// | next |0|1| | | |// 第二轮:i=2,j=1
// |   i  |1|2|3|4|5|
// |target|a|a|b|a|b|
// i=3,j=2
// | next |0|1|2| | |// 第三轮:i=3,j=2
// |   i  |1|2|3|4|5|
// |target|a|a|b|a|b|
// i=4,j=1
// | next |0|1|2|1| |//第四轮:i=4,j=1
// |   i  |1|2|3|4|5|
// |target|a|a|b|a|b|
// i=5,j=2
// | next |0|1|2|1|2|
next数组的优化

可以对next数组进一步优化,当计算出next[++i]后,如果发现*charAt(target, i) == charAt(target, next[i]),那么下一次比较必将失败,此时就可以将next[i] = next[next[i]]

int *getNextVal(String target) {int *nextVal = calloc(target->length + 1, sizeof(int));*(nextVal + 1) = 0;int i = 1, j = 0;while (i < target->length) {if (j == 0 || charAt(target, i) == charAt(target, j)) {nextVal[++i] = ++j;if (charAt(target, i) == charAt(target, nextVal[i])) {nextVal[i] = nextVal[nextVal[i]];}} else {j = nextVal[j];}}
}

完整的KMP算法如下:

int enKMP(String src, String target) {int *next = getNextVal(target);int i = 1, j = 1;while (i <= src->length && j <= target->length) {if (j == 0 || charAt(target, i) == charAt(target, j)) {i++;j++;} else {j = *(next + j);}}if (j > target->length) {return i - target->length;} else {return 0;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/156973.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis--动态sql

XML映射文件&#xff08;简单的SQL用注解&#xff0c;复杂的用xml&#xff09; 规范&#xff1a; XML映射文件的名称和Mapper接口名称一样&#xff08;同包同名&#xff09;注意&#xff1a;不能直接用.创建文件夹,用/分层 xml映射文件的namespace属性为mapper接口全限定名一致…

Python数据分析实战-实现T检验(附源码和实现效果)

实现功能 T 检验&#xff08;Students t-test&#xff09;是一种常用的统计方法&#xff0c;用于比较两个样本之间的均值是否存在显著差异。它可以应用于许多场景&#xff0c;其中一些常见的应用场景包括&#xff1a; A/B 测试&#xff1a;在市场营销和用户体验研究中&#xf…

迁移Linux服务器用户数据(将一个服务器的Linux用户数据迁移到另一个Linux服务器用户的流程)

文章目录 1、打包源Linux服务器用户的数据2、发送源Linux服务器用户的数据3、查看目的服务器用户接受到的数据 1、打包源Linux服务器用户的数据 先来到根目录&#xff0c;再使用tar命令打包数据&#xff1a;tar czvf root.zip.gz ./* 2、发送源Linux服务器用户的数据 在根目…

探秘PMP和六西格玛的不同:哪一个能为你的职业生涯加分?

今天&#xff0c;我们将带你深入了解一项相对冷门但价值不菲的证书——六西格玛黑带。 可能你曾听说过PMP&#xff0c;但相比之下&#xff0c;六西格玛黑带的资源分享似乎较少&#xff0c;考试内容却更为广泛深入。这里&#xff0c;让我为你详细解析这一考试&#xff0c;带你进…

Python操作Hive数据仓库

Python连接Hive 1、Python如何连接Hive&#xff1f;2、Python连接Hive数据仓库 1、Python如何连接Hive&#xff1f; Python连接Hive需要使用Impala查询引擎 由于Hadoop集群节点间使用RPC通信&#xff0c;所以需要配置Thrift依赖环境 Thrift是一个轻量级、跨语言的RPC框架&…

latex如何对.pdf格式的图片实现裁剪

目录 问题描述&#xff1a; 问题解决&#xff1a; 问题描述&#xff1a; 在使用draw.io进行绘图&#xff0c;导出的时候不知道为什么周围会有留白&#xff0c;比如下图&#xff1a; 在导入latex的时候&#xff0c;会因为两侧的留白导致整张图片缩小。 如果直接进行裁剪.pdf&a…

简要归纳UE5 Lumen全局光照原理

文章目录 一、Jim kajiya老爷子的渲染方程&#xff1a;二、工程上的实时全局光照技术&#xff1a;三、Lumen的解决办法&#xff1a;1、用距离场 Distance Field&#xff08;SDF&#xff09;判断光线和三角面相交&#xff1a;2.表面缓存&#xff08;Surface Cache&#xff09; 四…

《论文阅读:Dataset Condensation with Distribution Matching》

点进去这篇文章的开源地址&#xff0c;才发现这篇文章和DC DSA居然是一个作者&#xff0c;数据浓缩写了三篇论文&#xff0c;第一篇梯度匹配&#xff0c;第二篇数据增强后梯度匹配&#xff0c;第三篇匹配数据分布。DC是匹配浓缩数据和原始数据训练一次后的梯度差&#xff0c;DS…

Apache Shiro 漏洞复现

文章目录 Apache Shiro 漏洞复现1. Apache Shiro 1.2.4 反序列化漏洞1.1 漏洞描述1.2 漏洞原理1.3 漏洞复现1.3.1 环境启动 1.4 漏洞利用1.5 修复方案 Apache Shiro 漏洞复现 链接地址&#xff1a;Vulhub - Docker-Compose file for vulnerability environment 1. Apache Shi…

MySQL中使用函数会使索引失效?

文章目录 1、前置准备2、ChatGPT的答案3、实践证明SQL1SQL2SQL3SQL4SQL5 4、总结 1、前置准备 首先创建我们要测试的库表 CREATE TABLE lianhe_index (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(255) DEFAULT NULL,age int(11) DEFAULT NULL,number int(1…

相似与不同:数字孪生和元宇宙的对比

数字孪生和元宇宙是两个备受瞩目的概念&#xff0c;都在数字领域产生了巨大的影响。它们有一些相似之处&#xff0c;但也存在显著的不同。本文将介绍它们的相同点和不同点&#xff0c;以及它们在不同应用领域的前景。 1. 相同点 虚拟性质&#xff1a; 数字孪生和元宇宙都是虚…

AlphaPose Pytorch 代码详解(一):predict

前言 代码地址&#xff1a;AlphaPose-Pytorch版 本文以图像 1.jpg&#xff08;854x480&#xff09;为例对整个预测过程的各个细节进行解读并记录 python demo.py --indir examples/demo --outdir examples/res --save_img1. YOLO 1.1 图像预处理 cv2读取BGR图像 img [480,…

LATR:3D Lane Detection from Monocular Images with Transformer

参考代码&#xff1a;LATR 动机与主要工作&#xff1a; 之前的3D车道线检测算法使用诸如IPM投影、3D anchor加NMS后处理等操作处理车道线检测&#xff0c;但这些操作或多或少会存在一些负面效应。IPM投影对深度估计和相机内外参数精度有要求&#xff0c;anchor的方式需要一些如…

【图像融合】差异的高斯:一种简单有效的通用图像融合方法[用于融合红外和可见光图像、多焦点图像、多模态医学图像和多曝光图像](Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

淘宝价格,淘宝商品优惠券数据接口,淘宝商品销量接口,淘宝商品详情数据接口,淘宝API接口

淘宝价格和商品优惠券数据接口是淘宝平台提供的官方数据接口&#xff0c;通过调用接口&#xff0c;可以获取到淘宝商品的价格信息和优惠券数据。 获取淘宝价格和商品优惠券数据接口的步骤如下&#xff1a; 输入淘宝网址登陆淘宝账号密码。点击获取key和secret。调用获取buyer…

android 与 flutter 之间的通信

文章目录 前言集成 flutter 混合开发android 与 flutter 之间的通信总结 一、前言 因为flutter 具有跨平台的属性&#xff0c;既可以在android上跑&#xff0c;也能在ios 上跑&#xff0c;所以为了节约开发的成本&#xff0c;减少人力&#xff0c;势必就会用到它。然而已有的…

04在命令行中使用Maven命令创建Maven版的Web工程,并将工程部署到服务器的步骤

创建Maven版的Web工程 使用命令生成Web工程 使用mvn archetype:generate命令生成Web工程时&#xff0c;需要使用一个专门生成Web工程骨架的archetype(参照官网看到它的用法) -D表示后面要附加命令的参数&#xff0c;字母D和后面的参数是紧挨着的&#xff0c;中间没有任何其它…

Ceph介绍与部署

Ceph介绍与部署 一、存储基础1.1、单机存储设备1.1.1、单机存储的问题 1.2、商业存储解决方案1.3、分布式存储&#xff08;软件定义的存储 SDS&#xff09;1.3.1、分布式存储的类型 二、Ceph 简介三、Ceph 优势四、Ceph 架构五、Ceph 核心组件5.1、Pool中数据保存方式支持两种类…

FlashDuty Changelog 2023-09-21 | 自定义字段和开发者中心

FlashDuty&#xff1a;一站式告警响应平台&#xff0c;前往此地址免费体验&#xff01; 自定义字段 FlashDuty 已支持接入大部分常见的告警系统&#xff0c;我们将推送内容中的大部分信息放到了 Lables 进行展示。尽管如此&#xff0c;我们用户还是会有一些扩展或定制性的需求…

数据可视化

pip install matplotlib 一、各种图 #线形图 import numpy as np import pandas as pd df1pd.DataFrame(datanp.random.randn(1000,4),indexpd.date_range(start10/10/2023,periods1000),columnslist(ABCD)) df1.cumsum().plot()#2、条形图 df2pd.DataFrame(datanp.random.ra…