数据结构简述

数据结构

数据结构是指相互之间存在着一种或者多种关系的数据元素的集合和该集合中数据元素之间的关系组成

  • 按逻辑分:

    • 线性结构:结构中元素存在一对一的相互关系,如列表

    • 树结构:结构中元素存在一对多的相互关系,如堆

    • 图结构:结构中元素存在多对多的相互关系,如

列表/数组

列表是顺序存储的

  • 数组与列表的不同:

    • 数组元素类型要相同

    • 数组长度固定

  • 列表基本操作时间复杂度:

    • 查找:O(1)
    • 插入:O(n)
    • 删除:O(n)

栈(stack)

是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表

  • 特点:后进先出LIFO(last-in,first-out)

  • 基本概念:

    • 栈顶

    • 栈底

  • 基本操作:

    • 进栈(亚栈):push li.append

    • 出栈:pop li.pop

    • 去栈顶:gettop li[-1]

  • 括号匹配问题:判断字符串中括号是否一一匹配

队列(Queue)

队列是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除

  • 特点:先进先出FIFO(first-in,first-out)

  • 基本概念:

    • 队尾

    • 队头

  • 基本操作:

    • 进队/入队:插入

    • 出队:删除

  • 队列的实现方式:环形队列

    • 概念:当队尾指针front==maxsize-1时,再进一个位置就自动到0

    • 队首指针前进1:front=(front+1)%maxsize

    • 对尾指针前进1:rear=(rear+1)%maxsize

    • 队空条件:rear==front

    • 队满条件:(rear+1)%maxsize==front

  • python中内置的队列模块

    • 双向队列:两端都支持进队和出队
    from collections import deque
    

链表

线性数据结构

链表是由一系列节点组成的元素集合,每个节点包含两部分,数据域item和指向下一个节点的指针next,通过节点之间的相互连接,最终串联成一个链表

  • 创建链表:

    • 头插法:在头节点插入

    • 尾插法:最链表的最后插入,需要知道头和尾

  • 双链表

    • 每一个节点有两个指针:一个指向后一个节点,一个指向前一个节点

    • p.next.prior:表示向前一个节点

链表的总结

顺序表(列表/数组)与链表

- 按元素查找:一个个找,O(n)

- 按下标查:顺序表O(1),链表O(n)

- 在某个元素插入:顺序表O(n),链表O(1)

- 删除:顺序表O(n),链表O(1)
  • 链表在插入和删除上快于顺序表

  • 链表的内存可以更为灵活的分配

  • 链表这种链式存储的数据结构对树和图的结构有很大的启发性

哈希表

哈希表通过哈希函数来计算数据存储位置的数据结构,支持以下操作:

  • insert(key,value):插入键值对(key,value)

  • get(key):如果存在键为key的键值则返回其value,否则返回空值

  • delete(key):删除键为key的键值对

直接寻值表

当关键字的全域U比较小时使用直接寻值表

缺点:

  • 当域很大时,需要消耗大量内存,很不实际

  • 如果域很大而实际出现的key很少,则大量空间被浪费

  • 无法处理关键字不是数字的情况、

直接寻值表+哈希=哈希表

  • 改进直接寻值表:哈希(Hashing):

    • 构建大小为m的寻值表T

    • key为k的元素放到h(k)位置上

    • h(k)是一个函数,其将域U映射到表T[0,1,…,m-1]

哈希表介绍

哈希表(Hash Table)又称为散列表,是一种线性表的存储结构,哈希表由一个直接寻值表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标

哈希冲突

哈希表大小有限,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突

解决方案

1、开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值

  • 线性探查:如果位置i被占用,则探查i+1,i+2,…

  • 二次探查:如果位置i被占用,则探查i+12,i-12,i+2^2,…

  • 二度哈希:有n个哈希函数,当使用第一个哈希函数h1发生冲突时,则尝试使用h2,h3,…

2、拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后

常见哈希函数

1、除法哈希法: h ( k ) = k h(k)=k h(k)=k% n n n

2、乘法哈希法: h ( k ) = f l o o r ( m ∗ ( A ∗ k e y h(k)=floor(m*(A*key h(k)=floor(m(Akey% 1 ) ) 1)) 1))

3、全域哈希法: h a , b ( k ) h_{a,b}(k) ha,b(k)=((a*key+b) m o d mod mod p) m o d mod mod m,a,b=1,2,…,p-1(mod相当于%)

哈希表的应用

集合和字典都是用哈希表实现的

密码方面的使用

1、MD5算法(message-digest algorithm 5):曾经是密码学常用哈希函数,可以把人意长度的数据映射为128位的哈希值。

2、SHA1

