![](https://img-blog.csdnimg.cn/20201031203723140.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
数据结构和算法
数据结构在C++STL中部分实现
马小超i
你若盛开 清风自来
展开
-
AVL树的平衡操作
确定状态平衡因子: 右子树的高度减去左子树的高度最简单的情况:失衡的点称之为parent,再看左右子树是谁失衡,如果左子树不是0,则是L,再接着往下看,LL,对于LL型,一次右旋就可以平衡,并且旋转的是parent和subL对于RR型,一次左旋就可以平衡,并且旋转的是parent和subR对于LR型和RL型,一次旋转不够LR型:先对subL和subR左旋,转化为LL型,再右旋RL型:先对subR和subL右旋,转化为RR型,再左旋...原创 2021-08-23 21:03:08 · 328 阅读 · 0 评论 -
最简练写法实现归并排序【C++代码】
目录归并排序C++代码实现稳定性分析求逆序对归并排序归并排序的思想是分治。其过程也就是“分”和“治”的两个步骤。对于一个序列,我们先将其一分为二,分别排好序,然后再合并这两个有序数列。对于被平分成的两部分怎么排序呢?再一分为二,递归下去,直至分成单个元素时,即自然有序了。C++代码实现网上很多代码会把归并排序的分和治两个环节分开写,这样可能好理解,但是函数太多了,冗余。我更习惯将其合并起来写,看起来更简洁。int t[50005]; //用于暂时存放合并原创 2021-01-25 01:01:53 · 2051 阅读 · 1 评论 -
双指针法及题目总结
最近,碰到了一个这样的题目,也用到了双指针的思想。有N种午饭和M种晚饭,每种饭都有热量值X和美味值Y,并且每顿饭最多吃一种,问在美味值之和不小于T的情况下,最少的热量摄入X。原题是拼多多秋招第三题:https://blog.csdn.net/qq_21989927/article/details/107751197如果饭A的热量值大于B的热量值,并且A的美味值小于B的美味值,那么这个饭A是肯定没有用的,所以我们可以直接不考虑饭A,所以排序完成后,应该是x和y都在递增的情况。至此,再...原创 2020-12-26 00:23:48 · 328 阅读 · 0 评论 -
前缀和与差分、二维前缀和
前缀和题目:有n个数,并且有m次操作,每一次操作要求查询第x个数到第y个数的和。前缀和是一种动态规划的思想,假如有数组 a[i] 。定义 sum[i] 表示从 1 到 a[i] 的和。状态转移方程则是: sum[i] = sum[i-1] + a[i] ; 其中 sum[1] = a[1] ;这样可以在O(n)的时间计算出整个数组的前缀和。查询 a[x...y] = sum[y] - sum[x-1] ;差分差分:我们定义 f[i] 表示 a[i] 与 ...原创 2020-12-24 16:01:11 · 160 阅读 · 1 评论 -
最长上升子序列 LIS【DP的O(n^2)及贪心+二分的O(nlogn)解法】
最长上升子序列给你一个长度为N的序列,求其最长上升子序列的长度。样例输入: 6 1 6 2 4 3 5样例输出: 4解释:其最长上升子序列的长度为4。可以是{1 2 4 5 }或者{1 2 3 5}注意:子序列是不连续的。假设我们把序列储存在a数组中,并且从下标1开始存储。动态规划解法定义 f[i] 表示到a[i] 为止的最长上升子序列的长度。(其中a[i]必须被选中,必须以a[i]结尾)所以动态转移方程是 f [ ...原创 2020-12-18 18:18:07 · 124 阅读 · 1 评论 -
红黑树删除操作的各种情况分析
目录删除情况具体分析1. 删除的节点没有子节点的情况2. 删除的节点只有一个子节点时3. 如果删除节点有两个子节点时红黑树删除平衡情况分析红黑书的删除本质上是一个穷举的过程。删除情况具体分析1. 删除的节点没有子节点的情况 a、如果为红色,直接删除即可,不会影响黑色节点的数量 删除红色节点(13)示例(不影响黑色节点数量,不需要平衡操作) b、如果为黑色,删除的时候需要进行平衡操作 删除示例,删除节点11(平衡操作后面会介绍到)2. 删除的节点只有一个子..原创 2020-12-07 22:17:13 · 1291 阅读 · 2 评论 -
红黑树插入操作的各种情况分析
目录1. 被插入的节点是根节点。2. 被插入的节点的父节点是黑色。3. 被插入的节点的父节点是红色。 3.1 叔叔结点为红色 3.2 插入结点的父结点p是祖父结点pp的左子结点,插入结点的叔叔结点s不存在或为黑色 3.2.1 插入结点是父结点p的左子结点 3.2.2 插入结点是父结点p的右子结点 3.3 插入结点的父结点p是祖父结点pp的右子结点,插入结点的叔叔结点s不存在或为黑色 3.3.1插入结点是父结点p的右子结点 3.3.2插入...原创 2020-12-07 22:07:47 · 955 阅读 · 3 评论 -
红黑树【性质、动图左旋右旋、插入、删除、应用】
一、为什么要有红黑树我们都知道搜索二叉树,但是最后我们发现搜索二叉树有缺陷,会退化成链表,之后我们又引入了AVL树。AVL树是一种高度平衡的二叉搜索树,能够满足增删查都是O(logN)的时间复杂度。但是由于AVL树是高度平衡的二叉搜索树,维持一棵AVL树的代价很高,而红黑树是一种弱平衡的二叉搜索树,其性能也不算查,所以在综合考虑下。许多情况都用红黑树。(如STL的map)。扩展:红黑树和AVL树的比较分析二、红黑树的性质红黑树具有以下性质:定义1:每个节点都有颜色,要么是黑色...原创 2020-12-07 21:06:53 · 782 阅读 · 0 评论 -
红黑树和AVL树的比较分析
定义AVL树全称是平衡二叉搜索树,相比于红黑树,他是一种高度平衡的二叉搜索树,所有节点的左右子树高度差不超过1。 红黑树是一种弱平衡的二叉搜索树,它只要求部分达到平衡,其保证最长路径最多是最短路径的2倍。增删查比较插入: 就插入节点导致树失衡的情况,AVL树和红黑树都是最多两次树旋转来实现平衡,旋转的量级是O(1)。 删除: 删除节点导致失衡,AVL需要维护从被删除节点到根节点root这条路径上所有节点的平衡,旋转的量级为O(logN); 而红黑树最多只需要旋转3次实现平衡,只需O(1)原创 2020-12-07 20:47:40 · 1022 阅读 · 0 评论 -
堆的性质、堆的实现、堆排序
目录一、堆的性质二、堆的实现1. 存储方式2. 向堆中插入元素3. 删除堆顶元素4. 其他操作5. 完整代码三、堆排序1. 原地建堆2. 排序3. 完整代码4. 建堆的复杂度:O(n)5. 堆排序的复杂度:O(nlogn)6. 和快速排序的比较一、堆的性质堆是一种特殊的树。只要满足以下两点,它就是一个堆:堆是一个完全二叉树; 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值。第一点,堆必须是一个完全二叉树。完全二.原创 2020-11-28 19:28:18 · 4293 阅读 · 0 评论 -
求矩阵中不同行不同列的两个数的最大乘积
题目描述:一个n*m的矩阵,求其中不同行并且不同列的两个数的最大乘积。 n,m<1000分析:首先矩阵不大,1000*1000,一共1000000个元素,我先想到了排序,从大到小把所有元素排序,然后选择最大的两个。这两个如果符合条件,那么就是答案,不符合的话,,,,就麻烦了假如排完序是这样的 a1 a2 a3 a4 a5,,,你如果保留a1的话,那么可能a2 a3都会不满足,直到a4才满足,但是 a2*a3>a1*a4。这样就陷入了a1到底留不留的问题,而且后.原创 2020-08-14 21:00:39 · 5275 阅读 · 11 评论 -
进阶版最大连续子序列的和:可以删除连续一段
用枚举边界的方法求删除一段的最大连续子序列的和原创 2020-08-14 16:43:42 · 1126 阅读 · 0 评论 -
三种解法求:最大连续子序列的和
示例:输入: [-2,1,-3,4,-1,2,1,-5,4]输出: 6解释:连续子数组[4,-1,2,1] 的和最大,为6。一、贪心思想假设a[l]...a[r]的和为 sum如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留如果 sum <0,则说明 sum 对结果无增益效果,需要舍弃明白了这个道理,那么对于第i个数来说,我们查看他之前的sum,如果sum<0,那么当前的最大连续子序列和就是它本身(舍弃前面的);如果sum>...原创 2020-08-14 10:51:07 · 9741 阅读 · 0 评论 -
取模运算的性质
模运算与基本四则运算有些相似,但是除法例外。其规则如下: (a + b) % p = (a % p + b % p) % p (a - b) % p = (a % p - b % p) % p (a * b) % p = (a % p * b % p) % p a ^ b % p = ((a % p)^b) % p 结合律: ((a+b) %...原创 2020-04-23 09:10:36 · 406 阅读 · 0 评论 -
翻转问题(开关问题)的通解及技巧
翻转问题技巧详解例.给定一个01串,现有翻转规则:翻转某一个位置时其后面2个位置也会跟着翻转,也就是每次翻转都会翻转3个连续的位置。要将01串全部翻转为0,求最小的翻转次数形似这类题的问题叫做翻转问题,也可以叫开关问题,对于这类题通常都会用到下面我要说的方法来解①.若某一个位置被翻转了n次,则其实际上被翻转了n%2次,因为翻转2k次相当与没翻转,翻转2k+1次相当于翻转了1次,因...原创 2018-08-01 17:24:18 · 2344 阅读 · 0 评论 -
和为s的连续区间【尺取法】
双指针的应用是灵活多变的,其使用条件是有序。常见的例子:有一个有序的一维数组,求和为s的连续区间。假如说这个序列是:1 34 7 9 10 1323 29 求和23的连续区间。很明显答案3种有 【3 47 9】 【10 13】 【23】用O(n)的时间复杂度求出来设置两个指针,l和 r,初始化为1,维护一个sum=Σa[i] l<=k<=r也就是说sum就是目前 l到 r连续区间的和。这个sum有一个性质:因为数组是有序的,所以l++,...原创 2020-08-07 17:01:14 · 184 阅读 · 0 评论 -
哈希表的实现【控制哈希表长度,设计哈希函数,处理哈希冲突】
目录怎样控制哈希表的长度怎样设计哈希函数字符串哈希算法优秀的字符串哈希算法怎样处理哈希冲突一、开放定址法 1.线性探测法 2.二次探测法 3.再哈希法二、拉链法(哈希桶)哈希表的设计主要是为了对内存中的数据进行快速查找,它的查找时间复杂度是O(1)。设计一个哈希表的关键有三个:怎么控制哈希表的长度,怎么设计哈希函数,怎么处理哈希冲突。怎样控制哈希表的长度哈希表的长度一般是定长的,在存储数据之前我们应该知道存储的数据规模是多大,应该尽可能地避免频...原创 2020-11-17 17:21:17 · 2164 阅读 · 0 评论 -
C++手写拉链法的哈希表【LeetCode705 设计哈希集合】
哈希表概述为了实现哈希表这个数据结构,有两个关键的问题,即哈希函数和冲突处理。哈希函数:目的是分配一个地址存储值。理想情况下,每个值都应该有一个对应唯一的散列值。冲突处理:哈希函数的本质就是从 A 映射到 B。但是多个 A 值可能映射到相同的 B。这就是碰撞。因此,我们需要有对应的策略来解决碰撞。总的来说,有以下几种策略解决冲突:拉链法:对于相同的散列值,我们将它们放到一个桶中,每个桶是相互独立的。 线性探测法:每当有碰撞, 则根据我们探查的策略找到一个空的槽为止。 再散列法:使用两原创 2020-11-17 17:16:04 · 1277 阅读 · 1 评论 -
启发式搜索A*算法【引入及思想】
算法介绍A*(念做:A Star)算法是一种很常用的路径查找和图形遍历算法。它有较好的性能和准确度。A*算法最初发表于1968年,由Stanford研究院的Peter Hart, Nils Nilsson以及Bertram Raphael发表。它可以被认为是Dijkstra算法的扩展。由于借助启发函数的引导,A*算法通常拥有更好的性能。广度优先搜索为了更好的理解A*算法,我们首先从广度优先(Breadth First)算法讲起。正如其名称所示,广度优先搜索以广度做为优先级进行搜索。原创 2020-11-13 16:09:36 · 2883 阅读 · 0 评论 -
图的存储方式【邻接矩阵、邻接表、链式前向星】
一个有n个点,m条边的图,分别用以下三种方法存储。一、邻接矩阵用一个二维数组来存储图的信息。有向图和无向图: 对于有向图:a[i][j]!=a[j][i] 对于无向图:a[i][j]==a[j][i]带权图和非带权图: 对于有边权的图,若顶点<i,j>之间有边且边权为x,则a[i][j]=x;若无边,a[i][j]=0 对于非带权图,若顶点<i,j>之间有边,则a[i][j]=1;若无边,a[i][j]=0适用范围:邻接矩阵适合稠密图,...原创 2020-11-03 14:42:58 · 1357 阅读 · 0 评论 -
博弈论【巴什博奕、威佐夫博弈、尼姆博弈】
(一)巴什博奕(Bash Game)只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后光者得胜。显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给.原创 2020-11-13 14:58:02 · 509 阅读 · 0 评论 -
【未完成】线段树的学习和使用
以后找机会写。原创 2020-08-14 10:18:21 · 287 阅读 · 1 评论 -
【未完成】强联通分量的学习与使用
tarjan算法和双层dfs遍历找机会全部研究明白。原创 2020-08-14 21:06:45 · 92 阅读 · 0 评论 -
最长回文前缀的求解【LeetCode 214】
题目描述:给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。示例 :输入:s = "aacecaaa" 输出:"aaacecaaa"提示:0 <= s.length <= 5 * 104 s 仅由小写英文字母组成链接:https://leetcode-cn.com/problems/shortest-palindrome/分析:最终的答案是什么样子呢?假设给s增加前缀为s1。因为s1+s是...原创 2020-11-28 20:23:15 · 823 阅读 · 0 评论 -
反证法证明:为什么KMP算法不会跳过(漏掉)正确的答案
利用反证法,证明KMP算法的正确性为什么不会漏掉正确答案?为什么可以跳到next的位置?原创 2020-11-05 21:16:07 · 2084 阅读 · 22 评论 -
KMP算法:字符串匹配问题【有详细的引入过程,很容易理解掌握】
#include<iostream>#include<string>using namespace std;int match(const string& s,const string& t){ int m=s.size(); int n=t.size(); for (int i=0; i<m; i++){ int j=0; while (i<m && s[i]==t[j]){ i++; j+...原创 2020-11-05 18:54:45 · 3849 阅读 · 1 评论 -
Manacher 算法【马拉车算法详解】最长回文子串
前提:在了解马拉车算法前,首先你得知道中心扩散法,中心扩散法的意思就是枚举每一个字符,假设以其为中心然后向两边扩散,对于每一个字符我们都会计算出一个他能扩散的最大半径,当然这里面要有奇偶中心的考虑。中心扩散法的时间复杂度最坏是O(n^2)。例如字符串是"aaaaaaa",每个字符都会往两边扩散很久。假如回文串的答案长度很短,是2,形如"abccdefgh",那么意味着每个字符往两边扩散的时候,很早就会结束,速度会快很多。所以综上我们可以得出,让中心扩散法变慢的主要原因是那些可以一直扩散的情...原创 2020-10-05 22:26:33 · 336 阅读 · 0 评论 -
最长回文子串【暴力、动态规划、中心扩散、马拉车算法】(LeetCode 5)
目录一、暴力法二、动态规划三、中心扩散法四、Manacher 算法求最长回文子串的方法:(子串一定是连续的,子序列不是)LeetCode题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/一、暴力法枚举子串的起点和终点,O(n^2),再用O(n)的时间判断枚举的这个子串是不是回文的。时间复杂度:O(n^3)空间复杂度:O(1)二、动态规划定义状态: p[ i ] [...原创 2020-10-05 16:42:57 · 280 阅读 · 0 评论 -
并查集模板
并查集建立 f[i]=i;寻找根节点(路径压缩) int find(int x){ if (f[x]==x) return x; else return f[x]=find(f[x]);}合并f[find(x)]=find(y);原创 2020-11-04 20:13:57 · 85 阅读 · 0 评论 -
快速选择算法(线性选择算法)+BFPRT算法优化:O(n)复杂度选择第k大的数
什么是快速选择算法?快速选择算法可以在O(n)的时间复杂度内,选择一个无序随机数组中第k小(大)的元素,它是根据快速排序算法的思想简化而来的。快速选择算法同样利用了分治回归策略,由于只需要选择出第k小(大)的元素,因此它在分治之后只需要考虑符合条件一边的元素情况。它同样利用了快速排序的分割元素集的思想,随机产生一个基数key,将小于基数分到左边,大于基数的分到右边,然后判断基数位置与选择k个最小(大)元素的大小关系,来确定是下一步是选择左区间还是右区间。代码:#include<io.原创 2020-10-19 22:06:20 · 916 阅读 · 0 评论 -
C++手写qsort:快速排序的代码实现【两种写法】
思想:快速排序是基于分治的思想实现的排序算法,对于一个无序数列,我们从中选取一个基数,然后把小于基数的所有数字放到基数前面,大于基数的数字放在基数后面,然后对基数左右的子序列进行同样的操作。代码:#include<iostream>using namespace std;void sort(int a[],int l, int r){ int i=l; int j=r; srand(time(0)); int k=rand()%(j-i+1)+i; //取[l,r]的原创 2020-10-19 16:59:04 · 1169 阅读 · 0 评论 -
二分查找模板和二分答案的应用
二分查找:在一个有序的序列中查找某一个数值,我们可以用传统的for循环,时间复杂度是O(n),但是很明显没有充分利用序列有序这个条件。根据有序这个条件,我们可以每次都选取序列的中间值作为标准,来决定我们下一步的区间选择,这样实现的二分查找,时间复杂度缩减为O(logN)。注意:二分查找写法不谨慎,很容易出现死循环,以下版本注意几个问题: 1. while循环的退出条件是 l < r ,也就是说最终 l 是会等于 r 的,但是 l 这个位置不一定是查找的对象,因为可能根本就不存...原创 2020-10-08 01:09:26 · 164 阅读 · 0 评论 -
为什么说B树和B+树的每个节点对应一个磁盘页,IO操作最坏的情况下是树的高度?
首先,讲一下什么是索引索引就好比是书的目录,比如当我们查看一本字典的时候,目录就相当于我们的对照表,也就是说,目录里的内容来源于书本但却独立于书本,但是呢,目录又是这本书里的一页(●ˇ∀ˇ●),就酱然后,讲一下访存的问题我们都知道,计算机的存储管理是分层的,各层次之间的速度差距比较大,尤其是辅存(磁盘)和主存之间的速度那么,我们都知道磁盘访问比较慢,为啥会这么慢呢?首先,cpu访问磁盘时,磁盘主要干了这些事 1.寻道:磁头摆一摆,找到对应的柱面 2.定位...原创 2020-09-30 10:45:18 · 4485 阅读 · 9 评论 -
B树和B+树【特征,插入、查询、删除操作,以及区别联系】
注意:首先需要说明的一点是:B-树就是B树,没有所谓的B减树B树的起源 我们都知道二叉查找树的查找的时间复杂度是O(log N),其查找效率已经足够高了,那为什么还有B树和B+树的出现呢?难道它两的时间复杂度比二叉查找树还小吗? 答案当然不是,B树和B+树的出现是因为另外一个问题,那就是磁盘IO;众所周知,磁盘的读写操作的效率很低,那么,当在大量数据存储中,查询时我们不能一下子将所有数据加载到内存中,只能逐一加载磁盘页,每个磁盘页对应树的节点。这样会造成大量磁盘IO操作(最坏情况下为树的高度)原创 2020-09-29 15:25:21 · 1479 阅读 · 0 评论 -
最大子矩阵和 (LeetCode面试题17.24)
题目描述:给定一个正整数、负整数和 0 组成的 N × M矩阵,编写代码找出元素总和最大的子矩阵。返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。输入:[ [-1,0], [0,-1]]输出:[0,1,0,1]解释:最大的和是0 答案可以是0 1 0 1或者1 0 1 0说明:1 <= matrix.length, matr...原创 2020-09-26 17:41:20 · 2179 阅读 · 1 评论 -
删除二叉搜索树中的节点【LeetCode 450】(两种方法的实现与比较)
目录题目描述:方法一:方法二:两种删除方法的比较:题目描述:给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的key对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。要求算法时间复杂度为O(h),h 为树的高度。分析:首先我们要明确:实现的是一个函数,最终需要返回删除之后的树的根节点。由于这是一棵二叉搜索树,所以对于要删除的节点我们需要找到他并进行删除操作: 如果当前节点值比key大,则需要删除当前...原创 2020-10-04 21:47:07 · 289 阅读 · 0 评论 -
二叉搜索树的基本操作【结合LeetCode 例题】
目录插入节点(也可以看做建树的过程)中序遍历查找查找最大值、最小值删除全部代码:插入节点(也可以看做建树的过程)递归的插入,如果当前节点大于插入的x,就往左走;反之小于就往右走;递归出口就是走到了一个NULL,在此创建节点即可。//插入节点(也可以看做建树的过程) void Insert(Node* &t, int x){ if (t==NULL){ t=new Node; t->val=x; t->left=NULL; t-&原创 2020-10-04 22:04:27 · 296 阅读 · 0 评论 -
反转链表 【指针迭代法、递归实现】(leetcode-206)
目录题目描述:迭代法:分析:实现:代码:递归法:递归结束的条件:开始翻转:后续翻转:代码:题目描述:反转一个单链表。示例:输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL迭代法:分析:如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一原创 2020-09-09 17:11:30 · 195 阅读 · 0 评论 -
删除链表中某特定值的元素【三种方法】(LeetCode 203)
删除结点的步骤找到该结点的前一个结点 进行删除操作。具体删除方式:我们假设要删除的节点是 i ,那我们只需要找到 i 的前驱 p , 然后让 p -> next =p -> next -> next,这样就跳过了 i 这个节点,自然也就实现了删除的目的。但是这个方法需要找到被删除节点的前驱,对于头结点(也就是第一个节点)来说, 他是没有前驱的,需要特殊处理。下面介绍的方法,第一种是把头结点单独拿出来考虑,第二种是加了一个虚拟头结点,都解决了这个问题。方法一 删除头结点.原创 2020-09-24 20:31:49 · 4512 阅读 · 0 评论 -
创建链表、插入节点、查找节点、打印链表、删除节点
关于链表的基本操作和一些LeetCode题目集合目录1.生成链表的一个节点:2.向链表中插入节点--遍历法:3.向链表中插入节点--尾插法:4.查找链表元素:5.打印链表:6.销毁整个链表:完整代码+测试:附加:删除链表的元素:如果被删除的元素是头结点应该怎么办?1.生成链表的一个节点:void Create_Node(Node* &t,int data){ t=new Node; t->val=data; t->next=NULL;原创 2020-09-24 21:30:54 · 620 阅读 · 0 评论