算法
CleanUp Hitter
学以致用
展开
-
Morris法解决二叉树问题,展开链表及中序遍历
咋一看非常简单的两道题,但是如果我们加以一些限制,这两题就不简单了。对于这两道题,我们的空间复杂度都必须控制在O(1)。也就是说,迭代和递归全部失效了,这怎么办呢?找到一个节点的前驱节点,然后全部拼接到原来的右边,原来的右边节点直接拼到前驱节点的右边即可。就是将叶子节点指向他的后驱节点。就是阉割版的线索二叉树啊。这样子我们就不需要通过栈来进行存储节点。Morris算法 Morris 遍历算法是另一种遍历二叉树的方法,它能将非递归的中序遍历空间复杂度降为 O(1)。于是我们的主角Morris就出现了。原创 2024-03-18 19:16:54 · 223 阅读 · 0 评论 -
排序链表的三种写法
第三种,归并排序,主要就是分为归并和合并两部分。拆分时候,通过快慢指针拆出来两个链表头。第二种,推荐面试直接写,堆排序,借助优先队列,能5分钟秒了。第一种,插入排序,会超时。原创 2024-03-15 16:05:35 · 241 阅读 · 0 评论 -
环形链表的起点——细节讲解
也就是说我们把一个节点放头部重新遍历,再次相交的点就是入环处。对于一个环形链表,我们要找到他的起点。首先快慢指针不能岔开设置,不然推导的T不一样了。到起点的距离是T,圈长是C,第一次相交的点是X。最后要记得特判fast的下一个节点是否存在。其次一开始就要移动,不然一开始指针一样了。我们设置两个快慢指针,相遇的点为X.然后快指针相遇后只能一步一步移动了。化出来T=N*C-X。原创 2024-03-14 18:04:58 · 316 阅读 · 0 评论 -
最长回文子串超级简单的做法——两次中心扩散
每次进入循环,指针设置一下两边扩散的地点。奇偶指针设置区分一下就行。常见的三种做法分别是动态规划,马拉车和中心扩散。我们分奇偶串进行中心扩散就能找到最长的回文串啦。原创 2024-03-12 15:29:29 · 248 阅读 · 0 评论 -
手撕经典数据结构——堆
在操作过程中我们需要用到查看父亲节点函数,查看左孩子节点函数,查看右孩子节点函数和交换元素位置函数。除了上面之外,插入和删除两个操作需要涉及到堆的元素上浮函数和元素下沉函数。我们需要定义一个数组,int类型长度和int类型容量。堆的函数主要有,插入,删除,查看堆顶元素。建堆主要依靠插入函数。原创 2024-03-02 20:56:35 · 366 阅读 · 0 评论 -
手撕LRU缓存——LinkedHashMap简易源码
首先我们要知道什么是LRU就是最小使用淘汰。怎么淘汰,链表尾部就是最不常用的直接删除,最近使用过的就移动到链表头部。要查找就利用hash表进行映射查找。原理非常简单,一个双端链表配上一个hash表。原创 2024-02-29 20:07:58 · 773 阅读 · 0 评论 -
经典单调栈问题——接雨水
从左到右遍历数组,遍历到下标 i 时,发现不满足单调递减。这时候要保证栈内有两个元素,一个作为中间的间隔标识,一个作为左边的支点。这时候先弹出来一个,然后记录下一个栈顶。这时候可以接的雨水为长*宽(左右两端的距离×左边支点和右边支点最小值和弹出来节点的差)。维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组 height 中的元素递减。原创 2024-02-27 10:18:10 · 203 阅读 · 0 评论 -
力扣Hot100——柱状图中最大矩形(单调栈)
思路:考虑单调递增栈,如果说我拿到的数字比栈顶来的要小,那么说明了我前面比我高的会被我自身的高度限制,所以前面的矩阵要依次出栈统计出来这个局部的最高高度,然后把目前的高度+前面比我高的宽度压入栈中。这题拿数组去模拟栈效果更加好。原创 2023-12-28 14:05:50 · 499 阅读 · 0 评论 -
单调栈数组问题——每日温度
思路:维护一个递减栈。由于每一次我们都需要知道后面一个较大的值,那么首先遍历数组,如果数是递减的就不断压栈,如果出现了当前数比栈顶的数大的就说明了——栈里面一定可以有至少一个元素能找到较大数字的下标,把满足条件的全部移出栈并更新答案数组即可。我们考虑优化代码,发现里面有很多冗余的地方,我们发现很多特判完了都只是做了压栈的操作,那么把这一步单独拉出来,把出栈判断就行了。的基本思想是,维护一个栈,栈内的元素从栈底到栈顶保持单调递增或者递减的顺序。回到本题:这题就是经典的单调栈题目。原创 2023-12-26 14:41:55 · 396 阅读 · 0 评论 -
力扣经典面试题——搜索旋转排序数组及最小值(二分搜索旋转数组系列一次搞定)
先来看这个搜索旋转排序数组:https://leetcode.cn/problems/search-in-rotated-sorted-array/description/?首先我们先尝试着二分看看,如果我们二分,就会出现这种情况,就是二分得到的点,左边右边并不是严格递增,如果一个数小于mid那么可能会导致他不在mid前面而是在mid后面。,我们只需要判断目标在不在那个有序的半边,不在就二分另外半边,怎么判断哪个半边有序,直接首尾看看递不递增就行了。我们要找11,每次我们分割半边判断然后看到底在哪一边。原创 2023-12-24 20:15:42 · 696 阅读 · 0 评论 -
力扣经典面试题——搜索二维矩阵(两次二分搜索)
具体二分查找的写法可以看我之前的文章:https://blog.csdn.net/qq_45816864/article/details/134752708?思路:先按行二分,再按列进行二分。即先找到对应的行,再找对应的列。原创 2023-12-24 00:20:55 · 672 阅读 · 0 评论 -
Java经典面试题——手写快速排序和归并排序
注意找哨兵那里内循环的等于号不能漏,不然能出不来循环了,因为如果数值都一样,那么l和r一直保持不变了。,因为归并是先拆后再合并,而快速排序我们需要知道哨兵的位置,所以需要先进行局部排序找到哨兵。题目链接:https://www.luogu.com.cn/problem/P1177。通过对比归并排序和快速排序,我们可以发现,归并排序和快速排序的区别在于。技巧:交换数组中的两个位置。原创 2023-12-23 23:07:19 · 568 阅读 · 0 评论 -
力扣思维题——寻找重复数
这题的思维难度较大。一种是利用双指针法进行计算环的起点,这种方法在面试里很难说清楚,也很难想到。大致做法就是,定义快慢指针,由于数字都是1-n,一共n+1个所以一定存在环。快慢指针一定会相遇,但是相遇的点并不是重复数字的点,所以再将fast放到起点,每次移动一格,再次和慢指针相遇的时候就是环的起点,两个指针每次都是一样快了。可以用一个具体的例子来理解:如果遍历一遍输入数组,统计小于 等于 4 的元素的个数,如果小于等于 4 的元素的个数 严格 大于 4 ,说明重复的元素一定出现在整数区间 [1…原创 2023-12-22 18:56:56 · 717 阅读 · 0 评论 -
双指针——找到字符串中的所有字母异位词
双指针,每次都统计出来p长度的滑动窗口里的数字,拿Arrays.equals进行对比,然后滑动一小格,减1加1继续比对即可。原创 2023-12-19 18:31:58 · 371 阅读 · 0 评论 -
双指针的运用——双数之和II和三数之和
我们考虑这个排序过的数组,首先一个指针在最左,一个在最右。如果这两个数字比目标数字来的要小,那么如果我们左边指针移动了,移动后一定变大了,所以这么移动OK,如果移动的右边左移那么由于排序的原因只会越来越小,所以根据这个规律双指针移动就一定能找到。这题较难,我们需要先定下三个点一个,然后在以这个点为起点,后面数组在定义两个节点,进行两数之和的过程,中间需要考虑重复的情况,加特判循环略过重复的点。原创 2023-12-13 15:42:11 · 85 阅读 · 0 评论 -
链表TOP难度——排序链表
定义一个头节点节点方便链表连接,while(每两个链表进行合并),进行遍历取到左边一个链表,链表末尾指空,进行遍历取到右边一个链表,链表末尾指空,两个链表合并,头节点连接到这个链表,头节点指向这个链表的尾部。原创 2023-12-12 10:06:23 · 96 阅读 · 0 评论 -
力扣经典面试题——合并区间
通过重写Comparator的compare方法来自定义排序规则,返回的值看正负。原创 2023-12-11 23:55:09 · 117 阅读 · 0 评论 -
力扣思维题/经典面试题——下一个排序
字节面试题,非常经典的逻辑思维题。这一位已经比原来的大了,后面不管怎么样,这个排列都会比原来的大,要是这个排列尽可能地小,为什么,仔细想想,后面都是倒序了怎么都不可能变得更加大了。原创 2023-12-10 16:04:27 · 462 阅读 · 0 评论 -
两种做法——判断是否是二叉搜索树
考虑只有两个节点和一个结点的情况,可以头尾各加一个最大最小值,不用特判了,也可以直接特判1和0。由于测试样例里有最大值,所以INT最大值不够用。非常精妙的区间法,利用区间进行递归。原创 2023-12-07 16:00:39 · 173 阅读 · 0 评论 -
力扣面试经典150题——Unix简化路径
思路:将串以/分割,判断字符串是…/./其他,进行入栈和出栈,最后留下的就是结果,拼装一下就好了。PS:LinkedList的API。原创 2023-12-05 23:31:20 · 306 阅读 · 0 评论 -
BFS求树的宽度——结合数组建树思想算距离
比如[1,2,3]可以构成一颗二叉树,1是根节点,2和3是孩子,二者的下标分别是×2和×2+1,这样子不同的节点就有了自己的编号。1、考虑树的宽度一定是在一层上的所以进行BFS,树的BFS不建议直接使用队列,每次add/offer然后poll/remove,这样子层级关系不好显示。我们可以定义两个数组,每次从List里面取出所有的元素,这些元素就是一层上的所有的节点。扩展:字节面试题,Z字形遍历树,利用上面的思想非常简单,只需要一个变量每多一层判断奇数还是偶数,对List正着或者倒着遍历。原创 2023-12-04 22:57:38 · 254 阅读 · 0 评论 -
中序和前/后序遍历构造二叉树———通用做法
*思路:我们每一次一定可以根据递归确定根节点是哪个,就是前序第一个数,然后找中序遍历这个点,看左子树有几个节点,右子树有几个节点,然后就可以根据节点个数,递归左子树和右子树,当且仅当left>right时结束,由于preorder和inorder对应的所以left>right只需要判断一个符不符合就行了。**8个位置的判断一定要仔细。原创 2023-12-03 19:43:50 · 108 阅读 · 0 评论 -
二分查找边界问题——排序数组找元素第一次出现和最后一次出现
由于哪怕已经找到了对应的值一样,但是我们要的是边界值,所以nums[mid]=target的时候不能退出循环,依旧要继续下去。同时如果mid值和要找的值相等,不能+1或-1,比如left=mid+1这种,因为如果是边界这个值就丢了。考虑left=5,right=6这种情况,如果5,6的值都是满足的条件的怎么办?如果mid=(left+right+1)/2,那么最后的mid就是6,如果mid=(left+right)/2,那么最后的mid就是5,原创 2023-12-02 15:16:26 · 446 阅读 · 0 评论 -
二叉树展开为链表的三种写法
新建一个树形链表,前序遍历这个树,遍历到一个节点就往里插;每次找左子树最右的节点,然后连接即可。原创 2023-12-01 21:33:20 · 94 阅读 · 0 评论 -
LRU缓存淘汰策略的实现——LinkedHashMap哈希链表
需要重写removeEldestEntry方法,在缓存容量超过设置的上限时,移除最老的缓存项。哈希表用于快速获取缓存项,而双向链表用于保持缓存项的顺序,最近使用的项在链表的末尾,最少使用的项在链表的开头。:使用put方法向缓存中存入键值对,使用get方法获取指定键对应的值。通过LinkedHashMap的特性,最近访问的缓存项会被移动到链表尾部,从而实现LRU淘汰。LinkedHashMap 是 Java 中提供的一种数据结构,它综合了哈希表和双向链表的特点,非常适合用来实现 LRU 缓存。原创 2023-11-30 19:23:18 · 392 阅读 · 0 评论 -
删除排序链表的重复元素I和II,多种解法和思考
这样子前面一个节点.next就可以跳过这个节点了,不再需要删除节点再定义两个节点了。单循环双节点计数法,先得到前面一个节点和当前节点,然后找到计数为1并且前后两个节点值不一样的位置,双节点再移动,又臭又长。一个循环就可以了,如果当前节点和下一个节点值一样,当前节点不移动让next后移动一个,如果不一样则当前节点后移。一个循环就可以了,如果当前节点和下一个节点值一样,当前节点不移动让next后移动一个,如果不一样则当前节点后移。原创 2023-11-29 21:54:51 · 343 阅读 · 0 评论 -
头插法巧解任意链表区间反转
设置一个虚拟节点,不断循环到要反转的前面一个位置,哪怕是1也能包含进去。接着反转,需要记录一开始的节点,这是反转后最后的一个节点,要指向反转完后的一个节点。中间直接头插法即可。原创 2023-11-28 14:44:11 · 97 阅读 · 0 评论 -
反转链表的三种写法
循序很重要,头插法首先先记录下来头节点下一个的点存起来,然后头节点指向当前节点,然后头结点的下一个下一个指向存起来的点,head继续指向下一个,head的next不能动。,首先我们直接递归找到最后的头节点,这个头结点不能再动了,直接有一个.next.next就是找下一个节点,把下一个节点的下一个指向当前节点,然后当前节点下一个指空,这样子回到上一个递归的时候就可以保持头节点不变,只变化了中间的节点关系,就算head指向了空也没事,递归回去栈中还保存着head的信息不会丢失。原创 2023-11-28 13:19:42 · 156 阅读 · 0 评论 -
力扣H指数——简约做法
6行代码搞定一切原创 2023-11-24 00:30:57 · 197 阅读 · 0 评论 -
力扣贪心——跳跃游戏I和II
利用边界进行判断,核心就是判定边界,边界内所有步数一定是最小的,然后在这个边界里找能到达的最远地方。设置边界,每次到边界就更新,重点在step遇到边界就更新,然后再到最远距离,每次步数一定+1;原创 2023-11-20 23:31:47 · 352 阅读 · 0 评论 -
一文助你了解ElasticSearch的底层经典数据结构——倒排索引
倒排索引还有很多进一步的优化技术,比如索引压缩、布尔匹配排序、聚合查询优化、缓存优化、索引重建优化等,这些技术可以根据实际应用场景的特点进行选择和组合,从而有效提高倒排索引的查询效率和应用效果。缓存优化:倒排索引查询是一个相对较耗时间的操作,可以通过使用缓存技术,将查询结果缓存到内存或者其他介质中,当下次再次查询相同的关键词时,直接从缓存中读取结果,避免重复查询,提高查询速度。倒排索引的核心是由所有文档中的单词建立的索引表,这个表通过单词查找指向文档列表(或词项出现位置列表)的指针。原创 2023-11-01 13:38:56 · 1710 阅读 · 0 评论 -
无重复字符的最长子串(字节二面题,java做法)
维护两个指针,一个负责记录左边能够保证不重复的最左边界,另外一个负责记录上一次该字符出现的位置,两个位置取最大maxk即可。每次遍历到的长度就是当前位置减去maxk。不断比较每个位置的长度取最大即可。第一次尝试使用java写leedcode题,还是很有意思的。原创 2023-10-06 22:46:17 · 84 阅读 · 0 评论 -
最小基因变化(BFS统计步数)
看完题目发现思路很清晰,就是BFS,每次向外搜索变化一个字母的串就行了。每个bank串最多只能被遍历一次,打上标记别让被遍历两次了,步数的统计有两种方式,一种就是把所有步数相同的都放到一个循环里,还有一个就是用map统计,step[string] = step[上一个串]+1即可。原创 2023-09-23 18:21:21 · 68 阅读 · 0 评论 -
状态压缩DP(状压DP)POJ-3245
我们遍历每一行判断有没有牛放在了0,这里可以预先对输入进来的01进行异或处理,这样子之后只需要进行&操作就能判断了。然后枚举每一行的方案和上一行每一种方案进行判断是否冲突,不冲突就加上上一行该方案的方法数。我们可以将DP数组中的下标代表一种方法的压缩,一般都可以用二进制进行压缩。从第二行开始遍历,在满足条件的情况下,只需要加上上一行满足条件的情况。那么我们可不可以dp[i][j]其中j每一个下标表示的就是一个状态。这个满足条件的数组长度就是j的长度,也是我们之后要进行枚举的情况。于是状态DP就这么诞生了。原创 2023-09-22 15:11:35 · 78 阅读 · 0 评论 -
位运算统计数字出现次数
乍一看好像是一道哈希,但是卡了常数空间,和线性时间复杂度,hash会到nlogn所以不符合要求,那么这道题为什么可以不用哈希就能做。注意道题目条件除了一个数只出现一次,其他数都会出现两次。这时候可以联想到位运算,一个数字连续两次异或得到0,那么整个nums数组进行异或最后就能得到只出现一次的值。原创 2023-09-10 10:21:04 · 71 阅读 · 0 评论 -
最长回文子串(扩散算法、动态规划)
扩散简单来说就是围绕这一个点去向外扩展看相应字符是否对等以判断回文串。不断去枚举区间的长度,dp[i][j]代表i位置到j位置是否构成回文子串,如果s[i]==s[j]并且dp[i+1][j-1]恰好为回文串,那么dp[i][j]=dp[i+1][j-1],表示其也是一个回文串。外部只需要放两个循环,一个循环用来循环扩散的长度,另外一个用来循环扩散的位置,扩散回文分为奇数长度的串扩散和偶数长度的串扩散,所以要分开扩散点为(i,i)和(i,i+1),其中需要多加讨论边界情况,防止溢出。原创 2023-09-08 17:32:01 · 52 阅读 · 0 评论 -
动态规划——单词拆分
一道经典动态规划题目。这题的状态转移方程还是很好想的,大致思路就是,对字符串做循环,每次都循环前面已经判断为true即已经可以由word拼接而成的字符串,看能不能组成新的字符串。原创 2023-09-05 10:48:39 · 100 阅读 · 0 评论 -
巧用位运算统计二进制串中1的个数
统计二进制中1的个数,二进制快速解法原创 2023-09-05 10:12:50 · 101 阅读 · 0 评论 -
字典树(前缀树Trie)的原理以及具体实现
介绍了字典树基本原理和实现过程。原创 2023-09-02 22:04:41 · 108 阅读 · 0 评论 -
力扣面试150题——两数之和非暴力哈希表做法
力扣面试经典150题,利用哈希表统计满足总和为固定值条件的两数。原创 2023-08-31 16:28:01 · 51 阅读 · 0 评论