• 面试__编程


    1. 队列

     电话号码的字母组合 # 思路:先入队列,再逐个出队列,出队列的元素和新的字符相加再进入队列,直到遍历结束
     层次遍历
    
    • 1
    • 2

    2. 栈

     括号匹配
     最长匹配括号  # 思路,栈中存储括号的下标,若为(,则入栈,否则出栈,判断栈是否为空,若为空,说明没有以其匹配的左括号,放入下标(最后一个没有被匹配右括号的下标);否则,和栈顶元素计算最大长度;
     linux路径
    
    • 1
    • 2
    • 3

    单调栈
    每日温度(减栈)# 栈中元素为下标和温度值,温度减少才入栈
    去除重复字符(增栈,考虑剩余)# 先统计每个字符的个数;遍历,set或者数组判断是否已存在,若不存在,看是否更小且后面还有(通过统计的个数),是则入栈,且更新set为没有
    求最大矩形(增栈)# 从左往右遍历,递增则入栈,否则栈顶元素逐渐出栈,高度(栈顶元素的下标对应的值)*宽度(i-stack.peek-1)= 面积,求面积最大值

    3. 链表(一般两个节点,一个next指向头结点,一个遍历)

     两两交换链表的节点 # 申请一个节点指向头节点,一个临时节点做交换
     旋转链表 # 先形成环,再在指定位置断开
    
    • 1
    • 2

    4. 数组

     旋转图像(数组旋转90度);先对角线旋转,再水平旋转  
     合并区间(先升序排列,再合并)
    # 5. 二维数组
        矩阵置0
            **描述**,O(1)空间复杂度,若某一行或某一列有0,则这一行及这一列都置为0
            **思路**,用第一行和第一列作为这一行或者这一列是否有0的标志位,用两个变量记录第一行及第一列本身是否有0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5. 回溯(深度优先,递归)一般有两个方法

     全排列 # dfs,参数有result,trace,depth,数组长度n,used数组
     全排列后的第k个
        方法二:全排列+剪枝
     n皇后,8皇后(原理同上,只是判断重复时,斜着的和竖着的通过set判断)
     括号生成
     ip生成
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5. 递归(树的深度优先)# 方法一般有两个方法

     验证二插搜索树
     相同的树
     有序数组转为二叉搜索树 # (中序遍历,总是选择中间左边的数作为根节点)
     二叉树最大路径和  # 单个dfs,先计算左右的贡献值,然后计算max,返回贡献值(<0时返回0)1:最大贡献值(root.val+max(0, dfs(root.left), dfs(root.right))) 2:求最大路径(dfs(root.left)+root.val+dfs(root.right))
     从前序和中序构造二叉树 # 构造中序遍历的值与左边的map,两个方法, return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1); 子方法返回root;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5. 树(递归,队列)

     中序遍历 # 递归
     公共祖先 # 中序遍历,if(root == null || root == p || root == q) return root; 递归调用,左子树为空,返回右边,返回root
     恢复二插搜索树 # 用中序遍历(递增)找到错误位置的数据的下标,然后交换
     二叉树展开为链表 # 先中序遍历,再展开
    
    • 1
    • 2
    • 3
    • 4

    3. 排序

     逆序对个数 # 分两步,1:合并两个有序数组(归并排序) 分:public  void sort(int[] nums,int l,int r) 和 sort(int[] nums,int l,int mid,int r)借用一个数组合并,3个while
                    2:改1,合并时,左边数组的指针移动时,计算逆序对的贡献(累加左边集合剩下的数量total += (mid-p1+1))
     课程表 # 拓扑排序,用队列维持入度为0的点,然后相关的入度减1
    
    • 1
    • 2
    • 3

    3. 逆序

    旋转数组(数组右移动k位)# 思路:3次逆序
    旋转数组的最小值 #思路:二分法(二分法带等号), 区间先递增,后到最小值,再递增
    搜索旋转数组(二分)
    
    • 1
    • 2
    • 3

    4. 动态规划(可以拆分为子问题)

    模板
        ```c
        单串问题
        # 依赖前单个元素
        dp[i] = dp[i-1] + ns[i]
        # 依赖前部区域元素,最大最小问题
        dp[i] = min(dp[i], f(dp[j])
        ```
    0-1背包;动态规划 # dp方程 f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - weight[i]] + value[i]);
    最长回文 # 方程:dp[i+1][j-1] == 1 && (s.charAt(i) == s.charAt(j),遍历顺序:一个递减,一个递增
    最大子序列和,如{5,4,-1,7,8}的最长子序列和是23 # dp[i] = Math.max(dp[i-1]+a[i],a[i]);
    编辑距离,一个单词变为另一个单词(删除,添加,替换的方式)的最短距离 # if( ){ dp[i][j] = dp[i-1][j-1]; else dp[i][j] = 1+Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);
    数字字符串编码为字母 #第一种情况:最后一位使用一个字符,s【i】!=0,dp[i] = dp[i-1] ;第二种情况:最后两位拼成字母,s[i-1]!=0 && 10*s[i-1]+s[i] <26,dp[i] = dp[i-2]
    不同二插搜索树的个数 # G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0) ,两层for循环, dp[i] += dp[j-1] * dp[i - j];
    交错字符串 # 定义 dp(i,j) 表示 s1的前 i 个元素和 s2的前 j 个元素是否能交错组成 s3的前 i+j 个元素,s3.charAt(i+j-1) == s1.charAt(i-1) && dp[i-1][j]或者s3.charAt(i+j-1) == s2.charAt(j-1) && dp[i][j-1];dp[i][j] = true
    不同的子序列 , s和t,s中包含t的个数 # dp[i][j]代表s前i个字符中包含t前j个字符的个数,if(s.charAt(i-1) == t.charAt(j-1)){ dp[i][j] = dp[i-1][j-1]+dp[i-1][j];else { dp[i][j] = dp[i-1][j];
    三角形最短路径和 # dp[i][j]=min(dp[i−1][j−1],dp[i−1][j])+c[i][j]
    买卖股票(可以交易多次)
        ```java
        //i天,状态j(0,股票),手里的最大收益
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
            dp[i][1] = Math.max(dp[i-1][0]+prices[i],dp[i-1][1]);    ```
    零钱兑换,最少硬币数  # 两层遍历金钱和硬币数,dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
    零钱兑换,数组总和 # 两层遍历硬币数和金钱, dp[i] += dp[i - coin];
        对于面额为 coin 的硬币,当 coin≤i≤amount 时,如果存在一种硬币组合的金额之和等于i−coin,则在该硬币组合中增加一个面额为 coin 的硬币,即可得到一种金额之和等于 i 的硬币组合。因此需要遍历coins,对于其中的每一种面额的硬币,更新数组 }dp 中的每个大于或等于该面额的元素的值。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    5. 二分法

     猜数字,平方根,搜索旋转数组
     两数相除 # 二分(left:0,right:被除数,判断除数*mid与被除数大小)+快速乘(while (k > 0) {  if ((k & 1) == 1) ans += a;  k >>= 1; a += a;  }),每次循环将a的值翻倍,将k的值减半
     指数运算 pow # 二分法,递归 如x的77次方,可以按照x的38,19,9,4,2,1的顺序
    
    • 1
    • 2
    • 3

    5. 滑动窗口,双指针

     无重复字符的最长子串; #sss left = Math.max(map.get(s.charAt(i))+1,left);
     盛最多水的容器  # 两边向中间收拢
     三数之和    #  先排序,遍历,对于每个小于0的数,双指针从两边向中间靠拢,通过sss if(i > 0 && a[i] == a[i-1]) continue; 去除重复解
     最接近的三数之和(同上)
     链表倒数第n个节点(两个指针,一个走k步再走第二个)
     是否有环(快慢指针)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6. 前缀和(hash存位置数量等,和为**的连续数组)

     最长相等01子数组 # map存前缀和为a时的下标i;
     最长偶数元音子数组
     连续和为 k 倍 的子数组# 前缀和对k取余相同即存在
    
    • 1
    • 2
    • 3

    6. 堆

     中位数 # 两个堆数量不一样,则放入大根堆,再remove大根堆的到小根堆,两个堆数量一样,则放入小根堆,再remove小根堆的到大根堆,小根堆元素多
    
    • 1

    7. 其他

     z字形变换
     下一个升序排列 # 思路:先从后往前找升序对,index= i(i为左边的数);i之后的一定降序;在【i+1,len)之间找到最小的大于num【i】的,然后交换值,然后【i+1,len)之间的升序
     接雨水 # 对于每一个坐标i,雨水为两边最大高度的较小者减去高度:min(left_max,right_max)-height [i] 
     买卖股票 # 遍历一次,记录当前节点之前的最低价格,和最高利润
    
    • 1
    • 2
    • 3
    • 4

    贪心

     整数转罗马数字 # 思路:先枚举,再从大往小减
     罗马转数字 # 有公共前缀,替换
    
    • 1
    • 2

    8. 数学

     整数反转 # 通过取余能求整数的最后一位,思路:通过对10取余求 最后一位,除法求剩余的数字,while循环中处理
     格雷编码 # 原有的集合a(n),前面各位前面加0的集合a1(n)  + a镜像后前面加1的集合a2(n) = 即为 a(n+1),其中a1(n) == a(n)
     最大公约数 #  public static int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); } 除数和余数计算,当除数为0时返回被除数 
    
    • 1
    • 2
    • 3

    [快排]
    两个方法,第一个,当low《high 时,获取中间的指针,并递归调用左右两边
    第二个,返回中间指针,遍历,当遇到比最右边小的时候,交换pointer和当前元素,遍历完成后交换pointer和最右边元素,返回中间的指针pointer
    归并
    分:从中间分开,左右两边循环调用,直至只有一个元素,然后调用第二个方法
    和:第二个方法,申请一个数组做合并使用
    LRU
    get和set方法,基于linkedHashMap(双向链表)set时先删除,再判断容量(超过时删除最后一个元素),再put
    get时如果存在,需要重置元素位置到第一个
    海量数据的中位数:二分,二进制的最高位,次高位。。。,分为两个文件,统计个数

    ========================================================================================================

    回溯模板

    public  static void dfs(){
    //终止条件
    
    for(){
    //if()剪枝
    //操作
    dfs();//递归调用
    //逆操作,回溯
    }
    }
    
    重复全排列剪枝
    要解决重复问题,我们只要设定一个规则,保证在填第
    idx
    idx 个数的时候重复数字只会被填入一次即可。而在本题解中,我们选择对原数组排序,保证相同的数字都相邻,然后每次填入的数一定是这个数所在重复数集合中「从左往右第一个未被填过的数字」,即如下的判断条件:
    ```java
    if(i>0 && nums[i] == nums[i-1] && used[i-1] == 0){
                    continue;
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    01前缀和
    if (map.get(prefixSum) != null) {
    ans = Math.max(ans, i - map.get(prefixSum));
    } else {
    map.put(prefixSum, i);
    }

    ========================================================================================================

    comparator方法 (a,b)->{return a-b;} //升序
    若只有一行可以改为 (a,b)-> a-b //升序

    二进制String转十进制
    Integer.parseInt(“1111”,2)
    十进制转二进制
    Integer.toBinaryString(7)

    位运算
    判断是否是 2 的幂 :A & (A - 1) == 0
    最低位的 1 变为 0 :n &= (n - 1)
    最低位的 1:A & (-A)

    list转数组
    list.toArray();
    二维
    list.toArray(new int[0][]);

    ========================================================================================================

  • 相关阅读:
    pytorch环境下跑通Focal Transformer
    Win10下Qt配置opencv/libtorch闭坑总结
    vite不能选配方案?vite-creater强势来袭!
    详解八大排序
    Git 使用规范流程
    AMBA总线协议之AHB学习记录(2)—ahb_bus的测试(附testbench代码)
    ROS中Rviz实时路径可视化的高效性能优化技巧
    blender assetBrowser 资产浏览器
    在Winform程序中动态绘制系统名称,代替图片硬编码名称
    14届蓝桥青少STEMA-C++组12月评测
  • 原文地址:https://blog.csdn.net/devilhai/article/details/125571356