目前较安全的密码算法:SHA-2、国产SM3

  • 树是一种数据结构,如目录结构

  • 树是一种可以递归定义的数据结构

  • 树是由n个节点组成的集合

    • 如果n=0,那这就是一个空树

    • 如果n>0,那么存在一个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树

  • 根节点:可分叉的节点

  • 叶子节点:不可分叉的节点

  • 树的深度(高度)

  • 树的度:整个树中节点的度的最大值

  • 节点的度:分几个叉就是几

  • 孩子节点/父节点:节点间的关系

  • 子树:每个根节点跟之后的部分可以看作子树

二叉树

  • 度不超过2的树

  • 每个节点最多有两个孩子节点,分为左孩子节点和右孩子节点

二叉树的遍历

二叉树

前、中、后指的是根节点的位置

1、前序遍历:EACBDGF

2、中序遍历:ABCDEGF

3、后序遍历:BDCAFGE

4、层次遍历:EAGCFBD,一层层遍历

二叉搜索树

满足性质:左孩子的值都比根节点小,右孩子的值都比根节点小

二叉搜索树常用操作

  • 插入

  • 查询

  • 删除

    • 删除叶子节点:直接删

    • 删除只有一个孩子节点的根节点:直接将该节点父亲与孩子连起来

    • 删除有两个孩子节点的根节点:将其右子树的最小节点删除,并替换当前节点

满二叉树

一个二叉树,如果每一个层的节点数都达到最大值,则这个二叉树就是满二叉树

完全二叉树

叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树,从左到右是满的

二叉树的存储方式
  • 链式存储方式:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接

  • 顺序存储方式:以列表的方式存储

二叉搜索树进行搜索的时间复杂度:O(logn)

AVL树
  • AVL树是一棵自平衡的二叉搜索树

  • AVL树性质:

    • 根的左右子树高度之差的绝对值不能超过1

    • 根的左右子树都是平衡二叉树

  • 插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正

  • 插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变,需要找出第一个破坏了平衡条件的节点,称之为k,k的两棵子树的高度差2

  • 不平衡是由于对k的右孩子插入导致:左旋

  • 不平衡是由于对k的左孩子插入导致:右旋

  • 不平衡是由于对k的右孩子的左子树插入导致:右旋-左旋

  • 不平衡是由于对k的左孩子的右子树插入导致:左旋-右旋

扩展应用

二叉树搜索树扩展应用–B树:是一棵自平衡的多路搜索树。

一种特殊的完全二叉树结构,除了最后一层,其他层都是满的,且最后一层是从左到右有数值的

  • 大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大

  • 小根堆:一颗完全二叉树,满足任一节点都比其孩子节点小

堆排序

向下调整型

假设:节点的左右子树都是堆,但自身不是堆

当根节点的左右子树都是堆时,可以通过一次向下的调整来将其变换成一个堆

过程:

1、建立堆

- 找到第一个非叶子节点,比大小,大上小下,图上看就是从最后一层、最右边看起,依次调整大小构造堆

- 农村包围城市

2、得到堆顶元素,为最大元素

3、去掉堆顶,将堆的最后一个元素放到堆顶,此时可通过一次调整重新使堆有序;

4、堆顶元素为第二大元素;

5、重复步骤3,直到堆变空。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个较长的字符串中查找一个较短的模式串。它通过构建next数组来实现快速匹配。 KMP算法的核心思想是在每次失配时,不是将模式串向后移动一位,而是将模式串向后移动至下一次可以和前面部分匹配的位置,从而跳过大部分的失配步骤。这样可以大大提高匹配效率。 next数组是部分匹配值表,它存放的是每一个下标对应的部分匹配值。部分匹配值是指前缀和后缀的最长共有元素的长度。通过构建next数组,可以在匹配过程中根据当前失配的位置快速确定模式串的移动步数。 下面是手动模拟KMP算法的过程: 1. 首先,根据模式串构建next数组。假设模式串为p[],长度为m。初始化next数组,next = -1,next = 0。 2. 从第2个位置开始,依次计算next[i]的值。假设已经计算到next[i-1],则比较p[i-1]和p[next[i-1]]的值: - 如果p[i-1]等于p[next[i-1]],则next[i] = next[i-1] + 1; - 如果p[i-1]不等于p[next[i-1]],则将next[i-1]的值赋给j,然后继续比较p[i-1]和p[j]的值,直到找到一个满足p[i-1]等于p[j]的位置或者j等于0为止。此时,next[i] = j。 3. 完成next数组的构建后,开始匹配过程。假设待匹配的字符串为s[],长度为n。初始化i = 0,j = 0。 4. 依次比较s[i]和p[j]的值: - 如果s[i]等于p[j],则i和j分别向后移动一位; - 如果s[i]不等于p[j],则根据next数组确定模式串的移动步数。将j移动到next[j]的位置,即j = next[j]。 5. 重复步骤4,直到匹配成功(j等于模式串的长度m)或者遍历完整个字符串s。 通过上述步骤,可以实现高效的字符串匹配。KMP算法的时间复杂度为O(n+m),其中n为字符串s的长度,m为模式串p的长度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值