学习之路,长路漫漫,写学习笔记的过程就是把知识讲给自己听的过程。
目录
数据结构初定义
数据结构(data structure)是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算,设计出相应的算法,并确保经过这些运算以后所得到的新结构仍保持原来的结构类型。简而言之,数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。
常用数据结构
数组(Array)
是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。数组可以说是最基本的数据结构,在各种编程语言中都有对应。一个数组可以分解为多个数组元素,按照数据元素的类型,数组可以分为整型数组、字符型数组、浮点型数组、指针数组和结构数组等。数组还可以有一维、二维以及多维等表现形式。
栈( Stack)
栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。栈按照先进后出或后进先出的原则来存储数据,也就是说,先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。栈在汇编语言程序中,经常用于重要数据的现场保护。栈中没有数据时,称为空栈。
队列(Queue)
队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作。一般来说,进行插入操作的一端称为队尾,进行删除操作的一端称为队头。队列中没有元素时,称为空队列。
链表( Linked List)
链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。链表由一系列数据结点构成,每个数据结点包括数据域和指针域两部分。其中,指针域保存了数据结构中下一个元素存放的地址。链表结构中数据元素的逻辑顺序是通过链表中的指针链接次序来实现的。
树( Tree)
树是典型的非线性结构,它是包括,2个结点的有穷集合K。在树结构中,有且仅有一个根结点,该结点没有前驱结点。在树结构中的其他结点都有且仅有一个前驱结点,而且可以有两个后继结点,m≥0。
图(Graph)
图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。如果两个顶点之间存在一条边,那么就表示这两个顶点具有相邻关系。
堆(Heap)
堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。堆的特点是根结点的值是所有结点中最小的或者最大的,并且根结点的两个子树也是一个堆结构。
散列表(Hash)
散列表源自于散列函数(Hash function),其思想是如果在结构中存在关键字和T相等的记录,那么必定在F(T)的存储位置可以找到该记录,这样就可以不用进行比较操作而直接取得所查记录。 [5]
这 8 种数据结构有什么区别呢?
①、数组
优点:
按照索引查询元素的速度很快;按照索引遍历数组也很方便。
缺点:
数组的大小在创建后就确定了,无法扩容;数组只能存储一种类型的数据;添加、删除元素的操作很耗时间,因为要移动其他元素。
②、链表
《算法(第 4 版)》一书中是这样定义链表的:
链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该节点还有一个元素和一个指向另一条链表的引用。
Java 的 LinkedList 类可以很形象地通过代码的形式来表示一个链表的结构:
这是一种双向链表,当前元素 item 既有 prior 又有 next,不过 first 的 prior 为 null,last 的 next 为 null。如果是单向链表的话,就只有 next,没有 prior。
单向链表的缺点是只能从头到尾依次遍历,而双向链表可进可退,既能找到下一个,也能找到上一个——每个节点上都需要多分配一个存储空间。
链表中的数据按照“链式”的结构存储,因此可以达到内存上非连续的效果,数组必须是一块连续的内存。
由于不必按照顺序的方式存储,链表在插入、删除的时候可以达到 O(1) 的时间复杂度(只需要重新指向引用即可,不需要像数组那样移动其他元素)。除此之外,链表还克服了数组必须预先知道数据大小的缺点,从而可以实现灵活的内存动态管理。
优点:
不需要初始化容量;可以添加任意元素;插入和删除的时候只需要更新引用。
缺点:
含有大量的引用,占用的内存空间大;查找元素需要遍历整个链表,耗时。
③、栈
栈就好像水桶一样,底部是密封的,顶部是开口,水可以进可以出。用过水桶的小伙伴应该明白这样一个道理:先进去的水在桶的底部,后进去的水在桶的顶部;后进去的水先被倒出来,先进去的水后被倒出来。
同理,栈按照“后进先出”、“先进后出”的原则来存储数据,先插入的数据被压入栈底,后插入的数据在栈顶,读出数据的时候,从栈顶开始依次读出。
④、队列
队列就好像一段水管一样,两端都是开口的,水从一端进去,然后从另外一端出来。先进去的水先出来,后进去的水后出来。
和水管有些不同的是,队列会对两端进行定义,一端叫队头,另外一端就叫队尾。队头只允许删除操作(出队),队尾只允许插入操作(入队)。
注意,栈是先进后出,队列是先进先出——两者虽然都是线性表,但原则是不同的,结构不一样嘛。
⑤、树
树是一种典型的非线性结构,它是由 n(n>0)个有限节点组成的一个具有层次关系的集合。
之所以叫“树”,是因为这种数据结构看起来就像是一个倒挂的树,只不过根在上,叶在下。树形数据结构有以下这些特点:
每个节点都只有有限个子节点或无子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点;除了根节点外,每个子节点可以分为多个不相交的子树。下图展示了树的一些术语:
根节点是第 0 层,它的子节点是第 1 层,子节点的子节点为第 2 层,以此类推。
深度:对于任意节点 n,n 的深度为从根到 n 的唯一路径长,根的深度为 0。高度:对于任意节点 n,n 的高度为从 n 到一片树叶的最长路径长,所有树叶的高度为 0。树的种类有很多种,常见的有:
无序树:树中任意节点的子节点之间没有顺序关系。那怎么来理解无序树呢,到底长什么样子?假如有三个节点,一个是父节点,两个是同级的子节点,那么就有三种情况:
假如有三个节点,一个是父节点,两个是不同级的子节点,那么就有六种情况:
三个节点组成的无序树,合起来就是九种情况。
二叉树:每个节点最多含有两个子树。二叉树按照不同的表现形式又可以分为多种。完全二叉树:对于一颗二叉树,假设其深度为 d(d > 1)。除了第 d 层,其它各层的节点数目均已达最大值,且第 d 层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树。
拿上图来说,d 为 3,除了第 3 层,第 1 层、第 2 层 都达到了最大值(2 个子节点),并且第 3 层的所有节点从左向右联系地紧密排列(H、I、J、K、L),符合完全二叉树的要求。
满二叉树:一颗每一层的节点数都达到了最大值的二叉树。有两种表现形式,第一种,像下图这样(每一层都是满的),满足每一层的节点数都达到了最大值 2。
第二种,像下图这样(每一层虽然不满),但每一层的节点数仍然达到了最大值 2。
二叉查找树:英文名叫 Binary Search Tree,即 BST,需要满足以下条件:
任意节点的左子树不空,左子树上所有节点的值均小于它的根节点的值;任意节点的右子树不空,右子树上所有节点的值均大于它的根节点的值;任意节点的左、右子树也分别为二叉查找树。
基于二叉查找树的特点,它相比较于其他数据结构的优势就在于查找、插入的时间复杂度较低,为 O(logn)。假如我们要从上图中查找 5 个元素,先从根节点 7 开始找,5 必定在 7 的左侧,找到 4,那 5 必定在 4 的右侧,找到 6,那 5 必定在 6 的左侧,找到了。
理想情况下,通过 BST 查找节点,所需要检查的节点数可以减半。
平衡二叉树:当且仅当任何节点的两棵子树的高度差不大于 1 的二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。
平衡二叉树本质上也是一颗二叉查找树,不过为了限制左右子树的高度差,避免出现倾斜树等偏向于线性结构演化的情况,所以对二叉搜索树中每个节点的左右子树作了限制,左右子树的高度差称之为平衡因子,树中每个节点的平衡因子绝对值不大于 1。
平衡二叉树的难点在于,当删除或者增加节点的情况下,如何通过左旋或者右旋的方式来保持左右平衡。
Java 中最常见的平衡二叉树就是红黑树,节点是红色或者黑色,通过颜色的约束来维持着二叉树的平衡:
1)每个节点都只能是红色或者黑色
2)根节点是黑色
3)每个叶节点(NIL 节点,空节点)是黑色的。
4)如果一个节点是红色的,则它两个子节点都是黑色的。也就是说在一条路径上不能出现相邻的两个红色节点。
5)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
B 树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个的子树。数据库的索引技术里就用到了 B 树。
⑥、堆
堆可以被看做是一棵树的数组对象,具有以下特点:
堆中某个节点的值总是不大于或不小于其父节点的值;堆总是一棵完全二叉树。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
⑦、图
图是一种复杂的非线性结构,由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G 表示一个图,V 是图 G 中顶点的集合,E 是图 G 中边的集合。
上图共有 V0,V1,V2,V3 这 4 个顶点,4 个顶点之间共有 5 条边。
在线性结构中,数据元素之间满足唯一的线性关系,每个数据元素(除第一个和最后一个外)均有唯一的“前驱”和“后继”;
在树形结构中,数据元素之间有着明显的层次关系,并且每个数据元素只与上一层中的一个元素(父节点)及下一层的多个元素(子节点)相关;
而在图形结构中,节点之间的关系是任意的,图中任意两个数据元素之间都有可能相关。
⑧、哈希表
哈希表(Hash Table),也叫散列表,是一种可以通过关键码值(key-value)直接访问的数据结构,它最大的特点就是可以快速实现查找、插入和删除。
数组的最大特点就是查找容易,插入和删除困难;而链表正好相反,查找困难,而插入和删除容易。哈希表很完美地结合了两者的优点, Java 的 HashMap 在此基础上还加入了树的优点。
哈希函数在哈希表中起着常关键的作,它可以把任意长度的输入变换成固定长度的输出,该输出就是哈希值。哈希函数使得一个数据序列的访问过程变得更加迅速有效,通过哈希函数,数据元素能够被很快的进行定位。
若关键字为 k,则其值存放在hash(k)的存储位置上。由此,不需要遍历就可以直接取得 k 对应的值。
对于任意两个不同的数据块,其哈希值相同的可能性极小,也就是说,对于一个给定的数据块,找到和它哈希值相同的数据块极为困难。再者,对于一个数据块,哪怕只改动它的一个比特位,其哈希值的改动也会非常的大——这正是 Hash 存在的价值!
尽管可能性极小,但仍然会发生,如果哈希冲突了,Java 的 HashMap 会在数组的同一个位置上增加链表,如果链表的长度大于 8,将会转化成红黑树进行处理——这就是所谓的拉链法(数组+链表)。
数据结构
数据结构是相互之间存在一种或多种特定关系的数据元素的集合
逻辑结构(分为线性结构和非线性结构)
反映数据元素之间的逻辑关系的数据结构,是针对具体问题的,可以表示成一种或多种存储结构
集合结构(非线性结构)
集合通常是由一组无序的, 不能重复的元素构成 数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系
线性结构
线性结构是一个有序数据元素的集合,常用的线性结构有:线性表,栈,队列,双队列,数组,串。 非线性结构,其逻辑特征是一个结点元素可能有多个直接前趋和多个直接后继。常见的非线性结构有:二维数组,多维数组,广义表,树(二叉树等)。
线性结构是一个有序数据元素的集合
数组
线性表
0个或多个具有相同类型的数据元素的有限序列
存储结构
顺序存储结构
顺序存储结构,用一段地址连续的存储单元 一次存储线性表的数据元素。比如数组
每个数据元素存数据元素信息外(数据域),还要存储它的后继元素的存储地址(指针域)
优点:
-
无需为表示表中元素之间的逻辑关系而增加额外的存储空间
-
可以快速的存取表中任意位置的元素,时间复杂度为O(1)
缺点:
-
插入和删除操作需要移动大量元素
-
当线性表变化较大时,难以确定存储空间的容量
-
造成存储空间的"碎片"
链式存储结构
用一组任意的存储单元存储线性表的数据元素, 这组存储单元可以是连续的,也可以是不连续的
单链表
链表中每个结点可以包含若干个数据域和若干个指针域。 如果每个结点中只包含一个指针域,则称其为单链表。
优点:
-
单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制
-
插入和删除更快,时间复杂度为O(n),在给出某位置的指针后,复杂度仅为O(1)
缺点:
-
查找效率比顺序存储结构差,时间复杂度为O(n)
静态链表
用数组描述的链表叫静态链表(用数组代替指针)
为了给没有指针的高级语言设计的视线单链表的方法,很少用(虽然java,c#也没有指针,但是引用类型相当于变样实现了指针)
优点: 插入和删除只需要修改游标,无需移动元素 从而改进了在顺序存储结构中的插入,删除移动大量元素的缺点
缺点: 没有解决连续存储分配带来的表长难以确认的问题 失去了顺序存储结构随机存取的特性
循环链表
将单链表中终端结点的指针段由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表就
称为单循环链表,简称循环链表
双向链表
在单链表的每个结点中,再设置一个指向其前驱结点的指针域 (就意味着双向链表的结点都有俩个指针域,一个指向直接后继, 一个指向直接前驱)
栈(特殊的线性表)
限定仅在表尾进行插入和删除操作的线性表,又称后进先出(LIFO)的线性表 (允许插入和删除的一端称为栈顶,另一端称为栈底)
队列(特殊的线性表)
只允许在一端进行插入操作,而另一端进行删除操作的线性表 先进先出(FIFO)
顺序存储结构(顺序队列)
循环队列
链式存储结构(链队列)
串(字符串)
有0个或多个字符组成的有限序列
针对的是字符集,就是说把字符串中多个字符连在一起
存储结构
顺序存储结构
链式存储结构
模式匹配
朴素的描述匹配 (从头开始一个个字符匹配)
效率很低,复杂度为O((n-m+1)*m) n是总的字符串,m是需要匹配的字符串
KMP模式匹配算法
算法证明和更详细的说明,请参阅《算法导论》第2版第32章字符串匹配
KMP模式匹配算法改进
树形结构(非线性结构)
数据元素之间存在一对多的层次关系
数是n(n>=0)个节点的有限集。若n=0,称为空树。树的节点是互不交互的。 + 度:节点拥有的子数(节点)数量称为"度",度为0的称为叶节点。 + 树的度:树的度是树内各节点的度的最大值 + 节点的层次:跟为第一层,一次类推。 + 数的深度:数中节点的最大层次称为"深度"或"高度"
二叉树
特点
-
每个结点最多有俩棵子树
-
左右子树是顺序的,次序不能颠倒
-
即使树中只有一棵子树,也要区分左右
五种形态
-
空二叉树
-
只有一个根结点
-
根结点只有左子树
-
根结点只有右子树
-
根结点既有左子树,又有右子树
性质
在二叉树的第i层上之多有2i-1(i-1是上标)个结点
深度为K的二叉树至多有2k-1(k>=1,k是上表)个结点
特殊二叉树
斜树
-
所有结点都只有左子树的二叉树叫左斜树
-
所有结点都只有右子树的二叉树叫右斜树
满二叉树
一个二叉树中,所有分支结点都存在左子树和右子树, 并且所有叶子都在同一层上,称为满二叉树
完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中 在最左边,这就是完全二叉树。
线索二叉树
每个二叉树左右俩个指针域,如果为空,放着也浪费空间。 所以第一次遍历二叉树时,如果左指针域为空就存前驱地址, 右指针域为空就存后继地址。但是左侧需要一个tag来标识左指 针域存的是左孩子结点还是前驱,右侧也同样需要。
存储结构
顺序存储结构
只用于完全二叉树(因为完全二叉树定义的严格, 可以用顺序结构表示出二叉树的结构,很有优越性。详情参考P172) 该结构不太通用,通用的见二叉链表
二叉链表
二叉树每个结点最多有俩个孩子,所以可以用 一个数据域+俩个指针域这样的结构存储二叉树
遍历
概念
从根结点出发,按照某种次序依次访问二叉树中 所有结点,使得每个结点被访问一次且仅被访问一次
遍历方法(限定了从左到右的习惯)
前序遍历
根结点=>左子树=>右子树 (切记:是先把所有左子树遍历完了,再遍历右子树)
中序遍历
从根结点开始(并不是先访问根结点),遍历根结点的 左子树=>根结点=>右子树
后序遍历
从根结点开始(并不是先访问根结点),遍历根结点的 左子树=>右子树=>根结点
层序遍历(BFS常用)
根结点一层层从上而下遍历
哈夫曼树(也叫最优二叉树)
带权路径长度WPL最小的二叉树称作哈夫曼树
哈夫曼编码
哈夫曼编码实现压缩,解压缩
原理:先构造哈夫曼树,然后对字符串再次编码,二进制的长度会明显缩小, 所以达到了压缩的效果。解压缩也要用哈夫曼树,才能知道哪几个二进制对应哪个字符
图形结构(非线性结构)
数据元素是多对多的关系
概念:有顶点的有穷非空集合和顶点之间边的集合组成 表示为G(V,E),其中G表示一个图,V是图G中顶点的集合,E是边的集合
特点:图中数据元素称为顶点,图中不允许没有顶点
术语
顶点
图中数据元素称为顶点,图中不允许没有顶点
无向边
顶点Vi到Vj(i和j是下标)之间的边没有方向, 则称这条边为无向边,用无序偶对(ViVj)来表示
有向边
顶点Vi到Vj之间的边有方向, 则称这条边为有向边,也称为弧
无向图
图中任意俩个顶点之间的边都是无向边
连通图
概念:从顶点V导顶点V'有路径,称V和V'是连通的。 如果对于图中任意俩个顶点都是连通的则称为连通图 (能连通就行,不一定要相邻)
连通分量
-
要是子图
-
子图要是连通的
-
连通子图还有极大顶点数
-
具有极大顶点数的连通子图包含依附于这些顶点的所有边
生成树
一个连通图的生成树是一个极小的连通子图, 它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边(p221)
有向图
图中任意俩个顶点之间的边都是有向边
强连通图
概念:如果对于图中任意俩个顶点都是连通的则称为连通图 (能连通就行,不一定要相邻)
强连通分量
参考连通分量定义
有向树
概念:一个有向图恰有一个顶点的入口为0(根节点), 其余顶点的入度均为1,则是一颗有向树
简单图
不存在顶点到其自身的边(自己到自己),且同一条边不重复出现
无向完全图
在无向图中,任意俩个顶点之间都存在边
有向完全图
在有向图中,任意俩个顶点之间都存在方向互为相反的俩条弧网
有的图的边或弧具有与它相关的数字,这叫做权。权可以表示一个顶点到另 一个顶点的距离或者耗费,这种带权的图称为网
存储结构
邻接矩阵
用俩个数组来表示图,一个一维数组存储图中顶点信息, 一个二维数组(邻接矩阵)存储图中的边或弧的信息
缺点
对于边数相对于顶点较少(顶点多,边数少)的图, 这种结构存在存储空间的浪费
邻接表
数组和链表相结合的存储方法称为邻接表
特点
-
图中顶点用一个一维数组存储
-
每个顶点的所有邻接点构成一个新线性表,
-
如果是有向图,还可以建立有向图的逆邻接表,
即对每个顶点vi都建立一个链接为vi为弧头的表。 (可以很容易得到顶点的入度或以顶点为弧头的弧)
优缺点
想了解入度就必须遍历整个图才知道,反之,逆邻接链表解决了入度却不了解出度的情况。俩者不可兼得
十字链表
把邻接表和逆邻接表结合起来
邻接多重表
主要优化无向图
边集数组
俩个一维数组构成,一个存储顶点的信息,一个存储边的信息。 这个边数组每个数据元素由一条边的起点下标,终点下标和权重组成
遍历
深度优先(DFS)
约定原则:在没有碰到重复顶点的情况下,始终向右手边走。 走回到顶点之后,还要按原路一步步返回,在一步步验证是否都都走过了
广度优先(BFS)
图需要变形下,改造成类似树那样有明显的层级关系的样式
最小生成树
概念:我们把构造连通网的最小代价生成树称为最小生成树
所谓最小代价,就是n个顶点,用n-1条边 把一个连通图连接起来,并且使得权值的和最小
算法
普里姆(Prim)算法
以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树
克鲁斯卡尔(Kruskal)算法
以最小权值边开始构建
最短路径
迪杰斯特拉(Dijkstra)算法
一步步求出与顶点的最短路径,过程中都是基于已经求出的最大路径的基础上
佛洛伊德(Floyd)算法
拓扑排序算法
物理结构(存储结构)
数据的逻辑结构在计算机中的存储形式
顺序存储结构
数据元素存放在地址连续的存储单元里, 数据间的逻辑关系和物理关系是一致的 (不方便插入,删除)
不方便插入,删除
链式存储结构
数据元素存放在任意的存储单元里,这组存储单元可以是连续的, 也可以是不连续的 (很灵活,给个指针就能找到。需要一个指针存放数据元素的地址)
很灵活,给个指针就能找到。需要一个指针存放数据元素的地址
索引存储结构
为了方便查找,整体无序,但索引块之间有序,需要额外空间,存储索引表。 优点:对顺序查找的一种改进,查找效率高 缺点:需额外空间存储索引
散列存储结构
选取某个函数,数据元素根据函数计算存储位置可能存在多个数据元素存储在同一位置,引起地址冲 优点:查找基于数据本身即可找到,查找效率高,存取效率高。 缺点:存取随机,不便于顺序查找。
数据运算
查找运算
顺序表查找
顺序查找(线性查找)
从表中第一个(或最后一个)记录开始,逐个进行记录的的关键字和给定值比较。
有序表查找
折半查找(二分查找)
切记:表必须是有序的。mid=(low+high)/2。复杂度是0(logn)
可以优化的地方:每次都从1/2处查找,对于有些场景还是不太适合的, 比如查找am单词,很显然从字典的前面查找效率更高,所以优化点是从哪里开始折半
插值查找
公式:mid=low+(key-a[low])*(high-low)/a[high]-a[low] ;low和high是数组的下标
适合场景:
表长较大,而关键字分配又比较均匀的查找表,性能比折半查找要好得多。复杂度也是0(logn)
斐波那契查找
构造另外一个斐波那契数列用于辅助
公式:mid=low+F[K-1]-1 ;F是斐波那契数列
比较:平均效率优于折半查找,最坏情况下,效率低于折半
线性索引查找
数据集增长非常快的情况下,保证关键字有序,代价很高昂。所以这种数据都是按先后顺序存储。
稠密索引
索引项一定要按照关键码有序的排列
分块索引
把数据集的记录分成了若干块,满足俩个条件
-
块内无序
-
块间有序
倒排索引(P312)
概念:记录号表存储具有相同次关键字的所有记录的记录号 (可以指向记录的指针或者该记录的主关键字)
搜索引擎最基础的搜索技术
二叉搜索树(又称二叉查找树/排序数)
特点:
-
若左子树不为空,则左子树上所有结点值均小于根结点的值
-
若右子树不为空,则右子树上所有结点的值均大于它的根结点的值
-
它的左右子树也分别为二叉排序树
优点:
是一个既使得插入和删除效率不错,又可以比较高效率的实现查找的算法
缺点:
极端情况下,会退化成链表。时间复杂度为O(n)
平衡二叉搜索树(AVL树)
概念:是一种二叉排序树,其中每一个节点 的左子树和右子树的高度差绝对值不超过1
优缺点:
维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多, 更多的地方是用追求局部而不是非常严格整体平衡的红黑树。 当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。
多路查找树(B树)
概念:每一个结点的孩子树可以多余俩个,且每一个结点处可以存储多个元素
4种形式
2-3树
每一个结点都具有俩个孩子(称为2结点),或三个孩子(称为3结点)
2-3-4树
2-3树的扩展,最多四个孩子
B树
一种平衡的多路查找树
所有叶子结点都位于同一层
B+树
应文件系统那个所需而出的一种B树的变形树
出现在分支节点中的元素会在叶子结点中再次列出,每一个叶子结点都会保存一个指向后一叶子结点的指针
红黑树
概念:一种自平衡二叉查找树,和AVL树类似(并不是真正的平衡二叉树), 都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
适合:适合搜索,插入,删除较多的情况
性质:
每个结点要么是红色,要么是黑色
根结点永远是黑色
所有的叶节点都是空结点(即null),并且是黑色的
每个红色结点的俩个子结点都是黑色(从每个叶子到跟的路径上不会有俩个连续的红色结点)
从任一结点到其子树中每个叶子结点的路径都包含相同数量的黑色结点
关键性质:
这些性质强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。主要原因在于,性质4导致了路径不能有两个毗连的红色节点,最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点,根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长
散列表查找(哈希表)
概念:散列技术是在记录的存储位置和它的关键字之间建立一个稳定的对于关系f, 使得每个关键字key对于一个存储位置f(key),f称为散列函数,又称为哈希(Hash)函数
特点
-
在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录
-
查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录
缺点
有时候俩个关键字不同,但是算出来的地址相同,称为冲突
散列函数构造方法
直接定址法
数字分析法
平方取中法
折叠法
除留余数法
随机数法
散列冲突的解决方法
开放定址法
一个冲突,就去寻找下一空的散列函数
再散列函数法
准备多个散列函数,有冲突就换一个
链地址法
公共溢出区法
冲突的都单独存储到溢出表中
排序运算
概念
内排序与外排序
内排序是在排序整个过程中,待排序的所有记录都放置在内存中。外排序是由于排序的记录太多, 不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行
排序种类
冒泡排序
复杂度O(n2) 2是上标
正宗的冒泡排序是相邻元素俩俩比较
冒泡排序优化
加个flag,如果后面后面已经是有序的了,就不需要再继续后面的循环判断了
简单选择排序
每个元素依次和后面所有的元素比较大小
直接插入排序
将一个记录插入到已经排好序的有序表中,从而得到一个新的记录数+1的有序表
希尔排序
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;虽则增量逐渐减少, 每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组
堆排序
利用堆这种数据结构所设计的一种排序算法
归并排序
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序
快速排序
通过一次排序将待排序记录分割称独立的俩部分,其中一部分记录的关键字均比另一部分小, 则可分别堆这俩部分记录继续进行排序,以达到整个序列有序的目的
算法
算法是解决特定问题求解步骤的描述。
时间复杂度(大O表示法)
推导方法
\1. 用常数1取代运行时间汇总的所有加法常数
\2. 在修改后的运行次数函数中,只保留最高阶
\3. 如果最高阶项存在且不是1,则去除与这个项相乘的常数, 得到的结果就是大O阶
常见的复杂度
常数阶 => O(1)
线性阶 => O(n)
对数阶 => O(log2n,缩写为logn)
平方阶 => O(n2)
以上就是本篇文章的全部内容了
~ 关注我,点赞博文~ 每天带你涨知识!
1.看到这里了就 [点赞+好评+收藏] 三连 支持下吧,你的「点赞,好评,收藏」是我创作的动力。
2.关注我 ~ 每天带你学习 :各种前端插件、3D炫酷效果、图片展示、文字效果、以及整站模板 、HTML模板 、C++、数据结构、Python程序设计、Java程序设计、爬虫等! 「在这里有好多 开发者,一起探讨 前端 开发 知识,互相学习」!
3.以上内容技术相关问题可以相互学习,可 关 注 ↓公 Z 号 获取更多源码 !
获取源码?私信?关注?点赞?收藏?
👍+✏️+⭐️+🙇
有需要源码的小伙伴可以 关注下方微信公众号 " Enovo开发工厂 "🙇 回复 " 2-sjjgbj "