ARTS挑战第二十周

Algorithm

143. 重排链表

  1. 快慢指针法找到链表中间节点 2) 将后半部分链表进行反转 3) 合并两个链表

781. 森林中的兔子

mp[i] 表示answer为i的兔子的个数,则最多有 i+1个兔子是相同的颜色,因此报出这个答案的兔子有 (mp[i]+i)/(i+1) * (i+1)个兔子。例如 mp[1] = 5, 则至少有 6 个兔子,至少有三个颜色

【动态规划】剑指 Offer 42. 连续子数组的最大和

​ dp[i] 表示以nums[i-1] 为结尾的最大子数组和,dp[i] = max(dp[i-1],0) + nums[i-1];

使用全局变量记录最大的dp[i]即可;basecase dp[0] = 0;

【dfs】200. 岛屿数量

对于岛屿的每一个1进行dfs,在原地图标记访问过的节点为2;执行多少次dfs就有多少个岛屿

【加法】415. 字符串相加

首先两个字符串进行翻转,然后再加

【字符串递归解析】394. 字符串解码

指针cur从头到尾遍历

|–如果遇到数字,则对应num增加;

|–遇到’[’,先递归处理后面的子串,并将num*递归结果加入buf。 遇到’]'则停止递归,返回buf

|–遇到其它字符,直接加到结果集

【字符串递归解析】224. 基本计算器

与字符串解码有异曲同工之妙,主要考虑怎么处理±号,对于±号,可以把它当作一个sign符号,将每个数压倒栈中

【hash+double list】面试题 16.25. LRU 缓存

主要使用stl_list 中的 splice api完成从链表中间移动到头部的操作,否则还需要先删除再放到头部,比较费时

【动态规划】72. 编辑距离

dp(i,j) 表示word1前i个字符和word2前j个字符的最小编辑距离。我们如果仅考虑在word1上进行操作,则有四种选择:

​ \1. 如果word1[i-1] = word2[j-1], 不需要操作,否则有下面三种选择:

​ \2. word1上删除一个元素,对应dp(i-1,j).

​ \3. word1上增加一个元素,等价于B上删除一个元素,对应dp(i,j-1)

​ \4. word1上替换一个元素,对应dp(i-1,j-1);

​ base case : dp(0,j) = j; dp(i,0) = i;

【归并排序】剑指 Offer 51. 数组中的逆序对

暴力求解会超时。逆序对与归并排序息息相关,

  • 假设归并排序的合并操作前有两个有序数组left_array和right_array.
  • l和r分别为指向两个数组的指针。如果l指向的元素小,则该元素加入结果,l++;对于r亦然。
  • 在这个过程中,如果l所在元素小,由于left_array比l所在元素大的在l右边,而right_array中比l所在元素大的也在l右边,因此不会出现(x,l)这样的逆序对;
  • 如果r所在元素小,由于left_array中比r所在元素大的元素x都在其左边,从而(x,r)都能构成逆序对
  • 对于递归的每一层都需要进行统计,最终结果加起来。

【动态规划】300. 最长递增子序列

dp[i] 表示以第i个元素为结尾的最长上升子序列,

  • dp[i] = max(dp[i], dp[j]+1)for all nums[j-1] < nums[i-1] && j < i;

  • base case : dp[i] = 1; dp[0] = 0;

【贪心+二分查找】300. 最长递增子序列

贪心+二分查找O(NlogN):

  • tail[i] 定义为 长度为i的上升子序列的最小末尾元素,注意tail数组是严格递增数组,
  • 贪心:末尾元素越小越好,这样有更大的机会后面能继续接上当前长度最大的子序列从而形成更长的子序列
  • 一开始我们只有tail[1] = nums[0],如何更新这个tail数组从而保持这个定义的准确?假设当前最大长度为len
  • 如果nums[i] > tail[len],说明可以直接续到长度为len的子序列后面,从而最长的上升子序列长度+1了,因此有 tail[++len] = nums[i];
  • 否则,nums[i] < tail[len]的话,它可能可以更新当前tail数组的某个元素,从而维持这个定义的准确性。具体需要更新哪个元素呢?
  • 如果存在tail[j] < nums[i] < tail[j+1],则更新tail[j+1]这个元素,因为这样才能继续保证tail数组的严格递增属性。
  • 这个过程其实就是在tail上二分查找nums[i]元素的左边界,如果nums[i] 已经在tail上出现过,则无需操作

【双指针】11. 盛最多水的容器

假设left指向容器左边界,right指向容器右边界,初始化left=0,right=n-1。然后两个指针向中间移动,每次都移动短的那一个指针,如果两个指针一样长,随便移动一个(因为此时移动后肯定容量会减小);记录最大值

【单调栈】739. 每日温度

从后往前入栈的单调递减栈,每个栈里面存的是下标。 对于temp[i], 如果栈顶元素 temp[stk.top()]更大,则需要等stk.top()-i天,栈为空则等0天; 否则一直把栈顶元素弹出,直到满足上面条件或者栈为空。然后temp[i] 入栈

【贪心】56. 合并区间 重要的是画图

区间start进行排序,然后不断将当前区间与其它区间合并,无法合并则将当前区间放入结果集

【模拟】54. 螺旋矩阵

按层进行模拟,一层一层地输出。假设第一层的左上角顶点为 (x1,y1), 右下角顶点为(x2,y2).则走的顺序为 (x1,y1) -> (x1,y2) -> (x2,y2)->(x2,y1)-> (x1+1,y1).

