记录一下目前为止的算法成长
每日笔记
复习曲线
间隔1天、3天、7天、15天、30天,然后以一个月为周期复习
2023. 12. 24
一定要每天早中晚都要复习一下
早中午每段一两道, 而且一定要是同一个类型, 不然刷起来都没有意义
11.29
开始向着面试刷题跟进!
每天刷4题左右 ,一周之内一定要是统一类型
而且一定稍作总结, 了解他们的内在思路究竟是怎样的!!
12.26
斐波那契数
爬楼梯
最小花费爬楼梯
不同路径1/2
12.28:
整数拆分
重点思路:一个正整数可以分为两个,或者多个,多个可以用dp[i-j]代替,一定不能直接分为乘以dp的情况,因为这就默认了必须拆分为三个以上
不同的二叉搜索树
重点思路: 把左右子树所有情况乘起来,递归子树的问题。注意左右节点个数的边界
12.29
01背包理论
12.30
分割等和子集
很难看出来是01背包。满足的条件有
- 每个元素只有取和不取两个状态
- 结果要满足,某一部分和,刚好等于什么什么value,而背包问题是在限制的重量内计算他们价值最大值, 这里只需把求最大值改成求刚好 == sum即可
- 此题的weight和value都是nums【i】,因为是一个一个数字要求刚好和为sum
- dp【i】代表在i内之和最大为多少 本题要求刚好等于sum所以结束条件是dp[ sum ] == sum ,即总量为sum之内刚好最大为sum!
最短无序连续子数组
12.31
209. 长度最小的子数组
思路:滑动窗口,先不断右移直到sum>=target , 然后左指针左移直到小于target,记录暂时的最小长度,然后继续右移右移直到sum>=target , 左指针左移直到小于target,不断迭代最小长度
和为 K 的子数组
使用前缀和,核心是
map.containskey(sum-k);
map.put(sum,map.getOrDefault(sum,0)+1);
152. 乘积最大子数组
重点思路:使用dp代表前i个最大乘积。但是我们注意到有可能含有负数,因此只记录max值是不行的,万一后面出现负数,乘上去大于max,就会使结果不对。
因此 我们需要两个dp数组,记录max和min,正和反向的最大值。每次迭代dp【i】时,我们需要比较【当前数字、上一个max乘以当前数字、上一个min乘以当前数字】,最大最小都记录下来。同时不断用max迭代结果res。
优化方式仍然可以采用迭代法。
4.打家劫社:这个屋子偷不偷,偷就加dp[i-2],不偷就等于dp【i-1】
有一个困扰很久的问题:
我以为这个递推公式的意思是相邻的两个元素必须有一个得取。
其实并不是,只是限制了要不要取 i ,没说不取 i 就要取 i-1 ,我们等于的是dp【i-1】!注意定义!所以有可能出现连着两个都不取。但是不可能出现连着三个都不取。
2024 . 1 . 1
无重复字符最长子串
记住map里存放的是当前元素及其下标。如果有就更新到i和最新的
1.15
结束了13天的期末 回归面试征途
算法:
最长子串老题
LRU缓存
知识点:
聚集索引和二级索引(非聚簇索引)
回表查询
什么是联合索引?
联合索引的创建
为什么需要注意联合索引中的顺序?
联合索引的优势
什么是索引失效
覆盖索引
索引创建的原则、什么时候创建
2.27
放完寒假归来第二天,正式进入状态,重新回归算法题和八股文密集时代
3.5号之前看完八股文,每天15集,算法题每天至少4道,开始!
无重复子串
2.28
LRU缓存
3.1
LRU缓存 18分钟ac
三数之和 的双指针解法,固定一个for循环再剩下两个双指针
反转链表复习了一遍迭代:为什么是now != null?因为迭代完now是最后一个的下一个,为什么是返回pre?因为pre是现在的最后一个,也就是反转过来以后链表的头部
还得复习递归法
3.3
数组中第k个最大元素
学会堆的相关知识,学会快速排序 和这道题解法
快速排序方法
通过设定第一个为基准值middle,还有左右两个边界,交替与middle比较,左比middle大就和右交换并且右开始扫描,左比middle小就继续扫描下一个(右比middle大就继续扫,比middle小就赋值给左并且左开始扫描)
超级重点!!!!必须先移动右指针再移动左指针 因为左指针一开始指的就是middle不能先移动
代码如下
void quickSort(int[] nums,int low,int high) {if(low > high){return;}int left = low;int right = high;int middle = nums[low];while(low < high){while(low < high && nums[high--] > middle); nums[low] = nums[high];while(low < high && nums[low++] < middle);nums[high] = nums[low];}nums[low] = middle;//相遇点就是中值点quickSort(nums,left,low-1);quickSort(nums,low+1,right);}
堆排序方法
本题可以直接使用堆
把所有元素加入进去,当容量超过k时(要寻找的top k的数),就把heap顶的数poll出来,这样最终遍历完一定是top k的那些数字
class Solution {public int findKthLargest(int[] nums, int k) {PriorityQueue<Integer> heap = new PriorityQueue<>();for(int num:nums){heap.add(num);if(heap.size() > k){heap.poll();}}return heap.peek();}
}
堆的知识点
大顶堆:父节点大于左右儿子
小顶堆:父节点小于左右儿子
重点: 必须依次排满(从上到下从左到右按顺序排)
如何调整堆
LRU缓存
getNode里必须delete!!!因为要查看一本书,我们要抽出来放到顶层,也就是getNode = delete+putFront,抽出书getNode来处理
delete和putFront都是很单纯的功能!
注意put的写法,要putFront然后保存到map!
3.4
三数之和
本题需要积累的:
if(k>0 && nums[k]==nums[k-1] ) 必须是和k-1相比,不能是k+1,否则会漏掉多个k的结果
continue;
if(tmp < 0)
while(i < j && nums[i] == nums[++i]); 必须写成++i,这里既可以调整了i、j的位置,还可以去除重复的数, 一举两得
res.add(new ArrayList(Arrays.asList(nums[i],nums[j],nums[k]))); 记住这里的Arrays.asList()
反转链表
记住newHead就是底层的head,这个的逻辑就是先通过
注意一定是传入head.next而不是head,你传自己有什么意义?就成原地打转了 .next才是下一个
这一句进入最底层(这句的作用就是,递归的入口) 一层一层往回反转 每一层的head都自己做反转,返回newHead进入上一层
class Solution {public ListNode reverseList(ListNode head) {if(head == null || head.next == null){return head;}ListNode newHead = reverseList(head.next);head.next.next = head;head.next = null;return newHead;}
}
3.5
最小int的数是 Integer.MIN_VALUE
最大子数组和
明确:当前sum如果小于0了,则下一个数直接重置sum,重新开始累加,因为sum已经开始拖累了!
3.6
102. 二叉树的层序遍历
思路:把每一层node都放进队列里,遍历到了左右子节点就放进队列,依次遍历
以下为层序遍历基础代码:
void bfs(TreeNode root) {Queue<TreeNode> queue = new ArrayDeque<>();queue.add(root);while (!queue.isEmpty()) {TreeNode node = queue.poll(); // Java 的 pop 写作 poll()if (node.left != null) {queue.add(node.left);}if (node.right != null) {queue.add(node.right);}}
}
在本题中必须一层的在一个数组中,所以得用一个for循环控制每一层加入同一个Array中
92. 反转链表 II
思路
我们把链表分为左、中(反转区域)、右
找到要反转的区域的前一个节点和后一个节点,反转完区域内的节点以后再连接左右部分
用到一个dummy节点,这样防止特殊情况(left为1)
注意一开始循环体内代码,把left处连接到了begin,但是最后我们出来是修改了的 begin.next.next = now; 这句话就是在让left处的指针连接右部分,而begin.next = pre; 是在把左部分和right处连接上。这样左+翻转的中+右,得到了答案
3.7
最长回文子串
思路:
使用中心扩散法,for循环遍历数组中每一个元素,然后分别讨论一个为中心和两个为中心,传入fun函数去进行处理。
fun函数就是验证回文:以传入的参数作为中心,向两边扩散,记录长度和字符串,更新结果
注意点substring的用法:
substring不大写,且是用字符串实例s.substring来引用的
切割范围是左闭右开 [left,right) ,由于此处fun中我们设置的边界
因此退出循环时是在左边界左一位,所以left+1;但右边由于切割范围右开,所以right直接不变
3.10
合并两个有序链表
递归我们什么时候使用??也就是有重叠的子问题的时候
想清楚:
每一层子问题是什么
每一层函数返回的是什么含义
当前一层要干什么
递归的出口
本题就很典型,首先每一层返回的含义是剩下的已经接好的链表
本层要干的事情就是接当前的链表
我把后面搞定了的接上,然后这一层的我自己返回给上层,因为这层该我接上
23. 合并 K 个升序链表
最简单的困难题之一
思路:这种一直在动态的排序问题,就使用小顶堆,把所有链表头的节点加入进去,这样他会自动排序,我们每一次都可以拿出当前其中最小的那个,接在res队列后面
循环条件是heap不为空,如果空了就说明添加完了
每次拿出node以后接在后面,然后把node的下一个节点(非null)放进heap中。
注意点:
1.用堆要写全称PriorityQueue,小顶堆要附带((a,b)-> a-b)
2.heap的添加方法是offer()!!拿出方法是poll()!!
PriorityQueue<ListNode> heap = new PriorityQueue<>((a,b)->a.val - b.val);
25. K 个一组翻转链表
注意点:第三行!!!!!
还有循环条件k-1,因为一次是反转两个,两个两个的对换位置!
3.11
143. 重排链表
思路:
1.找到链表中点 分为左右部分
2.反转右半边部分链表
3.两个链表交替插入
23. 合并 K 个升序链表
最简单的困难题之一
思路:这种一直在动态的排序问题,就使用小顶堆,把所有链表头的节点加入进去,这样他会自动排序,我们每一次都可以拿出当前其中最小的那个,接在res队列后面
循环条件是heap不为空,如果空了就说明添加完了
每次拿出node以后接在后面,然后把node的下一个节点(非null)放进heap中。
注意点:
1.用堆要写全称PriorityQueue,小顶堆要附带((a,b)-> a-b)
2.heap的添加方法是offer()!!拿出方法是poll()!!
PriorityQueue<ListNode> heap = new PriorityQueue<>((a,b)->a.val - b.val);
3.12
33. 搜索旋转排序数组
重点是二分查找的各个边界
注意这个如果条件只是if(nums[mid] > nums[start]),那么当nums[mid]和nums[start]相等时,代码就不会进入这个分支,即使start和mid都指向升序部分的开始。这可能导致错过搜索目标值target的机会,特别是当target正好等于nums[start]或nums[mid]时。
这一堆条件要分清,跟mid不取等(等了不就是答案了),跟边界要取等!
主要是重点是先分清是在有序区域还是无序区域。我们只讨论有序区域的东西!
142. 环形链表 II
找循环位置,先找到相遇点,然后让p从head开始走slow从相遇点走,走到他们相遇就是答案点
注意此处必须这样写循环体。因为用其他写法会超时
也就是必须把(fast == slow)这个判断条件放在里面
148. 排序链表
核心思路:归并排序
先找中点拆分为左右两半,然后使用递归不断拆分成左右两半,每次返回上一层时都把下一层的排序好再返回上去
分析一下这里的递归:
子问题是:将左右两边的链表排序,返回排序以后的链表
递归函数的作用是进左右两层,本层的作用是合并两个链表并返回
注意
寻找中点时fast要从head下一个开始,因为我们要让slow指向right部分的前一个节点,获得right并且断开left和right!
class Solution {public ListNode sortList(ListNode head) {if (head == null || head.next == null)return head;ListNode fast,slow;slow = head;fast = head.next;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;}ListNode newhead = slow.next;slow.next = null;ListNode left = sortList(head);ListNode right = sortList(newhead);ListNode dummy = new ListNode(0);ListNode p = dummy;while (true) {if(left == null){p.next = right;break;}if(right == null){p.next = left;break;}if(left.val < right.val){p.next = left;left = left.next;}else{p.next = right;right = right.next;}p = p.next;}return dummy.next;}
}
2. 两数相加
独立做出来的mid!!!!!
直接朴朴素素的相加,使用tmp变量记录每两个节点的和,本层now就直接取余,下一层的用now取整
p1或者p2其中一个为null就不加,要不就加上,规避了长短不一的结果
退出 条件不仅要同时为null,还要保证tmp已经结算完毕了,防止最高位进一的情况
滑动窗口
按照这题为模板
这种题的特征是 "子串" "子数组" 这种需要连续元素的
有一个窗口在扫描,使用两个变量left限制左范围:right用于for遍历计数
tmp,max用于记录最终数据
从0处开始滑动区间 ,right每加一次,就判断right这个元素和窗口内的元素是否满足某种条件
如果不满足了就进入处理块, 不断把left向右推进直到他满足条件, 在外层循环记录tmp和最大的max即可.(此题判断的条件是是否有重复元素,有就把left推进到重复的位置)
无重复字符最长子串
字节最经典的一道题
/
注意要用HashMap提高查找效率
- containsKey先于put处理防止自己contains自己。调整i位置,通过比较两个元素下标谁大,来确定谁是后面的0
- 左边界调整位置的时候调整到第一个有效位(即重复位+1),而不是重复位,因为如果这个字符串没有重复的,此时应该使用j-i+1计算结果, 然而如果是调整到重复位 结果就变成了j-i,没有统一。所以必须挪到重复位的右边(第一个有效位)!
- i = Math.max(i,map.get(s.charAt(j))+1); 这一句是比较 当前边界和重复字符谁更右 ,取更右边的作为边界重点:要用HashMap来搞。注意put是会覆盖相同的元素的!!!!由于遍历的顺序是下标由小到大,因此得到的那个重复元素的下标一定是目前最大的,直接和左边界比较即可
哈希表(12.9-12.16)
什么时候使用哈希法:
1.当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。
经典题: 比较两个集合的元素重叠的地方, 或者这个数组能不能由那个数组里的元素构成
2.当我们需要在一次遍历中记录某种元素出现的次数, 或者记录某个和他相关的特征的时候
抽象来说就是, 需要建立一个数据和另一个数据之间的映射关系的时候
下面是这两种数据结构的一些常用方法:
HashMap
- put(key, value): 如果key已经存在,那么值就更新
- get(key): 根据key获取value
- remove(key): 删除HashMap中指定key的元素
- containsKey(key): 检查HashMap中是否包含给定的key
- containsValue(value): 检查HashMap中是否包含给定的value
- keySet(): 返回所有key的Set
- values(): 返回所有value的Collection
- isEmpty(): 检查是否为空
- clear(): 清除所有元素
- map.put(i , getOrDefault(map.get(i),0)+1)
HashSet
- add(element): 添加一个元素
- remove(element): 删除一个元素
- contains(element):是否包含给定的元素
- isEmpty(): 检查是否为空
- clear(): 清除所有元素
- size(): 返回元素的数量
- iterator(): 返回一个迭代器,用于遍历HashSet中的所有元素。
回溯(12.19-12.24)
为什么要用回溯?
- for循环只能有单层遍历,但是回溯是可以多层遍历
- 回溯的本质就是多层遍历, 用for循环控制这一层的广度, 用递归控制深度, 用退出条件控制结束时机
- 一定要画图辅助理解,可以明确写递归方法的思路, 这很重要
- 什么题用回溯?问你返回所有可能得什么什么组合,集合, 需要枚举/遍历所有情况 , 特别是组合/子集问题,要从题目抽象中出来
基本步骤
- 定义结果res集合(ArrayList) 临时存储tmp集合(LinkedList) 当前总和int sum
List<List<Integer>> res = new ArrayList<>();
List<Integer> tmp = new LinkedList<>();
int sum=0;
- 定义dfs函数, 包含传入的数组nums, 每次遍历的开头begin, 目标target
- 定义退出条件: 等于target时 把tmp加入res然后返回 超出target直接返回 大小超出也返回
- 定义循环体:注意for(i=begin;i<length;i++)
tmp.add(candidates[i]);
sum += candidates[i];
dfs(candidates, i ,target);
sum -= candidates[i];
tmp.removeLast();
三大要点总结
- 数组中(有无)重复元素
- 结果中(能否)含有重复元素
- 结果(能否)出现重复集合(顺序不同是否算同一个集合)
主要是修改i = begin参数 和 dfs(nums, i ,target)中是 i 还是 i+1
子集能重复, 只用修改为 i=0 不可重复则是 i=begin
重点情况:
- 数组中无重复元素 结果中不能含有重复元素 子集不能出现重复: i=begin dfs(nums, i+1 ,target)
- 数组中有重复元素 结果中能含有重复元素 子集不能出现重复: 加条件:if(i > begin && nums[i] == nums[i-1]) continue;i=begin dfs(nums, i+1 ,target)
- 数组中有重复元素 结果中不含有重复元素 子集不能出现重复:加条件:if(i > 0 && nums[i] == nums[i-1]) continue;i=begin dfs(nums, i+1 ,target)###
回文子串问题
12.28
class Solution {List<List<String>> res = new ArrayList<>();List<String> tmp = new LinkedList<>();public List<List<String>> partition(String s) {int index = 0;dfs(0,s);return res;}public void dfs(int index , String s){if(index == s.length()){res.add(new ArrayList(tmp));}for(int i = index;i < s.length();i++){if(fun(index,i,s) == true) {String now = s.substring(index,i+1);tmp.add(now);dfs(i+1,s);tmp.removeLast();}}}public boolean fun(int i,int j,String s){while(true){if(i >= j)return true;if(s.charAt(i) != s.charAt(j)){return false;}i++;j--;}}}
. - 力扣(LeetCode)
可以算困难一级的了
难点:
- 把分割方案化为回溯问题, 我们想枚举每一种字符串的分割方式, 再一个一个去验证
- 可以看做是字符间空隙的组合问题(在一个空隙集合中选择不同的空隙组合) 采用回溯枚举
- 因为for循环只能有单层遍历,但是回溯是可以多层遍历(回溯的本质就是遍历, 用for循环控制这一层的广度, 用递归控制深度, 用退出条件控制结束时机 )
- 一定要画图辅助理解, 这很重要,一开始都没意识到
- 判断回文直接双指针
//我们使用index来代表当前遍历的空隙, 当枚举到**最后一个字符后**的空隙时就才遍历
if(index == s.length())
{res.add(new ArrayList(tmp));
}for(int i = index;i < s.length();i++)
{//index和i表示我们当前处理的哪两个空隙之间的字符串,只有当前满足是回文我们才继续去dfs,否则直接进入下一个循环,这样保证了只有回文, 并且因为只有遍历到最后一个字符后才会结束,所以不用担心会脏结果if(fun(index,i,s) == true) {String now = s.substring(index,i+1); //注意边界是[index,i]tmp.add(now);dfs(i+1,s);//从下一个字符开始继续判断tmp.removeLast();}
}
动态规划(12.25-1.4)
如果某一问题有很多重叠子问题,使用动态规划是最有效的。
就是你发现这一步的答案要根据上一步的答案得,而且上一步的答案也是同样的方法得到的
所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的
状态转移公式(递推公式)是很重要,但动规不仅仅只有递推公式。
动规五步曲(一定要明确的每一步结果写出来)
- 确定dp数组(dp table)以及下标的含义这很重要,注释出来
- 确定递推公式写完dp数组含义以后 立马着手递推公式 用注释先写上
- dp数组如何初始化一定要注意dp[0] dp[1] 这种边界值的初始化,很有可能要取特值
- 确定遍历顺序要确保后面的可以由前面的推出来,特别是多维dp
- 举例推导dp数组
为什么要先确定递推公式,然后在考虑初始化呢?因为一些情况是递推公式决定了dp数组要如何初始化!
Debug三问
- 这道题目我举例推导状态转移公式了么?
- 我打印dp数组的日志了么?
- 打印出来了dp数组和我想的一样么?
背包问题
01背包
(1)
对于背包问题,有一种写法即dpi 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
确定递推公式
不放物品i:和上一个相同。由dp[i - 1] [j]推出,即背包容量为j,里面不放物品i的最大价值,此时dpi就是dp[i - 1] [j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
放物品i:等于上一个加这个的value。由dp[i - 1] [j - weight[i]]推出,dp[i - 1] [j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1] [j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
取i那就是后者,不取i就是前者
dp[i][j] = max{ dp[i-1][j] , dp[i-1][j-weight[i]] + value[i] }
(2)滚动数组法
重点一定要记住, 就是数据的覆盖
在此时,数组是一遍一遍覆盖的。覆盖前就相当于原来的dp[i-1 ] [j ],所以此时,不取物品i 的情况就可以化为dp[j ] 直接就是上一个的。同理,要取物品i 就直接化为dp[j-weight ]+value[j ]
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]
初始化dp就应该全是0,在题目给的全是正整数的情况下可以保证后续被覆盖掉。
循环还是双重循环,要有i 控制前n个物品进不进去, 但是dp会减少一维度
同时我们可以窥见遍历的次序问题。因为我们要保证j 之前的数据还没有被覆盖
因为有比较dp[j - weight[i]] + value[i]的部分
所以我们要倒序遍历
ps:能不能交换遍历顺序?不能,不然就变成 dp[j]表示:取前 j 个的背包,所背的物品价值可以最大为dp[j]
链表
反转链表
迭代法:为什么是now != null?因为迭代完now是最后一个的下一个,为什么是返回pre?因为pre是现在的最后一个,也就是反转过来以后链表的头部
递归法:记住newHead就是底层的head,这个的逻辑就是先通过
注意一定是传入head.next而不是head,你传自己有什么意义?就成原地打转了 .next才是下一个
这一句进入最底层(这句的作用就是,递归的入口) 一层一层往回反转 每一层的head都自己做反转,返回newHead进入上一层
class Solution {public ListNode reverseList(ListNode head) {if(head == null || head.next == null){return head;}ListNode newHead = reverseList(head.next);head.next.next = head;head.next = null;return newHead;}
}
反转链表2
最优解,先找出pre,然后直接穿针引线再拉直,代码量极少,模仿反转k链表
注意
第三行是pre.next
注意
每次是将两个节点一起反转所以是b-a
环形链表
重点:
快指针要在head。next不然你slow永远等于fast
必须先判断fast,因为后面的有可能报空指针异常
重排链表
思路:
1.找到链表中点 分为左右部分
2.反转右半边部分链表
3.两个链表交替插入
合并两个有序链表
递归我们什么时候使用??也就是有重叠的子问题的时候
想清楚:
每一层子问题是什么
每一层函数返回的是什么含义
当前一层要干什么
递归的出口
本题就很典型,首先每一层返回的含义是剩下的已经接好的链表
本层要干的事情就是接当前的链表
我把后面搞定了的接上,然后这一层的我自己返回给上层,因为这层该我接上
K 个一组翻转链表
先算出长度,再使用两个循环去处理!
注意点:第三行!!!!!因为我们采用的是:把开头节点右侧的节点一个接一个挪到pre.next头插法
因此是把nex接到pre.next
还有循环条件k-1,因为一次是反转两个,两个两个的对换位置!必须K-1
合并 K 个升序链表
最简单的困难题之一
思路:这种一直在动态的排序问题,就使用小顶堆,把所有链表头的节点加入进去,这样他会自动排序,我们每一次都可以拿出当前其中最小的那个,接在res队列后面
循环条件是heap不为空,如果空了就说明添加完了
每次拿出node以后接在后面,然后把node的下一个节点(非null)放进heap中。
注意点:
1.用堆要写全称PriorityQueue,大顶堆要附带((a,b)-> a-b)
2.heap的添加方法是offer()!!拿出方法是poll()!!
PriorityQueue<ListNode> heap = new PriorityQueue<>((a,b)->a.val - b.val);
328. 奇偶链表
把一个链表的奇数和偶数节点分别合在一起,再相连
直接创建两个节点作为奇偶链表的开头,容纳完再接上即可
二叉树
代码
236. 二叉树的最近公共祖先
103. 二叉树的锯齿形层序遍历
记住有三个数据结构
一个当容纳节点的队列,一个是res,一个是tmp,不要把tmp和Deque搞错了
这个题直接在层序遍历基础上
在添加tmp的时候正向添加反向添加就好了,一组一组的呀他是
你在那儿管deque的添加顺序不是纯纯给自己找麻烦吗
110. 平衡二叉树
相关文章:

记录一下目前为止的算法成长
每日笔记 复习曲线 间隔1天、3天、7天、15天、30天,然后以一个月为周期复习 2023. 12. 24 一定要每天早中晚都要复习一下 早中午每段一两道, 而且一定要是同一个类型, 不然刷起来都没有意义 11.29 开始向着面试刷题跟进! 每天刷4题左右 ,一周之内一定要是统一类…...
AI大模型学习在数控系统工艺优化与智能制造中的应用
目录 1.工艺优化: 2.预测维护: 3.质量控制: 4.自动编程: 5.人机协作: 6.系统集成: AI大模型学习在数控系统工艺优化与智能制造中的应用主要体现在以下几个方面: 1.工艺优化: …...

安卓findViewById 的优化方案:ViewBinding与ButterKnife(一)
好多小伙伴现在还用findViewById来获取控件的id, 在这里提供俩种替代方案:ViewBinding与ButterKnife; 先来说说ButterKnife ButterKnife ButterKnife是一个专注于Android系统的View注入框架,在过去的项目中总是需要很多的findViewById来查…...

map和set(三)——红黑树
1、红黑树的概念及性质 1.1概念 概念: 红黑树是一种二叉搜索树,以颜色(Red or Black)互斥来限制每条路径不会比另外的路径长出两倍,来达到近似平衡 1.2性质 红黑树的性质: 每个节点不是黑色就是红色根节点是黑色的如果一个节点是…...
Day26 HashMap
Day26 HashMap 文章目录 Day26 HashMap一、应用场景二、特点三、基本用法四、面试题 一、应用场景 1、概念: HashMap是Java集合框架中的一种实现类,用于存储键值对。 2、好处: HashMap是一个常用的集合类,适用于需要快速查找和插…...

某蓝队面试经验
背景 据小道消息说今年的国护疑似提前到了五月份,所以最近也是HW面试的一个高峰期啊,这里分享一下上次长亭的蓝队面试问题(附本人的回答,仅供参考) 面试问答 1、谈谈作为蓝队护网过程使用过厂商的设备 这里我回答的…...

【Linux】 centos7安装卸载SQL server(2017、2019)
一、安装配置 准备一个基础Linux配置: 内存为20GB 运行内存为2GB的系统(数据库小于2GB安装不了) 1、网络配置 我们需要进行网络的连接 进入 cd /ect/sysconfig/network-script/ 编辑文件ifcfg-ens33 vi ifcfg-ens33 Insert键进行编辑 把ONBOO…...
面试算法-110-课程表
题目 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …...
注册前后端php的检测
首先,在HTML表单中添加一个用于输入密码的文本框,并在其后面添加一个用于显示密码格式要求提示的元素,例如一个 <span> 标签。 <input type"password" id"passwordInput"> <span id"passwordHint…...
Redis:什么是redis?①
一、思想 Redis是一个开源的高性能基于内存key-value数据库,常用作数据库、缓存或消息代理 二、数据类型 String List...
【课程】MyBatisPlus视频教程
MyBatis-Plus是一款非常强大的MyBatis增强工具包,只做增强不做改变. 在不用编写任何SQL语句的情况下即可以极其方便的实现单一、批量、分页等操作。 本套教程基于MyBatis-Plus新2.3版本,详细讲授:集成Mybatis-Plus、 通用CRUD、EntityWrapper条件构造器、ActiveRec…...

如何使用人工智能和ChatGPT来优化营销转化率
人工智能 (AI) 和营销的交集正在彻底改变企业与客户互动的方式,最终改变营销转化率。人工智能能够分析大量数据、理解模式和自动执行任务,它不仅是一项创新技术,而且是营销领域的根本性转变。这种转变允许更加个性化、…...
Ubuntu 22.04上构建libvirt源码错误解决
当在Ubuntu 22.04上构建libvirt源码时,可能会遇到一些错误。下面是一些常见错误及其解决方法: 1. 错误:Program xmllint’未找到或不可执行 解决方法:安装libxml2-utils sudo apt-get install libxml2-utils2. 错误:…...
游戏客户端面经
1,3D的模型怎么显示到2DUI上面 2,C#的ArryList和List的区别 3,接口和抽象类的区别,一般什么时候用接口 4,UGUI怎么渲染的UI,UGUI的层级管理(怎么不打断合批),合批流程…...
AS,idea,maven,gradle
Jdk,sdk。提前都是需要下好的。 Maven与gradle的思考: 用AS开发app时,gradle本就有,自己也可以指定,AGP同样。要注意gradle,AGP,jdk版本的事情。还有依赖库。 用idea开发网络程序时,也有内置的maven&…...

ElasTool v3.0 程序:材料弹性和机械性能的高效计算和可视化工具包
分享一个材料弹性和机械性能的高效计算和可视化工具包: ElasTool v3.0。 感谢论文的原作者! 主要内容 “弹性和机械性能的高效计算和可视化对于材料的选择和新材料的设计至关重要。该工具包标志着材料弹性和机械性能计算分析和可视化方面的重大进步…...

Redis入门级详解(一)
一、Redis入门介绍 1、什么是Redis? Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。…...
java算法题每日多道六
138. 随机链表的复制 题目 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对…...

C# 特性(Attribute)
C# 特性(Attribute) 文章目录 C# 特性(Attribute)Obsolete语法示例代码 创建自定义特性(Attribute) Obsolete 这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例…...
Redis 教程系列之Redis 配置(三)
Redis 配置 Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf(Windows 名为 redis.windows.conf)。 你可以通过 CONFIG 命令查看或设置配置项。 语法 Redis CONFIG 命令格式如下: redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME 实例 redis 127.0…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...