![](https://img-blog.csdnimg.cn/20201014180756926.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
算法
文章平均质量分 94
王江奎
这个作者很懒,什么都没留下…
展开
-
链表三连击
876:链表的中间节点206:反转链表143:重排练表链表的中间节点这个题一看就是最简单的快慢指针,但是在具体实现的时候我还是犹豫思考了一下:要不要在链表前面放置哑节点,快指针应该什么时候判断已经到达结尾。但是单纯的想并没有什么结果。对于这种不是算法本身的问题,而只是实现细节的问题,不要多想,没有很大的意义,只要对于每种情况都动手(脑)模拟一遍,看这么样能够让情形变得更加简单即可。我思考了一下以后写了一下自己的代码:/** * Definition for singly-linked list原创 2020-10-20 23:09:33 · 140 阅读 · 0 评论 -
844. Backspace String Compare
题目的意思大概是有两个字符串,其中的#表示退格键,让比较最后两个字符串是否相当。很容易想到的思路就是用一个栈进行模拟这个过程,特别需要注意如果一个串是空串也是可以退格的。但是很容易想到的另一个特性就是,前面的字符有可能被后面的退格符删去,但是如果如果从后往前进行遍历的话那么一个字符一旦保留就不会被删除,我们比较两个字符串保留下来的字符,一旦发现不相等就直接返回。总体来讲就是双指针加逆序遍历。其实看到这个题我直接就想到双指针加逆序遍历了。但是昨天晚上也不知道是因为困了还是什么原因,写了二十分钟写的都是原创 2020-10-20 13:48:32 · 113 阅读 · 0 评论 -
19 删除链表的倒数第N个
题目的意思很简单,就是删除一个链表倒数第N个节点。需要用到链表的标准操作:快慢指针。我们让一个快指针先指向第N个元素,这个时候快指针总比慢指针领先N个元素,等到快指针指向链表尾部的时候慢指针就指向需要删除的元素。之前已经用了几次了(比如空间复杂度O(1)O(1)O(1)判断链表是否有环以及环的位置),但是我看到这道题竟然还是一下没有想到。顺带忏悔一下,之前说好每天至少一道题解的,但是前面忙其他的事就没有弄了,罪过罪过,后面要继续坚持。看到提接学到链表的一个操作:哑指针。因为我们一般得到的都是链表的原创 2020-10-19 00:01:17 · 70 阅读 · 0 评论 -
141 环形链表
要求使用空间复杂度为O(1)的方法,可是我并没有想到。我想到的只有用一个哈希表记录一下所有访问过的节点。题解给出的空间复杂度为O(1)的方法是使用两个指针,然后让一个一次跑一步,一个一次跑两步,如果跑的快的能追上跑的慢的就是有环,如果跑得快的跑到了链表的末尾就是没有环。设置跑的快的比跑的慢的多跑一步,这样对一个长度至多为N的环,总会追上的,时间复杂度为O(N)。需要注意处理循环条件,并且因为跑的快的节点每次要跑两步,要处理如果没有环跑的快的节点跑一步就跑到结尾的情况。/** * Definition原创 2020-10-09 13:32:40 · 88 阅读 · 0 评论 -
75 颜色分类
题目已经提示可以一遍扫描了但是我还是没有想到,其实双指针的想法我已经有了,但是一想到有问题就觉得无法实现。这也揭示了我思维上的问题:用一种方法解决问题遇到困难第一件事情不是想着如何攻克而是想着换一种方法。对自己的思维也不自信。我自己简单了写了一个两遍扫描的程序:class Solution {public: vector<int> cnt; void sortColors(vector<int>& nums) { cnt.resize(3,原创 2020-10-08 10:37:05 · 121 阅读 · 0 评论 -
834 树中距离之和
这道题我自己的想法只有对每个点都用一遍Dijkstra然后再求和,显然会超时,所以我都没有尝试。研究了一下题解,发现题解很巧妙,自己对树的处理还是太稚嫩,之前树链剖分学的都忘光了。对于固定根节点的,我们应该使用树状dp:dp[u]=∑v∈son(u)dp[v]+sz[v]dp[u]=\sum_{v\in son(u)} dp[v]+sz[v]dp[u]=v∈son(u)∑dp[v]+sz[v]其中,dp[u]dp[u]dp[u]表示以u为根节点的子树中根节点u到其儿子节点的所有距离之和,之所以原创 2020-10-06 18:28:16 · 110 阅读 · 0 评论 -
1 两数之和
虽然只是一道很简单的题,但是也给我很多思考。刚看到这道题的时候没有仔细思考,直接写了个排序和二分查找,想着对每个数字查找另一个数字会不会出现,复杂度是O(nlogn+nlogn)O(nlogn+nlogn)O(nlogn+nlogn),主要训练了一下自己手写快速排序和二分查找。class Solution { void swap(vector<int>& a,vector<int>& b,int i,int j) { int t=a原创 2020-10-06 17:14:06 · 139 阅读 · 0 评论 -
P、NP、NP完全问题、NP难问题
可以在多项式时间内求解的问题称为易解的,而不能在多项式时间内求解的问题称为难解的。P类问题:多项式类型,是一类能够用(确定性的)算法在多项式的时间内求解的判定问题。只有判定问题才属于P不可判定问题:某些判定问题是不能用任何算法求解的,则称这种判定问题为不可判定问题。否则就称作可判定问题。例如:Halting problem(停机问题):给定一段计算机程序和他的一个输入,判断该程序对于该输入是会中止还是会无限的运行。证明停机问题是不可判定问题:反证法,通过构造一个输出和解决停机问题的算法的输出相反的原创 2020-09-02 23:18:35 · 3967 阅读 · 0 评论 -
各种排序算法总结
插入排序算法思想将数组分为有序区和无序区,每次把无序区中和有序区交界的元素加入有序区,直到所有的元素都处于有序区。加入的方法是:从有序区中从后往前寻找合适的位置,然后插入。这里合适的位置是指,如果从小到大进行排序,则应该插入的位置是前面比他都小,后面比他都大的。当然我们可以对相等的情况进行判断,是选择继续前移还是停止移动。因此不难发现,插入排序具有稳定性。在寻找合适的插入位置的时候我们可以同时完成数组的移动操作。算法实现按照上面的思路我们可以直接进行实现void insert_sort(in原创 2020-09-02 21:37:51 · 179 阅读 · 0 评论 -
斐波那契数列计算
定义斐波那契数列:F[n]{0,n=01,n=1F[n−1]+F[n−2],elseF[n]\begin{cases}0,n=0 \\1,n=1\\F[n-1]+F[n-2],else\end{cases}F[n]⎩⎪⎨⎪⎧0,n=01,n=1F[n−1]+F[n−2],else朴素计算法根据递归式F[n]=F[n−1]+F[n−2]F[n]=F[n-1]+F[n-2]F[n]=F[n−1]+F[n−2]进行计算,时间复杂度T(n)=T(n−1)+T(n−2)=ϕnT(n)=T(n原创 2020-09-02 16:24:19 · 208 阅读 · 0 评论 -
递归式复杂度求解
代换法猜测复杂度验证是否满足递归式(使用归纳法)找到常数应该满足的条件针对基本情况,常数足够大时总是成立的需要注意的是,我们猜测的复杂度有可能不满足递归式,这个时候就要通过减去一些低阶项来使得归纳成立。对递归式T(n)=4T(n/2)+CnT(n)=4T(n/2)+CnT(n)=4T(n/2)+Cn我们假设T(n)=Θ(n2)⩽C1n2−C2nT(n)=\Theta(n^2) \leqslant C_1n^2-C_2nT(n)=Θ(n2)⩽C1n2−C2n,下面进行归纳证明:假设k&原创 2020-09-02 11:13:47 · 313 阅读 · 0 评论 -
Bellman-Ford算法和SPFA算法
Belloman-Ford算法算法介绍Dijkstra可以解决单源无负边最短路径问题。但是当遇到含有负边的单源最短路径问题就需要使用Bellman-Ford算法来解决。Bellman-Ford算法还可以检测出负环。算法步骤源点s,数组d[u]d[u]d[u]表示s到u的最短距离初始化:d[s]=0d[s]=0d[s]=0,∀v∈V−{s},d[v]=∞\forall v \in V-\{s\},d[v]=\infin∀v∈V−{s},d[v]=∞循环∣V∣−1|V|-1∣V∣−1次,每次对每条原创 2020-06-07 16:58:57 · 282 阅读 · 0 评论 -
将字符串中的空格用%20替换
如果不需要原地操作,则一遍遍历,将非空串复制,遇到空格加上%20,如果需要原地操作,首先进行遍历出空格的个数x,然后扩容2x,从后往前遍历实现。如果非空格字符串比空格字符串多的多的时候而且字符串非常长的时候使用原地操作节省空间。下面证明不会发生冲突:字符串长度为n,空格数为x,扩容以后为n+2x。从后往前遍历到第i个字符的时候,假设经过了j个空格,j⩽\leqslant⩽x,新串占用空间为i+2j,总空间为n+2x,则新串所占用串的头地址为n+2x-i-2j,旧串所占用串的尾地址为n-i-1<n+原创 2020-05-23 19:18:38 · 281 阅读 · 0 评论 -
LCS最长公共子串
问题介绍LCS问题(longest common subsequence problem)指的是求解两个字符串最长公共子序列问题。这里的子序列是可以不连续的。LCS问题广泛地出现在计算生物学中(DNA序列、系统生成树等等)。这里介绍如何解决LCS问题,以及算法的正确性证明和性能分析。解决方案假设需要求解串X,Y的LCS,其中∣X∣=n,∣Y∣=m,c[i][j]表示X[1..i]和Y[1..j]的LCS长度,z[1..k]表示X[1..i]和Y[1..j]的LCS,k=c[i][j]|X|=n,|Y|原创 2020-05-23 19:15:29 · 418 阅读 · 0 评论 -
斐波那契数列求解+尾递归
1.普通递归这里观察f[4]的递归树代替f[10]的递归树(后者比较大,画不下)。使用递归求解的时候复杂度为T(n)=T(n−1)+T(n−2)T(n)=T(n-1)+T(n-2)T(n)=T(n−1)+T(n−2),观察递归树,发现降速最快的是最右边每次减2,因此n2\frac{n}{2}2n层以上的部分肯定是满二叉树,因此时间复杂度肯定是Ω(2n2)\Omega(2^{\frac{n}{2}})Ω(22n)的,再加上其他节点,因此我们可以大概认为时间复杂度为O(2n)O(2^n)O(2n),空原创 2020-05-22 15:56:47 · 900 阅读 · 0 评论 -
Dijkstra算法介绍+正确性证明+性能分析
算法介绍源点s,数组d[u]表示s到u的最短距离,空集S,点集Q初始化:将源点s从点集中去掉,加入S,d[s]=0,∀v∈Q,d[v]=∞\forall v\in Q ,d[v]=\infty∀v∈Q,d[v]=∞将Q中d[v]最小的点去掉加入s,并对u∈Q,w[u][v]<∞u\in Q,w[u][v]<\inftyu∈Q,w[u][v]<∞进行松弛操作:如果d[v]+w[v,u]<d[u],d[u]=d[v]+w[v,u]d[v]+w[v,u]<d[u],d[u]=原创 2020-05-22 15:45:52 · 2606 阅读 · 0 评论 -
哈夫曼算法证明+哈夫曼编码译码程序实现
哈夫曼算法证明哈夫曼算法是一种贪心算法,我们考虑证明其最优子结构和贪心选择性质:最优子结构:假设一个树是哈夫曼树,则以其任意节点为根节点的最大子树也是哈夫曼树。证明:子树的根节点的值是其所有叶子节点出现权值之和,因此无论子树是什么形式,对子树上方的节点计算WPL2都没有影响。根据哈夫曼树的定义:WPL最小的二叉树。如果子树不是哈夫曼树,其WPL1就不会是最小,那么整个树的WPL=WPL1+WPL2就不会是最小,这与哈夫曼树的定义相悖,因此子树是哈夫曼树。贪心选择性质(哈夫曼算法):每次去掉权原创 2020-05-16 16:03:07 · 485 阅读 · 0 评论 -
随机化快速排序+快速选择 复杂度证明+运行测试
对于快速排序和快速选择我之前的文章已经有详细的说明,需要了解的同学可以移步传送门:快速排序|快速选择(BFPTR)所谓随机化其实就是选择枢纽的时候使用随机数选择而已,实现起来很简单。但是我们使用随机数如何保证复杂度呢?首先,我们假定随机数的确是随机的,即我们选取任何一个元素作为枢纽都是有可能的。我们使用指示器随机变量xix_ixi,如果选择第iii个元素作为枢纽则xi=1x_i=1xi...原创 2020-04-30 18:11:43 · 1397 阅读 · 0 评论 -
BFPTR算法详解+实现+复杂度证明
BFPTR算法是由Blum、Floyed、Pratt、Tarjan、Rivest这五位牛人一起提出来的,其特点在于可以以最坏复杂度为O(n)O(n)O(n)地求解top−ktop-ktop−k问题。所谓top−ktop-ktop−k问题就是从一个序列中求解其第k大的问题。这个算法的介绍更详细的可以参照算法导论9.3节 Selection in worst-case linear time,这里记...原创 2020-04-30 13:27:36 · 3566 阅读 · 2 评论 -
证明AVL树的上界和下界
对于n个节点的AVL树,其高度最低的时候肯定为叶子节点只在最后一层和倒数第二层的时候。即对于2k−1<n≦2k+1−12^k-1< n\leqq 2^{k+1}-12k−1<n≦2k+1−1的时候下界都为kkk。因此下界为h=┌log2(n+1)┐−1h=\ulcorner log_2(n+1)\urcorner-1h=┌log2(n+1)┐−1对于上界,我们可以将问题转换为...原创 2020-04-28 20:21:55 · 509 阅读 · 0 评论 -
计算矩阵的逆和行列式的值(高斯消元+LU分解)
计算矩阵的逆选主元的高斯消元法朴素的高斯消元法是将矩阵A和单位矩阵放在一起,通过行操作(或者列操作)将A变为单位矩阵,这个时候单位矩阵就是矩阵A的逆矩阵。从上到下将A变为上三角矩阵的复杂度为O(n3n^3n3),再从下往上将上三角矩阵变化为单位矩阵复杂度为O(n3n^3n3),因此总共的复杂度为O(n3n^3n3) 。还有一种做法是按照高斯消元接线性方程组的方式求解n次线性方程组,这样复...原创 2020-04-28 20:17:07 · 2810 阅读 · 3 评论 -
快速排序详解+各种实现方式
快速排序的思想大体来说比较简单,就是从数组中挑选一个数字当做枢纽,然后将比枢纽大的和比枢纽小的分别放在枢纽的两边,再递归地对两边进行操作,从而进行分治解决问题。平均情况下快速排序是复杂度为O(nlogn)O(nlogn)O(nlogn),可是有时候复杂度会退化为O(n2)O(n^2)O(n2),这与我们如何选择枢纽以及如何将数组进行划分有关。 总共有两种情况下复杂度会退化:数组大体有序:...原创 2020-04-28 20:07:32 · 652 阅读 · 0 评论 -
二分查找的最大比较次数
二分查找很简单,可是对于一个区间长度为n的数组,最大的比较次数为多少呢?对于标准的二分查找,我们每次从区间[l,r)中取一个值,和中间值mid=(l+r)>>1进行比较,然后将数组分为[l,mid) [mid+1,r),即每次将区间长度x变为(x-1)>>1。最大比较次数显然是我们想要查找的数并不在数组中的时候,这样的话我们需要将区间长度变为0才能结束比较。这样直接分析有...原创 2020-04-23 12:18:21 · 12014 阅读 · 1 评论 -
斐波那契查找(Fibonacci Search)和折半查找
两个查找算法都是针对有序数组进行查找,不同点在于分界点的取值不同。算法介绍折半查找很简单,每次与当前区间的中点进行比较,然后决定查找前一部分还是后一部分。Fibonacci查找利用了Fibonacci序列每一项等于前两项和的特点进行划分,然后再在前一部分或者后一部分进行查找。对于一个数组,我们首先将他填充成长度为F[k]-1的数组,其中F[]表示斐波那契数列。为什么非要填充成F[k]-...原创 2020-04-16 19:39:06 · 798 阅读 · 0 评论 -
求序列第K大算法总结
参考博客:传送门在上面的博客中介绍了求序列第K大的几种算法,感觉收益良多,其中最精巧的还是利用快速排序的思想O(n)查询的算法。仔细学习以后我将其中的几个实现了一下。写算法返回一系列数中的第K小 数。解法 1:将乱序数组从大到小进行排序然后取出前K大,总的时间复杂度为O(nlogn)解法 2:将乱序数组按照从大到小进行排序,取出前K大,总的时间复杂度为O(nk)解法 3:借鉴快...原创 2020-04-09 16:12:00 · 635 阅读 · 0 评论