​ 下一层的顶点为 (x1+1,y1+1), (x2-1,y2-1); 如果 x1> x2 || y1> y2 ,停止遍历

​ 对于只有一行或者只有一列的情况处理一下。

【归并排序,堆排序】23. 合并K个升序链表

方法一:采用归并排序方式,进行分而治之,一次性只合并两个链表; 方法二:采用小根堆,对里面每个节点存储链表指针即可。

【动态规划】10. 正则表达式匹配

dp[i][j] 表示s 前i个和p前j个字符是否匹配

\1. 如果s[i-1] == p[j-1]|| p[j-1]=='.' 则表示s[i-1]与p[j-1]是匹配的,因此 dp[i][j] = dp[i-1][j-1]

\2. 否则 如果p[j-1] 不为‘*’,则肯定没法匹配了 dp[i][j] = false;

\3. 如果p[j-1] 为 ‘*’ ,则查看p[j-2]. p[j-2]==s[i-1]||p[j-1]=='.',则'*'可以匹配0次或者多次,因此dp[i][j] = dp[i][j-2]||dp[i-1][j]

\4. 如果p[j-2]!=s[i-1], 则只能匹配0次 , dp[i][j] = dp[i][j-2];


base case dp[i][0] =false; dp[0][j] 则需要根据p[0:j] 是否是偶数个且为 'x*x*'的形式

【动态规划】121. 买卖股票的最佳时机

股票的价格就是求差,left_min[i] 表示前i天出现的最低价,right_max[i]从第i天往后出现的最高价

【双指针】16. 最接近的三数之和

先排序,然后在使用传统threeSum问题求解,中间把结果记录下来即可。

【模拟】43. 字符串相乘

|–num1和num2结果最多m+n位,使用一个长度位m+n的数组res存储最终结果。

|–每次只计算num1[i] 和 num2[j]的乘积,乘积的结果加到res[i+j]和res[i+j+1]中

|–i和j均从后往前进行计算, *sum= num1[i]num2[j]+res[i+j+1]; res[i+j+1]= sum%10; res[i+j] += sum/10

|–去除结果中的前导0,并转化成string 返回

【快慢指针】26.删除排序数组中的重复项(简单)

使用快慢指针算法,快慢指针 slow 指针上一个不重复的元素,fast指向下一个不重复的元素,然后将fast指向的元素放到slow后面,然后两个指针不断前进

【回文数的判定】9. 回文数

直接翻转整个数会导致溢出,因此在此题中是不可行的。翻转后面一半(假设位r),x不断减小,r不断增大,当r>=x时,说明已经翻转一半了;这时候如果r>x,则r可以丢弃最后一位再比较;r==x则说明是回文数; 对于一些边界case需要特殊处理:1)x为负数;2)x>0 且末尾有0(例如10,r=1,x=0,由于r>x,r/10=0,从而出现误判的情况

【归并排序】148. 排序链表 找到中点断开,归并排序

【二分搜索】33. 搜索旋转排序数组

思路:利用部分有序性进行二分搜索,其实二分搜索的关键是判断什么时候往左区间搜索,什么时候往右区间搜索

一般的二分搜素,只有两个分支,而本题却有四个分支。

|–首先取mid=(l+r)/2,如果nums[mid] ==target 直接返回

|–然后mid要么在左边有序区,要么在右边有序区,分别讨论

|–如果nums[mid] > nums[0], 说明mid在左边有序区

|–如果此时 target在mid 左边有序区范围内,则往mid左边找

|–否则往右边找

|–否则nums[mid]<= nums[0] (该题不存在==其实),则说明mid在右边有序区

|–如果此时target 在mid 右边有序区范围内,则往mid右边找

|–否则往mid左边找

【二分搜索】81. 搜索旋转排序数组 II

|–首先取mid=(l+r)/2,如果nums[mid] ==target 直接返回

|–然后mid要么在左边有序区,要么在右边有序区,分别讨论

|–如果nums[mid] > nums[l], 说明mid在左边有序区

|–如果此时 target在mid 左边有序区范围内,则往mid左边找

|–否则往右边找

|–否则nums[mid]< nums[l],则说明midA在右边有序区

|–如果此时target 在mid 右边有序区范围内,则往mid右边找

|–否则往mid左边找

|–否则无法判断mid在哪个区,只能l++,来去重一个重复项

Review

java gc算法

内存回收的针对哪部分内存

  • 堆区(主要)

  • 方法区(1.8后 元空间),主要是运行时常量池

判断对象存货的方式

  • 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
  • 可达性分析:从GC root开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

垃圾回收算法

  • 标记-清除:先标记,再清除。缺点:1)效率不高,2)内存碎片
  • 复制算法:将存活的对象复制到另一块区域,再将原来的区域整个一次性清理。优点:无内存碎片。缺点:1)频繁复制代价高。2)内存容量减半
  • 标记-压缩:先标记,将所有存活对象移向一端,清理掉边界外的内存。优点:解决复制算法容量减半问题。缺点:移动代价高昂
  • 分代收集:将堆分为新生代和老年代,对新生代采用复制算法,对老年代采用标记清除或整理。

垃圾收集器

G1收集器:

Tips

  • c++锁类型:互斥锁、自旋锁、条件锁、共享锁
  • c++ dynamic_cast与其它cast的本质区别:dynamic_cast在运行时进行转换,而static_cast等则在编译器就完成了转换。
  • c++ priority_queue默认是大根堆,对于数值类型,可以前面添加负号变成小根堆
  • c++内存对齐的概念:每个成员变量的起始地址为cpu取地址粒度的整数倍

Share

c++内存对齐方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值