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

news2024/11/15 8:38:13

文章目录

  • 前言
  • 线性表
  • 顺序表
  • 链表
    • 合并有序链表
    • 反转链表
  • 队列
    • 循环队列
    • 双端队列
    • 资源分配问题
    • 共享栈
    • 表达式求值
    • 递归处理
    • 迷宫问题
    • 串的模式匹配
      • 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.coloradmin.cn/o/1074704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spark-08

学习视频&#xff1a; 黑马程序员Spark全套视频教程&#xff0c;4天spark3.2快速入门到精通&#xff0c;基于Python语言的spark教程_哔哩哔哩_bilibili

增强LLM:使用搜索引擎缓解大模型幻觉问题

论文题目&#xff1a;FRESHLLMS:REFRESHING LARGE LANGUAGE MODELS WITH SEARCH ENGINE AUGMENTATION 论文地址&#xff1a;https://arxiv.org/pdf/2310.03214.pdf 论文由Google、University of Massachusetts Amherst、OpenAI联合发布。 大部分大语言模型只会训练一次&#…

Spring Data Redis使用方式

1.导入Spring Data Redis的maven坐标 pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2. 配置Redis数据源 2.1application.yml文件…

idea compile项目正常,启动项目的时候build失败,报“找不到符号”等问题

1、首先往上找&#xff0c;看能不能找到如下报错信息 You aren’t using a compiler supported by lombok, so lombok will not work and has been disabled. 2、这种问题属于lombok编译失败导致&#xff0c;可能原因是依赖jar包没有更新到最新版本 3、解决方案 1&#xff09…

C语言编程实现只有一个未知数的两个多项式合并的程序

背景&#xff1a; 直接看题目把&#xff01;就是C语言写两个多项式多项式合并 题目要求&#xff1a; 1. 题目&#xff1a; 编程实现只有一个未知数的两个多项式合并的程序。如&#xff1a; 3x^26x7 和 5x^2-2x9合并结果为8x^24x16。 2. 设计要求 &#xff08;1&#xff09…

有哪些值得推荐的Java 练手项目?

大家好&#xff0c;我是 jonssonyan 我是一名 Java 后端程序员&#xff0c;偶尔也会写一写前端&#xff0c;主要的技术栈是 JavaSpringBootMySQLRedisVue.js&#xff0c;基于我学过的技术认真的对每个分享的项目进行鉴别&#xff0c;今天就和大家分享我曾经用来学习的开源项目…

微调codebert、unixcoder、grapghcodebert完成漏洞检测代码

文件结构如下所示&#xff1a; mode.py # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. import torch import torch.nn as nn import torch from torch.autograd import Variable import copy from torch.nn import CrossEntropyLoss, MSELosscl…

若依4.7.6 版本任意文件下载(CVE-2023-27025)

CVE-2023-27025 框架说明 若依/ruoyi 是使用java主流框架的一款优秀的国内开源cms&#xff0c; 基于SpringBoot、Shiro、Mybatis的权限后台管理系统。 环境搭建 查询最近的漏洞信息 https://cve.mitre.org/ 搜索ruoyi 代码审计感兴趣的漏洞&#xff1a;CVE-2023-27025 …

MyBatis-Plus 内置雪花算法主键重复问题

Mybatis-Plus 使用ID_WORKER生成主键id重复 问题描述 目前项目使用的id是mybatis-plus 内置的主键生成策略 ID_WORKER &#xff0c;最近测试在做性能压测&#xff0c;部署架构是单服务集群的部署方式&#xff0c;然后就发现了id重复的异常&#xff0c;异常如下 问题分析 首先分…

JSONUtil.parse将java对象转为json时,需要在java对象中设置get、set方法

想要使用JSONUtil.parse将java对象转为json格式&#xff0c;但是一直为空&#xff0c;代码如下 public class MyTest {public static void main(String[] args) {Test3<String> test3 new Test3<>("2","hhhhhhaaa");System.out.println(JSON…

Excel统计一列数据中某数字出现的频次函数COUNTIF

