算法
余丰旭
这个作者很懒,什么都没留下…
展开
-
动态规划之戳气球
leetcode312 戳气球这一题可以用动态规划来解决,但是dp含义的设置和状态转移方程的设计很有意思。首先,一维dp难以实现的,应该考虑二维dp,尤其在一个数组中,要考虑到双指针移动来解决复杂问题。如果将dp[i][j]的含义设置为戳爆下标[i,j]之间所有的气球能获得的最大钱币,会发现由于涉及到与i左边和j右边数字相乘,难以书写状态转移方程。将dp[i][j]的含义设置为:戳爆下标i和j之间(开区间)的所有气球能获得的最大钱币。此时,如果将状态转移方程写为dp[i][j]=max(先戳气球k,原创 2021-08-21 10:11:02 · 344 阅读 · 0 评论 -
排列数与组合数的递归实现
排列数排列数:从N个目标中选出M个进行排列(即使元素相同,其顺序不同,排列也不同)leetcode46 全排列class Solution {public: vector<vector<int>>ans; vector<vector<int>> permute(vector<int>& nums) { dfs(nums,0); return ans; } //处理下标原创 2021-08-19 19:25:21 · 127 阅读 · 0 评论 -
划分为k个相等的子集
leetcode698 划分为k个相等的子集解题之前先将题目等效为更容易求解的问题,这一题可以转换为一组数放入若干个桶中,使每个桶中数字的和均为target。这一题有两种解题视角:1.从数字角度出发,每个数字选择一个桶放入2.从桶角度出发,每个桶选择若干数字放入自身解法1:该解法中,事先对数组进行由大到小的排序,可以大幅提升效率,原因在于:如果本轮的一系列选择最终会导致溢出,那么应该让溢出早点发生,不浪费算力,因此大的放前边可以使迟早会溢出的一组数早点溢出,提高效率。class Solutio原创 2021-08-19 14:50:04 · 158 阅读 · 0 评论 -
leetcode10 正则表达式匹配
正则表达式匹配这一题的关键在于第一,处理好dp的初始状况(i==0, j==0的情况)第二,对复杂问题进行分类讨论。思考清楚当前匹配字符为*时的情况,*可以匹配0个,或者多个前一位的字符,抓住这个特点即可写出状态转移方程*匹配0次:如果dp[i][j-2]==true,则dp[i][j]=true*匹配多次:需要两个条件,第一:dp[i-1][j]==true,第二s当前字符必须和p中*的前一个字符匹配。(思考一下,这是一种有趣的递归写法)class Solution {public:原创 2021-08-18 23:00:53 · 71 阅读 · 0 评论 -
K 站中转内最便宜的航班
这一题是带有限制的带权最短路径题目。方法一:改进的BFS,使用优先级队列(超时)一般遇到最短路径(最少步骤、最近距离)的题目,要下意识地想到用BFS。这一题是带权的,直接bfs不好写,可以借助于优先级队列,这一题中需要用到小根堆,每次选择距离src最近的进行处理class Node{public: int N; //号 int K; //与src之间有多少个中转节点 int D; //与src之间的距离 Node(int n,int k,int d):N(n)原创 2021-08-13 15:50:09 · 124 阅读 · 0 评论 -
最长递增子序列的长度
300最长递增子序列这一题如果使用动态规划来写,那么会到达O(N2)O(N^2)O(N2)的时间复杂度。由于不需要求出具体的最长递增子序列,只需要求出最长的长度,那么可以使用贪心+二分,将时间复杂度降低到O(N∗logN)O(N*logN)O(N∗logN)思路:维护一个升序队列,在遍历nums的过程中,不断优化升序队列的结构(使每个元素尽可能小),这样给后序元素的添加提供了更大的数字空间。class Solution {public: int lengthOfLIS(vector<i原创 2021-08-13 10:06:14 · 240 阅读 · 0 评论 -
leetcode174 地下城游戏
这一题让我学到了两个教训:动态规划先写状态转移方程写动态规划应该思维灵活,有时需要逆向思维//先写状态转移方程,无论是递归还是动态规划/*dp[i][j]=max(1,min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]);*/class Solution {public: int calculateMinimumHP(vector<vector<int>>& dungeon) { int m=dunge原创 2021-08-11 15:54:38 · 77 阅读 · 0 评论 -
贪心...
求最值、求最优解的题目,一般可以用动态规划、贪心、二分来解决。贪心中常常先进行排序。452用最少数量的箭引爆气球考虑到每个气球都要被射中,所以射每个气球的时候,我们希望有尽量多的别的气球也被射到。迭代时,已经被射中的气球就跳过。本题以左边界为标准进行排序,会导致如下的情况,射当前气球无法保证射爆所有与其有重叠的气球:但是以右边界为标准进行排序,只要我在当前气球的右边界射一箭,那么就能射爆所有和其有重叠的:class Solution {public: int findMinArro原创 2021-08-09 10:59:31 · 80 阅读 · 0 评论 -
动态规划之背包问题
01背包416分割等和子集这一题,需要能想到把分割等和子集的问题转化为数组中能否凑出target2\frac{target}{2}2target。一旦转化了稳提,就变成了一个01背包问题。内层for循环从右到左,是避免重复使用一个元素。因为从左往右的话,每次利用的前面的数据都是已经处理过了的,而从右到左就不会出现这个问题。class Solution {public: bool canPartition(vector<int>& nums) { int原创 2021-08-09 08:29:02 · 81 阅读 · 0 评论 -
非递归实现树的三种遍历
中序遍历前序遍历后序遍历原创 2021-08-08 17:30:29 · 368 阅读 · 0 评论 -
求平方根的两种方法
二分二分法求平方根其实是将问题转化为求x2≤targetx^{2}{\leq}targetx2≤target中x的最大整数值牛顿迭代法原创 2021-08-08 14:00:17 · 281 阅读 · 0 评论 -
动态规划思考与总结
动态规划其实是运筹学的一种最优化方法,其核心思想是穷举/暴力搜索,只不过因为存在大量重叠子问题,所以需要避免重复计算。备忘录、DP table 就是在追求“如何聪明地穷举”。用空间换时间的思路,是降低时间复杂度的不二法门。动态规划三要素重复子问题存在大量重复计算的子问题最优子结构要符合「最优子结构」,子问题间必须互相独立。公众号labuladong作者对这个问题作如下解读:比如说,假设你考试,每门科目的成绩都是互相独立的。你的原问题是考出最高的总成绩,那么你的子问题就是要把语文考到最高,数原创 2021-08-06 09:51:12 · 410 阅读 · 0 评论 -
leetcode710黑名单中的随机数
710黑名单中的随机数涉及到等概率的问题,一般都得使用数组(用随机数作下标等概率随机返回一个数)来解决,数组即为一种顺序存储的结构,因此逻辑上顺序,也能够满足要求。下面的图片用√表示有效数字,X表示在黑名单中的数字。其中,虚线是,最终将所有有效数字挪到左侧以后,和右侧黑名单数字的分界线(用总数量减去黑名单数量,即可得到最终有效数字的数量)如图,根据虚线划分位置的数学原因,左侧X的数量和右侧√数量相同,因此只要将左侧的X映射到右侧每个√,即可完成任务。后序在虚线左侧随机返回一个下标值,就达到了题目的要求原创 2021-08-04 12:42:37 · 99 阅读 · 0 评论 -
双指针之滑动窗口
滑动窗口是双指针一个比较经典的应用,其执行过程可以概括为:1.初始left=0;right=02.向右移动right,扩大窗口,寻找可行解3.找到可行解后,向右移动left,收缩窗口,优化可行解4.循环执行第2步和第3步,直到right到达边界76最小覆盖子串class Solution {public://滑动窗口思想:每轮先找到可行解,然后优化可行解 string minWindow(string s, string t) { //结果 int l原创 2021-08-02 18:44:38 · 163 阅读 · 0 评论 -
二分搜索经典题目
第一题410分割数组的最大值遇到诸如 “xx最大值的最小”,这类问题,一般都可以采用二分来解决。这一题正向思维不好解决,需要逆向思维,直接对分组和的最大值进行二分搜索,其下限是数组中最大元素,上限是所有元素的和。枚举分组和的最大值后,逆向判断在此情况下,能否使得数组分为m个分组(贪心解决)。class Solution {public: bool check(vector<int>& nums, int x, int m) { long long sum原创 2021-08-02 11:27:44 · 85 阅读 · 0 评论 -
递归删除不带头节点的链表中所有值为x的节点
题目如标题所示,这一题有个很关键的问题,在于传参引用,然后修改传入指针的指向,只有这样才能完成递归操作。值得一提的是,按我目前的java知识水平来丽姐,Java似乎无法完成这个算法,因为java只有值传递,因此无法完成这个算法。//递归删除没有头节点的链表中所有值为x的节点void deleteX(Node *p, int x) { if (!p) return; if (p->val == x) { //Node*tmp=p; //释放节点原创 2021-08-02 09:46:03 · 279 阅读 · 0 评论 -
原地删除序列某些元素的两种方法
遇到删除序列中所有不符合条件的元素时,均可以使用以下两种算法,原地修改序列,时间复杂度O(N),空间复杂度O(1)。如:删除序列中大于等于s,小于等于t的所有元素。k记录不用删除元素的个数void deleteTarget(vector<int>arr,int s,int t){ int k=0; /*记录不删除、需要保留下来的元素的个数*/ for(int i=0;i<arr.size();i++){ if(arr[i]<s || arr原创 2021-08-01 09:58:53 · 287 阅读 · 0 评论 -
二分搜索的三种形式
二分搜索可以在有序序列中,以logN的时间复杂度快速找到答案。常见的问题有三种,第一:找到目标值,第二:找到符合要求的最小值,第三:找到符合要求的最大值。对应的二分搜索也有三种形式。注意:二分搜索也可以是逻辑上的第一种:标准二分搜索,返回目标值下标,没找到返回-1int binarySearch(vector<int>&arr,int target){ int left=0,right=arr.size()-1; while(left<=right){原创 2021-07-31 21:15:55 · 150 阅读 · 0 评论 -
贪心之田忌赛马
今天遇到一道算法题870优势洗牌,发现这个问题类似著名的田忌赛马问题。使用贪心算法可以有效解决这个问题,其思想核心是:比得过就比,比不过就找个最菜的垫背。下面的C++代码也值得品味,使用了Lambda表达式。class Solution {public: vector<int> advantageCount(vector<int> &nums1, vector<int> &nums2) { int len = nums1.s原创 2021-07-30 21:02:41 · 74 阅读 · 0 评论 -
最长递增子序列
这篇文章讲的很细:https://blog.csdn.net/lxt_Lucia/article/details/81206439方法一:动态规划时间复杂度nlognn\log{n}nlogndp[i]:以第i个元素为结尾的子序列的最大长度状态转移方程:dp[i]=max(dp[j]+1),其中arr[i]>arr[j],i>jdp[i]=max( dp[j]+1), 其中arr[i]>arr[j], i>jdp[i]=max(dp[j]+1),其中arr[i]>.原创 2021-05-30 17:13:29 · 46 阅读 · 0 评论 -
质因数分解及其幂次求解
涉及到算术基本定理和约数定理的题目经常需要求质因数及其幂次,下面是一种高效的求法#include <iostream>#include <cstring>using namespace std;int primePower[101];const int N = 100; int power[N + 1]; //power[i]是质因子i的幂次void findPrimeFactorPower() { int x = N; //x不断缩小,直至不再能分解原创 2021-05-29 16:42:06 · 911 阅读 · 0 评论 -
常用的数学知识
算数基本定理(唯一分解定理)对于一个大于1的整数n,n可以分解质因数为:∏i=1kpiai=p1a1⋅p2a2⋯pkak\prod_{i=1}^kp_i^{a_i}={p_1^{a_1}}\cdot{p_2^{a_2}}\cdots{p_k^{a_k}}∏i=1kpiai=p1a1⋅p2a2⋯pkak其中pip_ipi表示n的第i个质因子例:18 = 212^121 × 323^232约数定理:一个正整数n的正约数的个数 d(n)=∏i=1k(ai+1)=(a1+.原创 2021-05-29 15:14:06 · 62 阅读 · 0 评论 -
两种经典最短路径算法
djikstral算法:计算单源最短路径(固定起点,计算出起点到其他所有顶点的最短路径)用贪心思想,每次找出距离起点最近的节点,直到找出所有节点floyd算法:计算任意两点之间的最短路径基于动态规划思想三重循环遍历所有顶点,如果a到c再到b的距离,小于a到b的距离,那么将a到b原来的路径更换为a到c再到b其中路径信息parent矩阵,parent[i][j]代表的是,从i到j,i走过以后,下一步应该走哪个节点,如果parent[i][j]==j说明i、j相邻。/* * 最短路路.原创 2021-05-25 11:57:23 · 2288 阅读 · 0 评论 -
拓展欧几里得算法
欧几里得算法又叫辗转相除法int gcd(int a,int b){ return !b?a:gcd(b,a);}ax+by=gcd(a,b)求解使用拓展欧几里得算法int extgcd(int a,int b,int &x,int &y){ int temp=a; if(!b){ x=1,y=0; } else{ temp=extgcd(b,a%b,y,x); y-=(a/b)*x; }原创 2021-05-18 11:38:58 · 77 阅读 · 0 评论 -
树状数组
引入问题给出一个长度为n的数组,完成以下两种操作将第x个数加上k输出区间[x,y]内每个数的和使用暴力算法单点修改:O(1)区间查询:O(n)对于大数据来说,这样的复杂度是不能接受的树状数组处理:单点修改:O(logn)区间查询:O(logn)可以应对非常大规模的数据前置知识——lowbit()操作lowbit()操作:非负整数n在二进制表示下最低为1及其后面的0构成的数值例如:lowbit(44) = lowbit( (101100)2) =(100)2 =4求原创 2021-05-15 15:33:49 · 73 阅读 · 0 评论 -
两种经典的最小生成树算法
B站一个Up主的动画演示非常生动形象:https://www.bilibili.com/video/BV1Eb41177d1?from=search&seid=9719056953147312216最小生成树最小生成树要求无环图联通图(任意两点之间必须有通路)N个顶点,N-1条边所有生成树之中权值和最小的那棵(MST不唯一)Kruskal算法基于贪婪算法思想,每次选择权值最小的边算法过程1. 将所有边按照权值从小到大排序2. 每次选择最小的边试图加入结果集3. 如.原创 2021-05-14 11:26:02 · 221 阅读 · 0 评论 -
状态压缩dp
理解当出现NPC问题,但是题目给出的N在20左右时,一般用状态压缩dp来解决状态压缩dp本质还是动态规划,只不过是将一些不方便表示的状态用二进制数的形式来表示。例题例题1#include <iostream>#include <cstdio>using namespace std;typedef long long ll;int N, K;ll dp[9][(1 << 9)][82] = {0}; //dp[i][st][j] 表示0~i行原创 2021-05-10 21:35:49 · 139 阅读 · 0 评论 -
蓝桥杯国赛准备
位运算位运算规则>>右移运算符对于无符号数,左边补0对于有符号数,左边补符号位的数任何数与00...00异或都等于自身,与111..11异或等同按位取反。位运算作用判断奇偶x & 1 结果是1则是奇数,为0则是偶数获取二进制位是1还是0用对应二进制位1,其余位0的数与之相与,再右移到最低位与1取余即可交换两个整数变量的值三次异或a=a^b;b=a^b;a=a^b;不用判断语句,求整数的绝对值负数位运算求绝对值原理:负数补码原创 2021-05-01 16:33:55 · 437 阅读 · 0 评论 -
复杂动态规划之状态机解法
刷到leetcode309 最佳买卖股票时机含冷冻期时,学习了一种状态机的解法,觉得很好用,遂记录一下。对于这类复杂的状态转移问题,可以通过建立多个状态以及他们之间的转移方程来解决。对于该题,则定义四种状态,买入,卖出,持有,和不持有,其值代表当前状态下最大的利润。状态机及之间的转换如下:#include <vector>#include <algorithm>using namespace std;class Solution {public: int.原创 2021-04-11 15:15:38 · 151 阅读 · 0 评论 -
背包问题解析
0-1背包问题问题描述:有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每件物品都只有1件。样例:5 8 // n==5, V==83 5 1 2 2 // w[i]4 5 2 1 3 // c[i]答案:10使用暴力法时间复杂度为O(2n),可以使用二维DP解决该问题,时间复杂度为O(nV)。其中dp[i][j]表示:在前i个物品中挑选若干,使容量为j的背包,能装下的最大价值。那么状态转移方程即为原创 2021-04-10 11:59:07 · 160 阅读 · 0 评论 -
N皇后问题
解决完N皇后问题后,将思路加以记录首先我整体的方法使用的是DFS+回溯,每次递归对第k行,在哪一列放置Queen进行处理。函数主要有:辅助函数assist,冲突位置记录函数choose,冲突位置回溯函数backTrace,其中辅助函数完成主要任务。对于辅助函数,终止条件是递归到第n行(只有0~n-1行,递归到第n行说明0~n-1行都成功放置了皇后)辅助函数的递归+回溯的逻辑是在当前行一个无冲突位置放置皇后(记录当前行放置的皇后的位置,同时在mark数组种记录因此而产生的冲突)递归下一行原创 2021-03-29 22:12:47 · 171 阅读 · 0 评论 -
常用排序算法
快速排序/** * 快速排序:每次选取一个基准,小的放左边,大的放右边 * @param arr */void quickSort(int arr[],int left,int right){ if(left>=right) return; int pivot=arr[left]; int p=left,l=left,r=right; //p表示马上要处理的元素,l左边都小于基准,r右边都大于基准 while(p<=r){原创 2021-03-22 17:43:47 · 44 阅读 · 0 评论 -
搜索旋转排序数组
刷到leetcode81题搜索旋转排序数组觉得有点意思,遂记录下解题思路和过程。要求实现O(logn)的时间复杂度。要抓住二分查找的核心,其在于每次排除掉一边的区间,故不在乎整个数组是否是完全递增的,只要每次能排除掉一边的区间,选择另一个区间继续进行二分查找即可。对于这一题,数组的大小顺序形如下图:我们以右端点为参照对象进行分类,当中间值==target时返回true,否则:如果mid值小于右端点时:如果中间值<target<=右端点,则下一步对mid右侧进行二分查找,否则.原创 2021-03-21 10:30:13 · 50 阅读 · 0 评论 -
Floyd判圈法
Floyd判圈法用于判断一个链表中是否有环,其算法分为两个部分。第一部分:判断是否有环其算法应用龟兔赛跑的思想,使用一个slow和fast指针初始都指向链表第一个节点,slow每次向前走一步,fast向前走两步。如果链表无环,那么fast会先走到NULL节点。如果有环,那么当slow和fast都进入环的时候,由于fast比slow走的快,fast总会追上slow。C++代码如下:bool hasCycle(ListNode *head) { ListNode*slow=head,*fa原创 2021-03-18 21:14:06 · 218 阅读 · 0 评论 -
欧拉筛质数(线性筛)
适用场景:求2~N范围内的素数优点:线性筛,复杂度为O(n)。与埃氏筛相比,不会对已经被标记过的合数再进行重复标记,故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个合数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。流程:我们知道当一个数为素数的时候,它的倍数肯定不是素数。所以我们可以从2开始通过乘积筛掉所有的合数。避免重复筛除原理:任何合数都能表示成多个素数的积。所以,任何的合数肯定有一个最小质因子。我们通过这个最小质因子就可以判断什么时候不用继续筛下去了。比如对于77,它分解.原创 2021-03-15 19:21:55 · 515 阅读 · 0 评论 -
日常编程心得
算法学习笔记2020/12/23对于递归函数,其空间复杂度与最大递归深度有关递归的诀窍在于找到形式相同但规模更小的子问题python函数中可以定义函数,但是参数不能不能写self,调用的时候直接写函数名即可树的深度优先遍历,一条路径末端不一定是叶节点!如:这里的1向右深度优先递归结束了,1不是叶节点2020/12/24python判断是否是字母或者数字的函数isalnum动态规划的精髓在于找到第i项和前i-1项(或第i-1项)的关系动态规划是由规模较小的问题不断得到较大规原创 2021-03-15 18:43:03 · 786 阅读 · 0 评论 -
KMP算法个人理解
原创 2021-03-15 10:26:23 · 47 阅读 · 0 评论 -
汉诺塔问题
递归方法,每次将上面的n-1个盘子看成一个整体public class Question6汉诺塔问题 { public static void main(String[] args) { int n=new Scanner(System.in).nextInt(); move(n,'A','B','C'); } //借助B把A上的盘子,移动...原创 2020-04-23 16:36:01 · 123 阅读 · 0 评论 -
动态规划——装箱问题
使用动态规划,dp[i]记录当容积为i时的最大填充体积import java.util.Arrays;import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int...原创 2020-04-26 10:17:22 · 1159 阅读 · 2 评论 -
最长回文子序列
刷算法题时遇到这道求最长回文子序列长度的题,因为其子序列不要求连续,跟普通求回文子序列不同,因此加以记录题目描述:给定一仅由大小写字母组成的字符串,求其回文子序列的最大长度,回文子序列指正反读都一样的子序列,如madam。本题中大小写不敏感,即a和A相同。输入描述:输入包含一行字符串,长度不超过300输出描述:输出该字符串最长回文子序列的长度输入样例:ABCdca输出样例:5提示...原创 2020-04-25 14:09:22 · 117 阅读 · 0 评论