本文详细介绍字符串的存储结构及相应的操作。 串的定义 串(string)是由零个或多个字符组成的有限序列。一般记为 其中,S 是串名,单引号括起来的字符序列是串的值;串中字符的个数 n 称为串的长度。 子串在主串中的位置以子串的第一个字符在主串中的位置来表示。当两个串的长度相等且每个对应位置的字符都相等时,称这两个串是相等的。 串的存储结构 大家看完定义就会明白,这不就是 Python 中的 str 数据类型吗?确实如此,所以我就不用Python 的 str 套壳实现一个串了,依旧换成 C/C++ 来实现串。 由于串的特殊性(每个元素只有一个字符),在具体实现时,每个结点即可以存放一个字符,也可以存放多个字符。每个结点称为块,整个链表称为块链结构。 串的基本操作 串的基本操作一共有 10 个:赋值操作、复制操作、判空操作、比较操作、求串长、求子串、串联接、定位操作、清空操作和销毁串。 我在这里基于堆分配存储表示来实现这 10 个基本操作。
然而,仔细观察会发现,i=4 和 j=1,i=5 和 j=1 及 i=6 和 j=1 这 3 次比较都是不必进行的,因为从第 3 次部分匹配结果可知,主串中第 4、5 和 6 个字符是'b'、'c' 和 上述公式不难理解,手工求 next 值时,用之前的方法也很好求,但如果想用代码实现,貌似难度还真不小,我们来尝试推理求解的科学步骤。 j 1 2 3 4 5 6 7 8 9 模式 a b a a b c a b a next[j] 0 1 1 2 2 3 ? ? ? 表的模式串中以求得 6 个字符的 next 值,现在求 next[7],因为 next[6]=3,又 ? 则需比较 ? 和 ? (因 next[3]=1),由于 ? 因为考研复试已经尘埃落定(已被拟录取),数据结构系列暂时停止更新!后期会随便更新一些东西! B 站账号:新时代的运筹帷幄,喜欢的可以关注一下,看完视频不要忘记一键三连啊!
Set一种新的数据结构,在之前数据的集合分为数组(Array)和对象(Object),ES6出现新的Set数据结构,和Map,这里先介绍一下Set. 如何定义Set数据结构? ,不过这个Set和Array相似度极高,只不过Set中不允许出现相同的元素! Set数据结构的方法 add()向Set追加元素 delete 删除Set中存在的元素 has() 判断Set中存在某个元素不,存在返回true,否则返回false clear , for ....values返回Set的键值, for....enteries返回一每个数组每一项是一个包含键值对的数组 ? 1,23,4,5,8,2,3,1,5,9,2,5,4,1,4,5,8]; console.log(arr) let s = new Set([...arr]); console.log(s) //此时arr数据实现了去重复
队列 队列的特性是先进先出。每次数据出去只能的队列的头部,每次数据进来只能加在队列的尾部。 队列实现一般有两种方式,线性队列,链表队列。 链表队列 链表队列的实现可以参考单向链表。 先建立一个普通的单向链表,然后设置三个属性。队列头,用来标识当前队列头的地址;队列尾,用来标识队列尾的地址;队列长,记录当前的队列长,理论上不给队列设置长度可以无限扩展。 每次删除数据,就把队列头的标识移到下一个node的地址。每次增加数据则就把队列尾的指针指向的node加上下一个node的地址,同时把队列尾的标识移过去即可。 线性队列 超简单的,基于数组实现,每次删除数据则把数组第一个删除,把后续的往前面移动,最后一个直接置空;添加数据只需要在最后继续添加即可;数组会有定长,删除和添加数据一定要检验。
1.栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作叫做出栈。出数据也在栈顶。 2.栈的实现 栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。 0,则相当于是malloc,扩容完之后就将数据放进top这个位置,然后再将top++,这样才会使得top一直是栈顶元素的下一个位置。 void STPop(ST* ps, STDataType x) { assert(ps); //空 assert(ps->top > 0); --ps->top; } 2.6栈的数据个数 int
前言 在上一篇关于树和二叉树的博客中,最后提到了堆。有小根堆和大根堆。 左边的结构是我们想象出来的,右边才是实际存储的结构。 这次来实现堆。 2. 堆的实现 用数组来实现,这里以实现小堆为例子,它的特点是父节点小于子节点。 先定义一个堆的结构体:为了方便扩容,加了size。 2.2.2 插入代码实现 先判断空间是否足够,不够就扩容,够就直接插入x,再将php->size++。 2.3.2 删除代码实现 首尾交换删除,然后将php->size--,最后向下调整。 { assert(php); return php->size == 0; } 3.3 test.c #include"Heap.h" int main() { int a[] = { 4,6,2,1,5,8,2,9
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set 本身是一个构造函数,用来生成 Set 数据结构。 Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。 keys():返回键名的遍历器 values():返回键值的遍历器 entries():返回键值对的遍历器 forEach():使用回调函数遍历每个成员 需要特别指出的是,Set的遍历顺序就是插入顺序。 Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。 let set = new Set([1, 2, 3]); set = new Set([...set].map(x => x * 2)); // 返回Set结构:{2, 4, 6} let set
pandas是本系列后续内容所需要的第三方库,它是基于之前介绍的NumPy构建的,使得Python可以更加简单、方便地完成一系列数据分析工作。 首先,使用下面的pandas导入约定: pd是pandas约定俗成的缩写,Series和DataFrame是pandas中两个最重要的数据结构。我们将简单介绍二者的用法,作为pandas的入门。 1.Series Series是一种类似于一维数组的对象,它由一组数据(NumPy数组)以及相对应的一组数组标签(即索引)构成。 其中,左边是索引部分,右边是数据部分。 通过Series的values和index属性,可以获取数据数组和索引数组。 我们可以通过传入索引参数对数据进行标记,然后就可以通过索引获取对应的数据点,这一点类似于字典数据结构。 2.DataFrame DataFrame是Pandas数据分析中最常用和最重要的数据结构,它是一个表格型的数据结构,这一点与Excel表格十分类似,每个数据点既有行索引又有列索引。
链表的概念 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。 这里我来解释一下什么叫做逻辑上连续而物理上补连续。 所谓逻辑上的连续,就是指你你可以通过第一个节点找到第二个节点,就行火车一样你可以从一节车厢走到另一节的车厢。 物理上的不连续是指内存地址的不连续,不想顺序表中的数组是连续的。 next就起到了重要的作用,我们把2的地址存在1的next中,把3的地址存在2的next中,最后把3的next指向NULL就可以了,再画一个图就是这样的。 我还用了一个头节点来指向第一个节点,从图上来看是不是每个节点都联系了起来,这就是有了逻辑上的连续。 下面我来讲解实现链表都要哪些函数。 头插函数 想要实现头部插入,我们先画图。 要想吧新的节点插入头部,我们就要把,新节点的nest指向第一个节点,然后把pphead指向新的节点。
之前我们学习过数据结构中的栈和队列,详情可点击这里数据结构——lesson5栈和队列详解进行查看,队列是一种先进先出的结构,但是我们之前讲的队列都是类似于线性的物理结构,这次我们所介绍的队列则是一直类似于环状的循环结构 1.循环队列的介绍 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 rear的下一个元素指向front,如果增加一个空闲的位置,队列满时rear的下一个位置就不再指向front; 在决定选哪种方法之前,我们先要考虑一下是使用链表来实现还是使用数组也就是顺序表来实现循环队列 ;当然这里土土会将两种方法都写下来,并和大家一起分析两种方法的优劣之处,以便大家选择合适和喜欢的形式(对于顺序表链表有疑问的可以在土土的数据结构专栏里——数据结构学习笔记 进行查看复习哦~) 3.用单链表实现循环队列 ,对应数组实现循环队列则需要front,rear不断进行取模运算以防越界; 但是链表实现需要手动将开辟的节点链接在一起,数组则不一样它一开辟就是地址连续的一段空间; 其他的实现链表和数组都差不多;
之前编写了自己的数组,下来基于之前的基础之上实现了栈的基本内容
namespace DataStructure
{
class Program
{
///
这时我们就可以使用另一种数据结构--链表。 和顺序表一样,链表也是一种线性表。和顺序表不同的是,虽然链表在逻辑上是连续的,链表在物理上并不是连续的。 链表中的节点通过指针域相互连接,形成一个动态的数据结构,可以方便地进行插入、删除等操作,而不需要像数组一样需要连续的内存空间。 单链表的实现 模块划分 和实现顺序表一样,分为三个文件来实现,SList..c用来实现顺序表的各种方法,SList.h用来包含实现方法所需的头文件和所需方法的初始化。 test.c用来测试写的方法是否有问题。 节点 实现链表的节点需要创建两个变量,数据域用来储存数据,指针域用来存放下一个节点的地址。我们用结构体来实现。 void SLTEraseAfter(SLTNode* pos); //销毁链表 void SListDesTroy(SLTNode** pphead); 方法实现 在SList.c中对需要的方法进行实现
线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就是说是连续的一条线。但是在物理结构上并不一定是连续的,比如链表。 * a; int sz;//有效数据个数 int capacity;//存储空间大小 }SL; 3.模拟实现 静态顺序表只适合于确定知道要存储多少数据的场景下。 下面是模拟实现: 3.1 准备工作 定义一个结构体 typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Datatype* a; &sl, 1); PushBack(&sl, 2); PushBack(&sl, 3); PushBack(&sl, 4); PushBack(&sl, 5); PushBack(&sl, 6) &sl, 1); PushBack(&sl, 2); PushBack(&sl, 3); PushBack(&sl, 4); PushBack(&sl, 5); PushBack(&sl, 6)
上一次我们实现了顺序表的应用,但是对于顺序表还是会有以下几个问题: 1. 中间/头部的插入删除,时间复杂度为O(N)。 2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 3. 单链表的概念 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。单链表中的数据元素由一个数据域和一个指针域组成,其中指针域指向下一个数据元素。 同顺序表一样我们先创建一个SList.h的头文件和一个SList.c的源文件,.h文件实现函数的声明,.c文件实现函数的定义。 现在我们来实现尾插,如下图: 尾插很简单,我们只需要注意,循环遍历时结束条件是尾节点的next指针为空,而不是尾节点为空。 测试运行: 6.删除节点 1)删除pos节点 和尾删有点相似,pos要分为第一个节点和其他节点两种情况。
上一次我们实现了单链表,也就是不带头单向不循环链表,这次我们首先双链表(带头双向循环链表)。 对于单链表来说,还是存在一些缺点的,例如:查找效率低(需要从头开始遍历链表),不能随机访问。 在List.h文件中,首先创建一个结构体用来存放节点的信息,这里和单链表的区别是我们额外增加了一个结构体指针成员prev用来指向上一个节点的指针。 接下来我们来实现双链表的增删查改操作。 所以我们要养成良好的编程习惯。 我们创建一个test.c文件调试一下: 2.链表的尾插 双链表尾插实现就很简单了,不同于单链表还要先找到尾节点,这里phead->prev指向的就是尾节点。 为了方便测试我们先将打印方法实现。 3.链表的打印 打印就需要循环遍历一遍,注意我们的条件现在不再是pcur->next != NULL,而是!= phead。 测试一下: 6.链表的头删 头删和尾删类似,不做过多说明。 测试一下: 7.链表的查找 找到了就返回该节点的地址,没找到就返回NULL。
堆的实现 介绍的话就到此为止,下面我们来进行堆的实现。无非就是那几样。 ,没错就是顺序表,但实现起来会比顺序表更难一些哦。 函数的声明 我们先把初始化,销毁,插入,删除等等要实现的函数声明一下: void HpInit(HP* php); void Hppush(HP* php, HpDataType x); void Hpdestroy 我们发现下面我有写了有个函数,向上调整的函数adjustup,没错上面我们讲的谁小于谁交换就要通过这个函数来实现。 child = parent; parent = (child - 1) / 2; } else { break; } } } 里面还有一个swap函数,就是交换函数,它的实现在下面
栈满足的特性是先进后出,就像货车装货物,把货物一次放进去,但是卸货的时候,你得先把最外面的卸载了,才能继续卸载里层的货物。 栈的实现有两种形式,一种是数组,一种是链表。 ? 对于一个栈,需要至少三个属性 top:记录当前栈的顶部,超过栈的长度和长度小于0都应报错。 push:往栈里面存东西,当超过栈的长度需要警报,当然,链表栈理论上是可以无限存东西的。 pop:把栈顶部的数据移除。 链表栈 链表栈实际上可以看成链表的一种约束,约束了链表的长度,链表的插入和删除位置。 对于链表栈,需要两个变量 top:记录当前栈顶的地址和位置。 链表:记录当前的数据和下一个,上一个的链表块的地址。top永远指向了链表栈的最后一个元素,记录其位置。链表栈和链表结构本质相同。 数组栈(顺序栈) 对数组进行约束,成为栈。 关于实现一个顺序栈 #include <stdio.h> #include <iostream> #include <stdlib.h> using namespace std; class Stack
目录 前言 堆的概念和结构 堆的实现 接口展示 堆结构创建 堆的初始化 堆的销毁 入堆 数据向上调整 入堆测试 出堆 向下调整数据 出堆测试 堆顶数据获取 堆数据个数 判断空堆 堆数据打印 堆源码 ---- 前言 ---- 本章主要讲解: 数据结构中的堆的知识以及实现 堆的概念和结构 ---- 概念: 将所有元素按完全二叉树的顺序存储方式存储在一个一维数组中并以一定的数据要求存储 堆总是一棵完全二叉树 注:在上节基础知识讲解中我们知道父节点和左右子节点的编码关系,以此可以对应到数组中的下标关系,这里我们主要用数组来表示和实现堆 图示:数组堆 堆的实现 ---- 注:这里我们主要实现大堆,对于小堆的实现与大堆只有数据调整上的出入 接口展示 //堆初始化 void HeapInit(HP* hp); //堆销毁 void HeapDestroy(HP* hp) 删除后数据依旧要保持大堆的特性 对于空堆无法出堆 出堆方式: 首先我们将堆顶数据也就是下标为0的数据与堆尾数据交换 交换后让记录存入数据的变量减减,实现删除堆顶数据 再让现在堆顶的数据向下调整 参考代码
顺序存储方式的优点是存储密度大,且插入、删除运算效率高。( ) 2. 数据结构的基本操作的设置的最重要的准则是,实现应用程序与存储结构的独立。( ) 3. 对于给定的 n 个元素,可以构造出的逻辑结构有 (1) , (2) , (3) ,(4)四种。 3.数据的逻辑结构是指() 。 4.一个数据结构在计算机中()称为存储结构。 5.抽象数据类型的定义仅取决于它的一组(1),而与(2)_无关,即不论其内部结构如何变化,只要它的(3)不变,都不影响其外部使用。 6.数据结构中评价算法的两个重要指标是 ()。 7. 数据结构是研讨数据的(1)和_(2),以及它们之间的相互关系,并对与这种结构定义相应的(3),设计出相应的(4)。 8. 5.(1)逻辑特性 (2)在计算机内部如何表示和实现 (3)数学特性。 6.算法的时间复杂度和空间复杂度。 7.(1)逻辑结构(2)物理结构(3)操作(运算)(4)算法。
栈 1.1 栈的概念 栈:一种特殊的线性表,其**只允许在固定的一端进行插入和删除元素操作(表的末端)。**进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 对栈的基本操作有push(入栈)和pop(出栈),前者相当于插入,后者则时删除最后插入的元素。 栈又称为 LIFO(后进先出)表。 栈在生活中的例子: 1.2 栈的使用 方法 功能 Stack() 构造一个空的栈 E push(E e) 将e入栈,并返回e E pop() 将栈顶元素出栈并返回 E peek() 获取栈顶元素 int size() 获取栈中有效元素的个数 boolean empty() 检测栈是否为空 2. 栈的模拟实现 栈的定义 定义一个数组,用于存储栈的元素 public int[] elem; 定义一个变量,用于记录栈的有效元素个数 public int usedSize; 定义一个常量NUMBER