一、函数COUNTIF 要统计Excel中一列数据中各个元素出现的频次&#xff0c;可以使用Excel的函数COUNTIF。 假设要统计的数据位于A列&#xff0c;从A1到A10&#xff0c;可以在某小格子中使用以下公式来统计每个元素的频次&#xff1a; COUNTIF($A$1:$A$10, A1) 二、示例 下表…

前端笔记:Create React App 初始化项目的几个关键文件解读

1 介绍 Create React App 是一个官方支持的方式&#xff0c;用于创建单页应用的 React 设置用于构建用户界面的 JAVASCRIPT 库主要用于构建 UI 2 项目结构 一个典型的 Create React App 项目结构如下&#xff1a; ├── package.json ├── public # 这…

[ZJCTF 2019]NiZhuanSiWei - 伪协议+文件包含+反序列化

[ZJCTF 2019]NiZhuanSiWei 1 解题流程1.1 分析1.2 解题 题目源码&#xff1a; <?php $text $_GET["text"]; $file $_GET["file"]; $password $_GET["password"]; if(isset($text)&&(file_get_contents($text,r)"welcome t…

中国替代方案探索:替代谷歌企业邮箱的选择

“谷歌企业邮箱在中国有哪些替代方案&#xff1f;在中国市场上表现出色的企业邮箱有腾讯企业邮箱、网易企业邮箱、阿里企业邮箱以及适合外贸的Zoho Mail企业邮箱。” 在中国由于各种原因&#xff0c;包括网络安全、数据隐私保护以及与GFW(防火长城)等&#xff0c;谷歌企业邮箱并…

unity操作_刚体 c#

刚体Rigidbody 首先在场景中创建一个Plane 位置重置一下 再创建一个Cube 充值 y0.5 我们可以看出创建的Cube 和 Plane都自带碰撞器 Plane用的是网格碰撞器 我们可以通过网格世界看到不同的网格碰撞器 发生碰撞&#xff08;条件&#xff09;&#xff1a; 两个物体都有碰撞器 …

Windows环境下下载安装Elasticsearch和Kibana

Windows环境下下载安装Elasticsearch和Kibana 首先说明这里选择的版本都是7.17 &#xff0c;为什么不选择新版本&#xff0c;新版本有很多坑&#xff0c;要去踩&#xff0c;就用7就够了。 Elasticsearch下载 Elasticsearch是一个开源的分布式搜索和分析引擎&#xff0c;最初由…

C++ 初识STL

STL 1. 初识STL2. STL相关知识学习网站3. STL体系结构4. STL六大组件之间的关系5. STL六大组件使用例子6. 初识容器7. 初识分配器7.1 其他分配器7.2 为什么需要其他的分配器 1. 初识STL STL全称为标准模板库&#xff08;Standard Template Library&#xff09;。设计STL的初衷是…

关于VMware Workstation Pro中虚拟机无法连接外网问题解决

解决方案 1.虚拟机设置 打开虚拟机设置&#xff0c;将网络设备器修改为NAT模式。注意如果是克隆的多个虚拟机&#xff0c;需要将高级&#xff08;V&#xff09;里面的mac地址进行重新生成。 2.配置虚拟网络编辑器 进入虚拟网络编辑器后&#xff0c;将子网和子网掩码进行修…

安卓玩机----展讯芯片机型解锁 读写分区工具 操作步骤解析

国内机型大都使用高通和MTK芯片。展讯芯片使用的较少。相对来说高通和mtk机型解锁以及读取分区工具较多。展讯的几乎没有。目前有大佬开发出了一款展讯芯片解锁 与读写分区工具.开源的tools 官方分享说明&#xff1a; 是一款专为 Windows 计算机设计的免费、用户友好的工具&am…

性价比高的项目管理软件推荐:哪个更适合您?

如今&#xff0c;企业管理软件层出不穷&#xff0c;面对诸多企业管理软件&#xff0c;我们要如何去进行选择。产品的功能都大同小异&#xff0c;当面对如此之多的“衍生品”&#xff0c;我认为首先要考虑的就是性价比。当产品的功能要求都能够满足时&#xff0c;性价比无疑是最…