JAVA剑指OFFER个人总结

 原文地址

https://www.b2bchain.cn/5154.html

下方查看可能有格式问题,推荐查看原文

面试题03. 数组中重复的数字
面试题04. 二维数组中的查找
面试题05. 替换空格
面试题06. 从尾到头打印链表
面试题07. 重建二叉树
面试题09. 用两个栈实现队列
面试题10- I. 斐波那契数列
面试题10- II. 青蛙跳台阶问题
面试题11. 旋转数组的最小数字
面试题12. 矩阵中的路径
面试题13. 机器人的运动范围
面试题14- I. 剪绳子
面试题14- II. 剪绳子 II
面试题15. 二进制中1的个数
面试题16. 数值的整数次方
面试题17. 打印从1到最大的n位数
面试题18. 删除链表的节点
面试题19. 正则表达式匹配
面试题 20. 表示数值的字符串
面试题21. 调整数组顺序使奇数位于偶数前面
面试题22. 链表中倒数第k个节点
面试题24. 反转链表
面试题25. 合并两个排序的链表
面试题26. 树的子结构
面试题27. 二叉树的镜像
面试题28. 对称的二叉树
面试题29. 顺时针打印矩阵
面试题30. 包含min函数的栈
面试题31. 栈的压入、弹出序列
面试题32 - I. 从上到下打印二叉树
面试题32 - II. 从上到下打印二叉树 II
面试题32 - III. 从上到下打印二叉树 III
面试题33. 二叉搜索树的后序遍历序列
面试题34. 二叉树中和为某一值的路径
面试题35. 复杂链表的复制
面试题36. 二叉搜索树与双向链表
面试题37. 序列化二叉树
面试题38. 字符串的排列
面试题39. 数组中出现次数超过一半的数字
面试题40. 最小的k个数
面试题41. 数据流中的中位数
面试题42. 连续子数组的最大和
面试题43. 1~n整数中1出现的次数
面试题44. 数字序列中某一位的数字
面试题45. 把数组排成最小的数
面试题46. 把数字翻译成字符串
面试题47. 礼物的最大价值
面试题48. 最长不含重复字符的子字符串
面试题49. 丑数
面试题50. 第一个只出现一次的字符
面试题51. 数组中的逆序对
面试题52. 两个链表的第一个公共节点
面试题53 - I. 在排序数组中查找数字 I
面试题53 - II. 0~n-1中缺失的数字
面试题54. 二叉搜索树的第k大节点
面试题55 - II. 平衡二叉树
面试题56 - I. 数组中数字出现的次数
面试题56 - II. 数组中数字出现的次数 II
面试题57. 和为s的两个数字
面试题57 - II. 和为s的连续正数序列
面试题58 - I. 翻转单词顺序
面试题59 - I. 滑动窗口的最大值
面试题59 - II. 队列的最大值
面试题60. n个骰子的点数
面试题61. 扑克牌中的顺子
面试题62. 圆圈中最后剩下的数字
面试题63. 股票的最大利润
剑指Offer 64
面试题65. 不用加减乘除做加法
面试题66. 构建乘积数组
面试题67. 把字符串转换成整数
面试题68 - I. 二叉搜索树的最近公共祖先
面试题68 - II. 二叉树的最近公共祖先



面试题03. 数组中重复的数字
class Solution {
    public int findRepeatNumber(int[] nums) {
         Set<Integer> set=new HashSet<>();
        for(int num:nums){
            if(!set.add(num)){
                  return num;
            }
        }
        return -1;
    }
}


面试题04. 二维数组中的查找
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if(matrix==null||matrix.length==0|| matrix[0].length == 0) return false;
        int i = matrix.length - 1, j = 0;
        while(i >= 0 && j < matrix[0].length)
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
}


  面试题05. 替换空格
class Solution {
    public String replaceSpace(String s) {
        StringBuilder  res=new StringBuilder ();
        for(Character c : s.toCharArray()){
              if(c==' '){
                  //加引号
                 res.append("%20");
              }
              else{
                  res.append(c);
              }
        }  
        return res.toString();
    }
}



面试题06. 从尾到头打印链表
返回为数组,直接用栈模拟即可
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        //使用LinkedList 替代stack 性能好
        LinkedList<Integer> stack=new LinkedList<Integer>();

       while(head != null) {
            stack.addLast(head.val);
            head = head.next;
        }
        int[] res = new int[stack.size()];
        for(int i = 0; i < res.length; i++){
             res[i] = stack.removeLast();
        }
           
         return res;
    }
}



  面试题07. 重建二叉树 


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    //key-value 中序 值-序号
    HashMap<Integer, Integer> dic = new HashMap<>();
    int[] po;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        po = preorder;
        for(int i = 0; i < inorder.length; i++) 
            dic.put(inorder[i], i);
        return recur(0, 0, inorder.length - 1);
    }
    //通过索引判断 前序根    中序左 中序右
    TreeNode recur(int pre_root, int in_left, int in_right) {
        if(in_left > in_right) return null;
        //前序 找到当前根节点
        TreeNode root = new TreeNode(po[pre_root]);
        //找出对应中序 索引
        int i = dic.get(po[pre_root]);

        root.left = recur(pre_root + 1, in_left, i - 1);
        root.right = recur(pre_root + (i - in_left + 1), i + 1, in_right);
        return root;
    }
}



面试题09. 用两个栈实现队列


class CQueue {

    LinkedList<Integer> A, B;

    public CQueue() {
        A = new LinkedList<Integer>();
        B = new LinkedList<Integer>();
    }
    
    public void appendTail(int value) {
        A.addLast(value);
    }
    
    public int deleteHead() {
        if(!B.isEmpty()) return B.removeLast();
        //栈A可能为空 
        if(A.isEmpty()) return -1;
        while(!A.isEmpty()){
             B.addLast(A.removeLast());
        }
        //最后B出栈    
        return B.removeLast();
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */



面试题10- I. 斐波那契数列
class Solution {
    public int fib(int n) {
     
       if(n==0) return 0;
       int[] dp=new int[n+1];
 
       dp[1]=1;
       //dp[i]是i不是n
       for(int i=2;i<=n;i++){
        dp[i]=(dp[i-1]+dp[i-2])%1000000007;
       }
       return dp[n];
    }
}



面试题10- II. 青蛙跳台阶问题

class Solution {
    public int numWays(int n) {
        //特例
         if(n==0) return 1;
         //构建DP数组
         int[] dp=new int[n+1];
         //赋值几个初始的值
        dp[0]=1; dp[1]=1;
        //递归循环
         for(int i=2;i<=n;i++){
             //每一步都需要取余
          dp[i]=(dp[i-1]+dp[i-2])%1000000007;
         }
         return dp[n];
    }
}



面试题11. 旋转数组的最小数字

和numbers数组最后一个数字比较  numbers[mid]==numbers[j]时候,需要J-- 缩小范围
public class Solution {

    // [3, 4, 5, 1, 2]
    // [1, 2, 3, 4, 5]
    // 不能使用左边数与中间数比较,这种做法不能有效地减治

    // [1, 2, 3, 4, 5]
    // [3, 4, 5, 1, 2]
    // [2, 3, 4, 5 ,1]

    public int minArray(int[] numbers) {
        int len = numbers.length;
        if (len == 0) {
            return 0;
        }
        int left = 0;
        int right = len - 1;
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (numbers[mid] > numbers[right]) {
                // [3, 4, 5, 1, 2],mid 以及 mid 的左边一定不是最小数字
                // 下一轮搜索区间是 [mid + 1, right]
                left = mid + 1;
            } else if (numbers[mid] == numbers[right]) {
                // 只能把 right 排除掉,下一轮搜索区间是 [left, right - 1]
                right = right - 1;
            } else {
                // 此时 numbers[mid] < numbers[right]
                // mid 的右边一定不是最小数字,mid 有可能是,下一轮搜索区间是 [left, mid]
                right = mid;
            }
        }

        // 最小数字一定在数组中,因此不用后处理
        return numbers[left];
    }
}
 


面试题12. 矩阵中的路径

//DFS搜索
class Solution {
    public boolean exist(char[][] board, String word) {
       char[] words=word.toCharArray();
       //尝试矩阵的全部点作为出发点入口
       for(int i=0;i<board.length;i++){
           for(int j=0;j<board[0].length;j++){
               //有满足条件返回TRUE
               if(dfs(board,words,i,j,0)) return true;
           }
       }
       return false;
    }
    private boolean dfs(char[][] board,char[] words,int i,int j,int k){
        // board[i][j] != words[k] 当前遍历点与 目标值不相等
        if(i<0||i>=board.length||j<0||j>=board[0].length|| board[i][j] != words[k]){
            return false;
        }
        //遍历到结尾了 成功【注意结束为words.length - 1】
         if(k == words.length - 1) return true;
       
         //处理当前元素
        char tmp=board[i][j];
        //防止重复遍历
        board[i][j]='/';
        //相当于k是一个索引判断遍历到了word的哪一位,相当于遍历的层数
        boolean res = dfs(board, words, i + 1, j, k + 1) || dfs(board, words, i - 1, j, k + 1) || 
                      dfs(board, words, i, j + 1, k + 1) || dfs(board, words, i , j - 1, k + 1);
        board[i][j] = tmp;
        return res;
    }
}



面试题13. 机器人的运动范围

方法一:DFS通用
class Solution {
    int ans=0;
    public int movingCount(int m, int n, int k) {
      //分析题目给定不能经过已经遍历的位置,所以增加是否访问数组
      boolean[][] used=new boolean[m][n];
      dfs(m,n,k,0,0,used);
      return ans;
    }

    //i,j为当前访问的座标
    private  void dfs(int m,int n,int k,int i,int j,boolean[][] used){

        //dfs先需要写递归结束条件
        if(i>m-1 || j>n-1  || digitSum(i)+digitSum(j)>k || used[i][j]==true)  return;

            used[i][j] = true;
            ans++;
            //只向下和右遍历即可
            dfs(m, n, k, i + 1, j, used);
            dfs(m, n, k, i, j + 1, used);
    }

    //求数位和
    private int digitSum(int num){
        int sum=0;
        while(num>0){
            sum+=num%10;
            num/=10;
        }
        return sum;
    }
} 

方法二:数学公式推导
class Solution {
    //全局变量的使用
        int m,n,k;
        boolean[][] visited;

    public int movingCount(int m, int n, int k) {
       this.m=m; this.n=n;this.k=k;
       this.visited=new boolean[m][n];

       return dfs(0,0,0,0);
    }
    //i,j 座标  si sj 数位和
    private int dfs(int i,int j,int si,int sj){
        if(i>=m||j>=n||k<si+sj|| visited[i][j]){
         return 0;
        }
        
        visited[i][j]=true;
        return 1+dfs(i+1,j,(i+1)%10==0?si-8:si+1,sj)+dfs(i,j+1,si,(j+1)%10==0?sj-8:sj+1);
    }
}



面试题14- I. 剪绳子
小数据DP问题 从最后剪得情况来反推
此处DP初始值和最开始特殊情况不一样
class Solution {
    public int cuttingRope(int n) {
        //DP算法
        //当绳子长度n>3时,最后一段绳子长度只有2,3两种情况(证明参考高赞精选贴),因此:
         //dp[i] = max { 2 * dp[i-2], 3 * dp[i-3] }
       
       //推到得出
        if(n<=3) return n-1;
        int[] dp = new int[n+1];
        //【记住这个dp的初始值】
        dp[2] = 2; dp[3] = 3;
        for(int i = 4; i <= n; i++){
            dp[i] = Math.max(2 * dp[i-2] , 3 * dp[i-3]) ;
        }
        return dp[n];

    }
}


面试题14- II. 剪绳子 II
大数贪心
大数定义必须用long防止溢出 此时n>4为循环判断条件
class Solution {
    public int cuttingRope(int n) {
         //大数贪心 特殊情况2,3,4 
         if(n==2) return 1;//题目要求必须至少切一刀
         if(n==3) return 2;
         //定义long 防止溢出 大数用long
         long res=1;
         //尽可能取3结果最大,n为绳子长度,每次结果乘3取余,总长度对应减去3
         while(n>4){
              res*=3;
              res=res%1000000007;
              n-=3;
         }
         //乘上剩下绳子的长度
         return (int) (res*n%1000000007);
    } 
}



面试题15. 二进制中1的个数
方法一:不改变原数字
【此时为不等于0】
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int ans=0;
        int mask=1;

        for (int i = 0; i <32 ; i++) {
            //此时为不等于0
            if((n & mask)!=0) ans++;
            //考虑到不改变原数字,移动掩码
            mask<<=1;
        }
        return ans;
    }
}



方法二:
>>将二进制高位用1补上,而>>>将二进制高位用0补上,这就导致了>>>将负数的移位操作结果变成了正数,因为高位用0补上了
 public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        //输入二进制数
        //根据 与运算 定义,设二进制数字 nn ,则有:
//若 n  & 1 == 0 ,则二进制 最右一位 为 0 ;
//若 n  & 1 == 1 ,则二进制 最右一位 为 1 。
        int res=0;
        while(n!=0){
            res+=n&1;
            //右移JAVA》》》 记得是>>> 
            n>>>=1;
        }
        return res;
    }
}



面试题16. 数值的整数次方

快速幂
把指数 n 做“二进制分解”,在底数不断自身乘以自身的过程中,将最终结果需要的部分保存下来。

class Solution {
    public double myPow(double x, int n) {
         //特殊情况
         if(x==0) return 0;
         //【b定义在之前后面再判断正负】
         long b=n;
         //指数为负数
         if(b<0){
             x=1/x;
             b=-b;
         }
         double res=1.0;
         while(b>0){
             //当前指数位为1,累乘到结果上
             //当为0时候,任意数的0次方==1 所以不需要乘
            if(b%2==1){
                res*=x;
            }
            //相当于每次底数*x
            x*=x;
            b=b/2;
         }
         return res;
    }
}


面试题17. 打印从1到最大的n位数
快速幂同16题,相当于用快速幂求需要打印的总数目
class Solution {
    private int mypow(int x,int k){
       if(x==0) return 0;
       int res=1;
       long b=k;
       while(b>0){
           if(b%2==1) res*=x;

           x*=x;
           b/=2;
       }
       return res;
    }
    public int[] printNumbers(int n) {
        int len=mypow(10,n);
         int[] ans=new int[len-1];
         for(int i=0;i<len-1;i++){
            ans[i]=i+1;
         }
         return ans;
    }
    
}


面试题18. 删除链表的节点
方法一:单个指针操作
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        //不加这句,这个地方会出空指针异常
      if(head.val==val) return head.next;
      ListNode dummy=head;
      while(head.next!=null && head.next.val!=val){
          head=head.next;
      } 
      head.next=head.next.next;
      return dummy;
    }
}


方法二:俩个指针操作
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        //删除节点为开始节点,指向head.next而不是null
        //这个判断有点坑的呀
        if(head.val==val) return head.next;
        ListNode cur=head;
        ListNode pre=head.next;
        while(pre!=null && pre.val!=val){
         pre=pre.next;
         cur=cur.next;
        }
        cur.next=pre.next;
        return head;
    }
}



面试题19. 正则表达式匹配

https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/zhu-xing-xiang-xi-jiang-jie-you-qian-ru-shen-by-je/
class Solution {
    public boolean isMatch(String s, String p) {
        int m=s.length();
        int n=p.length();

        boolean [][] ans=new boolean[m+1][n+1];
        //等于m,n
        for(int i=0;i<=m;i++){
            for(int j=0;j<=n;j++){
                //空正则
                if(j==0){
                    ans[i][j]= i==0;
                }else{
                    //不等 *  直接判断前一个是否相等,或者正则正则的对应为.
                    if(p.charAt(j-1)!='*'){
                        if(i>0&&(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.')){
                            ans[i][j]=ans[i-1][j-1];
                        }
                    }else{
                        // * 对应为0个  例如把c* 跳过,因此为j-2 正则表达式前移2个
                        if(j>=2){
                            ans[i][j]|=ans[i][j-2];
                        }
                        //*为 一个或多个  比如正则为 c* 此时比较的是c和对应原串的c 或者 正则j-2处为.不是c 
                        if(i>=1&&j>=2&&(s.charAt(i-1)==p.charAt(j-2) || p.charAt(j-2)=='.')){
                            //原串前移一个继续比较,正则不移动
                            ans[i][j] |=ans[i-1][j];
                        }
                    }

                }
            }

        }
        return ans[m][n];
    }
}



面试题 20. 表示数值的字符串
https://leetcode-cn.com/problems/valid-number/solution/java-luo-ji-chao-qing-xi-jie-fa-by-charlesgao/

class Solution {
    public boolean isNumber(String s) {
        if(s==null||s.length()==0) return false;
        boolean numSeen=false;
        boolean dotSeen=false;
        boolean eSeen=false;
        char arr[]=s.trim().toCharArray();
        for(int i=0; i<arr.length; i++){
            if(arr[i]>='0'&&arr[i]<='9'){
                numSeen=true;
            }else if(arr[i]=='.'){
                if(dotSeen||eSeen){
                    return false;
                }
                dotSeen=true;
            }else if(arr[i]=='E'||arr[i]=='e'){
                if(eSeen||!numSeen){
                    return false;
                }
                eSeen=true;
                numSeen=false;
            }else if(arr[i]=='+'||arr[i]=='-'){
                if(i!=0&&arr[i-1]!='e'&&arr[i-1]!='E'){
                    return false;
                }
            }else{
                return false;
            }
        }
        return numSeen;
    }
}



面试题21. 调整数组顺序使奇数位于偶数前面
class Solution {
    public int[] exchange(int[] nums) {
       int i=0,j=nums.length-1,tmp;
       while(i<j){
          //找到偶数,跳出循环
          while(i<j && (nums[i]&1)==1) i++;
          //找到奇数,跳出循环
          while(i<j && (nums[j]&1)==0) j--;
          //交换
          tmp=nums[i];
          nums[i]=nums[j];
          nums[j]=tmp;
       }
       return nums;
    }
}



面试题22. 链表中倒数第k个节点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
          ListNode fast=head;
          ListNode slow=head;
          for(int i=0;i<k;i++){
            //防止空指针【判断fast指向空,则返回空,证明达不到所需的倒数k个节点】
            if(fast==null) return null;
            fast=fast.next;   
          }
          //防止空指针,此时必须为fast
          while(fast!=null){
            fast=fast.next;
            slow=slow.next;
          }
           return slow;
    }
}



面试题24. 反转链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur=null;
        ListNode pre=head;
        while(pre!=null){
            ListNode t=pre.next;
            pre.next=cur;
            cur=pre;
            pre=t;
        }
        return cur;
    }
}



面试题25. 合并两个排序的链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
         ListNode dummy=new ListNode(-1);
         ListNode cur=dummy;
         
         while(l1!=null&&l2!=null){
            if(l1.val<l2.val){
              cur.next=l1;
              l1=l1.next;
            }
            else{
                cur.next=l2;
                l2=l2.next;
            }
            cur=cur.next;
         }
         cur.next= l1!=null?l1 :l2;
         return dummy.next;
    }
}



树递归类的题,
递归函数内:
1.先判断是不是为空
2.进行条件判断,比如是否相等,是否有一个为空
3.继续调用递归函数

面试题26. 树的子结构
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        //俩树均不为空  A根节点树包含B 或者 A左包含B 或者A右包含B 
          return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
 
    }
    //判断以A为根节点的树 包含树B
    private boolean recur(TreeNode A,TreeNode B){
          if(B==null) return true;
          if(A==null||A.val!=B.val) return false;
          return recur(A.left,B.left) &&recur(A.right,B.right);
    }
}


面试题27. 二叉树的镜像
递归
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
         if(root==null) return null;
         //【先保存一下root.left的值】
         TreeNode tmp=root.left;
         root.left=mirrorTree(root.right);
         root.right=mirrorTree(tmp);
         return root;
    }
}

 非递归
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
         if(root==null) return null;
         Stack<TreeNode> stack=new Stack<>();
         stack.add(root);
         while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if(node.left != null) stack.add(node.left);
            if(node.right != null) stack.add(node.right);
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return root;
    }
}


面试题28. 对称的二叉树
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
          if(root==null) return true;
          return helper(root.left,root.right);
    }
    private boolean helper(TreeNode left,TreeNode right){
        if(left==null&&right==null){
            return true;
        }
        if(left==null||right==null||left.val!=right.val){
            return false;
        }
        return helper(left.left,right.right) && helper(left.right,right.left);
    }
}

面试题29. 顺时针打印矩阵


class Solution {
    public int[] spiralOrder(int[][] matrix) {
        //特殊条件
        if(matrix.length == 0) return new int[0];

        int l=0, r=matrix[0].length-1, t=0, b=matrix.length-1, x=0;

        //返回一维数组
        int[] res=new int[(r + 1) * (b + 1)];
        while(true){
            //开始i=l【结束条件都和当前的rltb有关,与0无关】
             for(int i=l;i<=r;i++) res[x++]=matrix[t][i];
             if(++t>b) break;
             for(int i=t;i<=b;i++) res[x++]=matrix[i][r];
             if(l>--r) break;
             for(int i=r;i>=l;i--) res[x++]=matrix[b][i];
             if(t>--b) break;
             for(int i=b;i>=t;i--) res[x++]=matrix[i][l];
             if(++l>r) break;

        }
        return res;
    }
}



面试题30. 包含min函数的栈
函数设计:
push(x) 函数: 重点为保持栈 B 的元素是 非严格降序 的。
将 x 压入栈 A (即 A.add(x) );
若 ① 栈 B 为空 或 ② x 小于等于 栈 B 的栈顶元素,则将 x 压入栈 B (即 B.add(x) )。
pop() 函数: 重点为保持栈 A,B 的 元素一致性 。
执行栈 A 出栈(即 A.pop() ),将出栈元素记为 y ;
若 y 等于栈 B 的栈顶元素,则执行栈 B 出栈(即 B.pop() )。
top() 函数: 直接返回栈 A 的栈顶元素即可,即返回 A.peek() 。
min() 函数: 直接返回栈 B 的栈顶元素即可,即返回 B.peek() 。

class MinStack {
    Stack<Integer> A;
    Stack<Integer> B;
    /** initialize your data structure here. */
    public MinStack() {
       A=new Stack<>();
       B=new Stack<>();
    }
    
    public void push(int x) {
        A.add(x);
        //b为空或者..大于-等于--
        if(B.empty() ||x<=B.peek()) B.add(x);
    }
    
    public void pop() {
        if(A.pop().equals(B.peek())) B.pop();
        
       
    }
    
    public int top() {
       return A.peek();
    }
    
    public int min() {
         return B.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */



面试题31. 栈的压入、弹出序列
stack存pushed
当stack不为空且栈顶等于poped时候
stack出栈
最后判断是否栈空
考虑借用一个辅助栈 stackstack ,模拟 压入 / 弹出操作的排列。
根据是否模拟成功,即可得到结果。

入栈操作: 按照压栈序列的顺序执行。
出栈操作: 每次入栈后,循环判断 “栈顶元素 == 弹出序列的当前元素” 是否成立,
           将符合弹出序列顺序的栈顶元素全部弹出。
           
由于题目规定 栈的所有数字均不相等 ,
因此在循环入栈中,
每个元素出栈的位置的可能性是唯一的(
若有重复数字,则具有多个可出栈的位置)。
因而,在遇到 “栈顶元素 == 弹出序列的当前元素” 就应立即执行出栈。

算法流程:
初始化: 辅助栈 stackstack ,弹出序列的索引 ii ;
遍历压栈序列: 各元素记为 num ;
元素 num入栈;
循环出栈:若 stack 的栈顶元素 == 弹出序列元素 popped[i] ,则执行出栈与 i++ ;
返回值: 若 stack 为空,则此弹出序列合法。



class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack=new Stack<>();
        int i=0;
        for(int num:pushed){、
       // 【所有元素都要入栈进行判断】
           stack.push(num);
           //判断栈顶元素和给定序列当前元素是否等
           while(!stack.isEmpty()&&stack.peek()==popped[i]){
               stack.pop();
               i++;
           }
        }
        return stack.isEmpty();
    }
}



面试题32 - I. 从上到下打印二叉树
输出为一维数组
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] levelOrder(TreeNode root) {
        //特殊条件判断
       if(root == null) return new int[0]; 
       List<Integer> ans=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
       queue.add(root);
       while(!queue.isEmpty()){
           //需要新建一个节点,因为后面需要左右子树入栈 操作node不是root
           TreeNode node=queue.poll();
           ans.add(node.val);
           //不为空才可以入队列
           if(node.left != null) queue.add(node.left);
           if(node.right != null)  queue.add(node.right);
       }
       int[] res=new int[ans.size()];
       for(int i=0;i<ans.size();i++){
           //get方法取值
            res[i]=ans.get(i);
       }
       return res;
    }
}




面试题32 - II. 从上到下打印二叉树 II
输出二维数组
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
       List<List<Integer>> ans=new ArrayList<>();
       //【特殊条件需要加】
       if(root == null) return ans;
       Queue<TreeNode> queue=new LinkedList<>();
      queue.add(root);
       while(!queue.isEmpty()){
           //存放当前层的值
          List<Integer> tmp=new ArrayList<>();
          // 使用i--,让size()只在循环开始使用一次,使循环次数不受队列长度变化影响
          for(int i = queue.size(); i > 0; i--){
             TreeNode node=queue.poll();
             tmp.add(node.val);
             if(node.left!=null) queue.add(node.left);
             if(node.right!=null) queue.add(node.right);
          }
         ans.add(tmp);
       }
       return ans;
    }
}



面试题32 - III. 从上到下打印二叉树 III
输出二维数组带奇偶
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
         List<List<Integer>> ans=new ArrayList<>();
         Queue<TreeNode> queue=new LinkedList<>();
         if(root!=null) queue.add(root);
         while(!queue.isEmpty()){
             //LinkedList
             LinkedList<Integer> tmp=new LinkedList<>();
             for(int i=queue.size();i>0;i--){
                TreeNode node=queue.poll();
                if(ans.size() % 2 == 1) tmp.addFirst(node.val);
                else tmp.addLast(node.val);

                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
             }
             ans.add(tmp);
         }
         return ans;
    }
}


面试题33. 二叉搜索树的后序遍历序列

递归 时间复杂度o(n2)

class Solution {
    public boolean verifyPostorder(int[] postorder) {
      return helper(postorder,0, postorder.length - 1);
    }
    private boolean helper(int[] postorder,int i,int j){
        //等于也可以。子树节点小于等于1
       if(i>=j) return true;
       int p=i;
       while(postorder[p]<postorder[j]) p++;
       int m=p;
       while(postorder[p]>postorder[j]) p++;
       //m,j-1 记得去除最后根节点【只是之后递归判断左右子树,最后一个为当前根节点无需继续判断】
       return j==p && helper(postorder,i,m-1) && helper(postorder,m,j-1);
    }
}





单调栈 o(n)
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        Stack<Integer> stack=new Stack<>();
        int root=Integer.MAX_VALUE;
        for(int i=postorder.length-1;i>=0;i--){
            //当前比根节点大则 返回false
           if(postorder[i]>root) return false;
           
           //当前遍历元素比栈顶元素小,就一直出栈,直达栈为空或者比他大停止
           //这样就可以找到大于且最接近postorder[i]的根节点
           //将根节点进行更新
           //【相当于进行左子树的判断,因为之前右子树已经判断过了】
           while(!stack.isEmpty()&&stack.peek()>postorder[i]){
                root = stack.pop();
           }
           //递增单调栈,这个地方是必须入栈的
             stack.add(postorder[i]);
        }
      return true;
    }
}


面试题34. 二叉树中和为某一值的路径
回溯递归,因此需要额外的函数
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    //前面定义 LinkedList
     LinkedList<List<Integer>> ans=new LinkedList<>();
      LinkedList<Integer> path=new LinkedList<>();

    public List<List<Integer>> pathSum(TreeNode root, int sum) {
     recur(root,sum);
      return ans;
    }
    private void recur(TreeNode root,int sum){
        //返回值,表示遍历到了最后没有节点的位置
         if(root==null) return;
         path.add(root.val);
         sum-=root.val;
         if(sum==0 && root.left==null && root.right==null){
             //必须new  相当于复制一个path,防止后面修改影响
             //记录路径时若直接执行 res.append(path) ,则是将 path 对象加入了 res ;
             //后续 path 改变时, res 中的 path 对象也会随之改变
             //【new LinkedList(path)】
             ans.add(new LinkedList(path));
         }
         //递归左右节点
         recur(root.left,sum);
         recur(root.right,sum);
         //向上回溯,删除当前节点
         path.removeLast();
         
    }
}



面试题35. 复杂链表的复制
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
 //不需要辅助空间,需要额外将两个链表进行拆分
    public Node copyRandomList(Node head) {
        if(head==null) return null;
        copy2(head);
        randomAdd2(head);
        return build2(head);
    }
    //在开始每个结点后面复制一个结点
    public void copy2(Node head){
         while(head!=null){
            Node copy = new Node(head.val);
            copy.next = head.next;
            head.next =copy;
            head = copy.next;
        }
    }
    //进行随机指针的复制
    public void randomAdd2(Node head){
        while(head!=null){
           if(head.random!=null) head.next.random = head.random.next;
           head=head.next.next; 
        }
    }
    //链表拆分
    public Node build2(Node head){
        //将链表拆成两个,注意要恢复原有的链表
        Node res = head.next;
        Node tmp = res;
        //这一步不可缺少,保证第一个复制节点对N N'的分离操作
        head.next = head.next.next;
        head = head.next;
        while(head!=null){
            tmp.next = head.next;
            head.next = head.next.next;
            
            tmp=tmp.next;
            head = head.next;
         }
        return res;
    }

 
}


HASHMAP复制
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        // 使用哈希
        // 哈希表中<原表节点,新表节点>
        // 
        Map<Node,Node> map=new HashMap<>();
        Node p=head;
        while(p!=null){
            map.put(p,new Node(p.val));
            p=p.next;
        }
        p=head;
        while(p!=null){
            map.get(p).next=map.get(p.next);
            map.get(p).random=map.get(p.random);
            p=p.next;
        }
        return map.get(head);
        
    }
}
 



面试题36. 二叉搜索树与双向链表
/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val,Node _left,Node _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
    Node head,pre;
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;
        dfs(root);
        head.left=pre;
        pre.right=head;
        return head;
    }
    private void dfs(Node cur){
        if(cur==null) return;
        //通过中序遍历来相当于移动cur,pre随着移动,形成双向链表
        dfs(cur.left);
        //设置返回链表的头节点
        if(pre==null) head=cur;
        else pre.right=cur;
            
        cur.left=pre;
        pre=cur;
        dfs(cur.right);
    }
}




面试题37. 序列化二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    private void seHelper(TreeNode root,StringBuilder sb){
        //结束条件 返回
       if(root==null){
          sb.append("#,");
          return;
       } 
       sb.append(root.val);
       sb.append(",");
       seHelper(root.left,sb);
       seHelper(root.right,sb);
    }
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        StringBuilder sb=new StringBuilder();
        seHelper(root,sb);
        return sb.toString();
    }

    private TreeNode deHelper(LinkedList<String> strlist){
         String first=strlist.removeFirst();
        if(first.equals("#")) return null;
        TreeNode root=new TreeNode(Integer.valueOf(first));
        root.left=deHelper(strlist);
        root.right=deHelper(strlist);
        return root;
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        LinkedList<String>  strlist=new LinkedList<>(Arrays.asList( data.split(",")));
        return deHelper(strlist);
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));


面试题38. 字符串的排列
class Solution {
    LinkedList<String> res=new LinkedList<>();
    char[] c;

    public String[] permutation(String s) {
     c=s.toCharArray();
     dfs(0);
     //返回类型
     return res.toArray(new String[res.size()]);
    }
    //递归函数
    //输入递归第x层
    private void dfs(int x){
        //递归结束条件
        if(x==c.length-1){
            res.add(String.valueOf(c));
            return;
        }
        //处理防止有重复的数
      Set<Character> myset=new HashSet<>();
        //递推第x层 for循环
        for(int i=x;i<c.length;i++){
            //集合contains
          if(myset.contains(c[i])) continue;
          myset.add(c[i]);
          
          swap(i,x);//交换,将 c[i] 固定在第 x 位 
          dfs(x+1); //递归下一层,开启固定第 x + 1 位字符
          swap(i,x);//恢复交换

        }
    } 
    private void swap(int i,int x){
      char t=  c[i];
        c[i]= c[x];
        c[x]=t;
    }
}

面试题39. 数组中出现次数超过一半的数字

HASHmap 
class Solution {
    public int majorityElement(int[] nums) {
       Map<Integer,Integer> map=new HashMap<>();
       int len=nums.length/2;
       for(int num :nums){
             if(map.containsKey(num)) {
                 map.put(num,map.get(num)+1);
             }
             else{
                 map.put(num,1);
             }
             if(map.get(num)>len) return num;
       }
       return -1;
       
    }
}


投票法

class Solution {
    public int majorityElement(int[] nums) {
       int votes=0,x=0;
       for(int num :nums){
           if(votes==0) {
              x=num;
           }
            if(num==x) votes++;
            else votes--;       
       }
       // 验证 x 是否为众数
       int count=0;
       for(int t :nums){
           if(x==t) count++; 
       }
       int len=nums.length/2;
       return count>len?x:0;
    }
}



面试题40. 最小的k个数

动态数据流只能使用堆排序,固定的都可以使用

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
       if(k==0){
           return new int[0];
       }
       //最小K个数,需要一个大顶堆
       Queue<Integer> heap=new PriorityQueue<>(k,(i1,i2)->Integer.compare(i2,i1));
       for(int num :arr){
           //进堆的条件
          if(heap.isEmpty()||heap.size()<k||heap.peek()>num){
               heap.offer(num);
          }
          //出堆的条件
          if(heap.size()>k){
             heap.poll();
          }
       }

       int[] ans=new int[k];
       int j = 0;
       for (int e : heap) {
          ans[j++] = e;
       }
       return ans;
    }
}


快排
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k == 0 || arr.length == 0) {
            return new int[0];
        }
        // 最后一个参数表示我们要找的是下标为k-1的数
        return quickSearch(arr, 0, arr.length - 1, k - 1);
    }

    private int[] quickSearch(int[] nums, int lo, int hi, int k) {
        // 每快排切分1次,找到排序后下标为j的元素,如果j恰好等于k就返回j以及j左边所有的数;
        int j = partition(nums, lo, hi);
        if (j == k) {
            return Arrays.copyOf(nums, j + 1);
        }
        // 否则根据下标j与k的大小关系来决定继续切分左段还是右段。
        return j > k? quickSearch(nums, lo, j - 1, k): quickSearch(nums, j + 1, hi, k);
    }

    // 快排切分,返回下标j,使得比nums[j]小的数都在j的左边,比nums[j]大的数都在j的右边。
    private int partition(int[] nums, int lo, int hi) {
        int v = nums[lo];
        int i = lo, j = hi + 1;
        while (true) {
            while (++i <= hi && nums[i] < v);
            while (--j >= lo && nums[j] > v);
            if (i >= j) {
                break;
            }
            int t = nums[j];
            nums[j] = nums[i];
            nums[i] = t;
        }
        //相遇的点 与开始标定的KEY值进行交换
        nums[lo] = nums[j];
        nums[j] = v;
        return j;
    }
}


  快速排序 https://blog.csdn.net/shujuelin/article/details/82423852


面试题41. 数据流中的中位数



class MedianFinder {
    Queue<Integer> A;
    Queue<Integer> B;
    /** initialize your data structure here. */
    public MedianFinder() {
      A=new PriorityQueue<>();//小顶堆
      B=new PriorityQueue<>((x,y)->(y-x));
    }
    
    public void addNum(int num) {
        if(A.size()!=B.size()){
            B.add(num);
            A.add(B.poll());
        }else{
              A.add(num);
            B.add(A.poll());
        }
    }
    
    public double findMedian() {
         return A.size()==B.size()? (A.peek()+B.peek())/2.0 :B.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */


面试题42. 连续子数组的最大和

比较更新即可,无需DP
class Solution {
    public int maxSubArray(int[] nums) {
     // 用sum动态地记录当前节点最大连续子数组和
        int sum = 0;

        // 用max 动态记录最大值
        int max = nums[0];

        for(int i:nums){
            // 如果当前和小于0,只会连累后面的数组和,干脆放弃!
            if(sum >=0) sum+= i;
            else sum = i;

            // 更新max
            max = Math.max(max, sum);
        }
        return max;

 
    }
}


面试题43. 1~n整数中1出现的次数
class Solution {
    public int countDigitOne(int n) {
        //找规律题。。
        int digit = 1, res = 0;
        int high = n / 10, cur = n % 10, low = 0;
        while(high != 0 || cur != 0) {
            //规律
            if(cur == 0) res += high * digit;
            else if(cur == 1) res += high * digit + low + 1;
            else res += (high + 1) * digit;

            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return res;
    }
}
 


面试题44. 数字序列中某一位的数字
class Solution {
    public int findNthDigit(int n) {
      //注意特例
      if(n<10) return n;
      //当前位数数字 比如2位数10---99共90个数字
      int count=0;
      //位数
      int power=1;

      //求所在几位数
      while(true){
         count=helper(power);
         if(n<count) break;
         n-=count;
         power++;
      }
      //所在数字的值 比如数字13
      int resNum=(int)(Math.pow(10,power-1)+n/power);
      //答案对应的字符,再减去字符'0'就得到答案。
      return String.valueOf(resNum).charAt(n % power)- '0';    
    }

    //求power位包含数字 位数而不是数目 如10-99 有90*power位 power = 2
      private int helper(int power){
          if(power==1) return 10;
          return (int)(Math.pow(10,(power-1))*9*power); 
      }
}


面试题45. 把数组排成最小的数

方法一:快速排序手写
class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++)
            strs[i] = String.valueOf(nums[i]);
        fastSort(strs, 0, strs.length - 1);
        StringBuilder res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    }
    void fastSort(String[] strs, int l, int r) {
        if(l >= r) return;
        int i = l, j = r;
        String tmp = strs[i];
        while(i < j) {
            while((strs[j] + strs[l]).compareTo(strs[l] + strs[j]) >= 0 && i < j) j--;
            while((strs[i] + strs[l]).compareTo(strs[l] + strs[i]) <= 0 && i < j) i++;
            tmp = strs[i];
            strs[i] = strs[j];
            strs[j] = tmp;
        }
        strs[i] = strs[l];
        strs[l] = tmp;
        fastSort(strs, l, i - 1);
        fastSort(strs, i + 1, r);
    }
}



方法二:内置排序函数
class Solution {
    public String minNumber(int[] nums) {
        //转换成为string数组
        String[] strs=new String[nums.length];
       for(int i = 0; i < nums.length; i++) 
            strs[i] = String.valueOf(nums[i]);
     
        Arrays.sort(strs,(x, y) -> (x + y).compareTo(y + x));

        StringBuilder sb=new StringBuilder();
        for(String s:strs){
          sb.append(s);
        }
        return sb.toString();
    }
}


面试题46. 把数字翻译成字符串


class Solution {
    public int translateNum(int num) {
       //dp问题表示前i个数字,不包括i的翻译数目
        //方便操作转换为string
        String s=String.valueOf(num);
        int len=s.length();
        if(len<2) return len;

        int[] dp=new int[len+1];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=len;i++){
            //依次取各位的值
            String t=s.substring(i-2,i);
            //翻译俩位数到字母 有俩种情况
            if(t.compareTo("10")>=0 && t.compareTo("25")<=0){
                dp[i]=dp[i-1]+dp[i-2];
            } else{
                dp[i]=dp[i-1];
            }

        }
        return dp[len];
    }
}


面试题47. 礼物的最大价值
特殊情况
新建DP数组
dp[0],dp[1]或者其他初始值
动态转移
返回结果


先处理计算上边界,左边界的值
然后考虑动态转移方程 当前值可以怎么推到出来
class Solution {
    public int maxValue(int[][] grid) {
       int m=grid.length;
       if(m==0) return 0;
       int n=grid[0].length;
       for(int i=1;i<m;i++){
            grid[i][0]+=grid[i-1][0];
       }
       for(int j=1;j<n;j++){
            grid[0][j]+=grid[0][j-1];
       }
        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++) 
                grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);
        return grid[m - 1][n - 1];
 
    }
}


面试题48. 最长不含重复字符的子字符串
出现重复时候

左右边界的维护  滑动窗口
hashmap存储 字符 更新左边界

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character,Integer> map=new HashMap();
        //左边界
        int i=-1;
        int res=0;
        for (int j = 0; j <s.length() ; j++) {
            char t=s.charAt(j);
            if(map.containsKey(t)) {
                //更新左边界
                //例如arabcar,j为最后一个r时,i更新为第一个r的索引就出了问题(此时i应该在第二个a处),所以一定要用max
                i=Math.max(i,map.get(t));
            }
            map.put(t,j);
            res=Math.max(res,j-i);
        }
        return res;
    }
}

 

面试题49. 丑数
https://leetcode-cn.com/problems/chou-shu-lcof/solution/chou-shu-ii-qing-xi-de-tui-dao-si-lu-by-mrsate/
三个指针对应维护三个数组进行每次取最小值
class Solution {
    public int nthUglyNumber(int n) {
       //定义三个指针分别指向2,3,5的k次方数组
        int a=0,b=0,c=0;
        //存丑数,从小到大
        int[]dp=new int[n];
        dp[0]=1;
        for (int i = 1; i <n ; i++) {
            //指向2,3,5的k次方数组
            int n2=dp[a]*2,n3=dp[b]*3,n5=dp[c]*5;
            dp[i]=Math.min(Math.min(n2,n3),n5);
            if(dp[i]==n2) a++;
            if(dp[i]==n3) b++;
            if(dp[i]==n5) c++;

        }
        return dp[n-1];
    }
}

面试题50. 第一个只出现一次的字符
class Solution {
    public char firstUniqChar(String s) {
        //map (字符,是否出现) 
        Map<Character,Boolean> map=new HashMap<>();
       char[] str=s.toCharArray();
       //字符 是否出现
       for(char t:str){
         map.put(t,!map.containsKey(t));
       }
       //输出第一个 出现字符
       for(char t:str){
          if(map.get(t)) return t;
       }
       return ' ';
    }
}


面试题51. 数组中的逆序对
public class Solution {

    public int reversePairs(int[] nums) {
        int len = nums.length;

        if (len < 2) {
            return 0;
        }
        //防止修改原数组 拷贝
        int[] copy = new int[len];
        for (int i = 0; i < len; i++) {
            copy[i] = nums[i];
        }

        //辅助数组,合并用
        int[] temp = new int[len];
        return reversePairs(copy, 0, len - 1, temp);
    }



 //nums[left..right] 计算逆序对个数并且排序 
    private int reversePairs(int[] nums, int left, int right, int[] temp) {
        if (left == right) {
            return 0;
        }

        int mid = left + (right - left) / 2;
        int leftPairs = reversePairs(nums, left, mid, temp);
        int rightPairs = reversePairs(nums, mid + 1, right, temp);
        
        //已经有序,提前结束递归
        if (nums[mid] <= nums[mid + 1]) {
            return leftPairs + rightPairs;
        }

        int crossPairs = mergeAndCount(nums, left, mid, right, temp);
        return leftPairs + rightPairs + crossPairs;
    }



//合并有序数组,nums[left..mid] 有序,nums[mid + 1..right] 有序
    private int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp) {
        for (int i = left; i <= right; i++) {
            temp[i] = nums[i];
        }

        int i = left;
        int j = mid + 1;

        int count = 0;
        for (int k = left; k <= right; k++) {

            //左半部分递归完
            if (i == mid + 1) {
                nums[k] = temp[j];
                j++;
            }
            //右半部分递归完
             else if (j == right + 1) {
                nums[k] = temp[i];
                i++;
            } 
             
            else if (temp[i] <= temp[j]) {
                nums[k] = temp[i];
                i++;
            } 
            else {
                nums[k] = temp[j];
                j++;
                //只有这里操作count++
                count += (mid - i + 1);
            }
        }
        return count;
    }
}


面试题52. 两个链表的第一个公共节点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode n1=headA;
        ListNode n2=headB;
        while(n1!=n2){
            //【重新指向的是头结点】
            n1= n1==null?headB:n1.next;
            n2= n2==null?headA:n2.next;    
        }
        return n1;
    }
}


面试题53 - I. 在排序数组中查找数字 I



分别求7和8的右边界 相减 得出 8的长度
我不太懂为什么要减去helper(target-1), 这个target-1=7刚好在这个数组里面,那如果target-1不在这个数组里面呢?
哈喽~ 实际上就是在查找 target - 1 在数组中的插入点,而“右边界”代表存在和 target - 1 相等的数字时,插入到相等数字的最右边~


若查找 右边界 right ,则执行 i = m + 1 ;(跳出时 i指向右边界)
若查找 左边界 left ,则执行 j = m - 1;(跳出时 j 指向左边界)

class Solution {
    public int search(int[] nums, int target) {
        return helper(nums,target)-helper(nums,target-1);
    }
    //二分查找右边界位置
    private int helper(int[] nums,int target){
        int i=0,j=nums.length-1;
        while(i<=j){
            int m=(i+j)/2;
            //小于等于--当等于时候:左i,m-1 右m+1,j 中间m 因此i=m+1为右边界返回
            if(nums[m]<=target) i=m+1;
            else j=m-1;
        }
        //返回i
        return i;
    }
}



面试题53 - II. 0~n-1中缺失的数字

排序数组中的搜索问题,首先想到 二分法 解决。
时间O(logN) 空间O(1)
class Solution {
    public int missingNumber(int[] nums) {
        
        int i=0,j=nums.length-1;
        // 小于等于 形成闭区间 等于也可以表示位于同一位置
        while(i<=j){
            int m=(i+j)/2;
            //下标和对应值相等时候 说明缺失的数字在右半部分 
            if(nums[m]==m){
                i=m+1;
            }
            else{
                j=m-1;
            }
        }
        return i;
    }
}

法2:O(n)
class Solution {
    public int missingNumber(int[] nums) {
        for (int i = 0; i <nums.length ; i++) {
            if(nums[i]!=i) return i;
        }
        return nums.length;
    }
}

面试题54. 二叉搜索树的第k大节点
倒数第K个节点
法1:只需遍历得到倒数第k大节点即可  

class Solution {
    private int res, n;

    public int kthLargest(TreeNode root, int k) {
       n = k;
       dfs(root);
       return res;
    }

    public void dfs(TreeNode node) {
        if (node == null || n == 0) return;
        dfs(node.right);
        if (--n == 0) {
            res = node.val;
            return;
        }
        dfs(node.left);
    }
}


法2:全部遍历存储,耗费空间复杂度
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthLargest(TreeNode root, int k) {
        List<Integer> ans=new ArrayList<>();
         helper(root,ans);
         return ans.get(ans.size()-k);
    }
    private void helper(TreeNode node,List<Integer> ans){
        if(node==null) return ;
        helper(node.left,ans);
        ans.add(node.val);
        helper(node.right,ans);
    }
}



面试题55 - II. 平衡二叉树
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
       if(root==null) return true;
       return Math.abs(depth(root.left)-depth(root.right))<=1 && isBalanced(root.left) && isBalanced(root.right);
    }
    private int depth(TreeNode root){
        if(root==null) return 0;
         return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}


面试题56 - I. 数组中数字出现的次数
异或,时间O(N)空间O(1) 满足要求
class Solution {
    public int[] singleNumbers(int[] nums) {
        //用于将所有的数异或起来,异或开始值为0
        int k = 0;
        
        for(int num: nums) {
            k ^= num;
        }
        
        //获得k中最低位的1
        int mask = 1;

        //因为俩个数字,二进制,必然有一位不同
        //找到不相同的那一位,为了方便直接找最右面的不相同那位
        while((k & mask) == 0) {
            mask <<= 1;//移动掩码!
        }
        //定义返回结果值
        int a = 0;
        int b = 0;
        
        for(int num: nums) {
            //和不同那位异或,得到0的放一组,另外的一组,必然可以得出俩个不同的数。因为相同的俩俩数都会异或为0
            if((num & mask) == 0) {
                a ^= num;
            } else {
                b ^= num;
            }
        }
        
        return new int[]{a, b};
    }
}
 

HASHSET  时间O(N)空间复杂度为O(N)不满足题意
class Solution {
    public int[] singleNumbers(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for (int num : nums)
            if (!set.add(num))
                set.remove(num);
        
        return set.stream().mapToInt(Integer::intValue).toArray();
    }
}

 


面试题56 - II. 数组中数字出现的次数 II

方法1:位运算

https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/solution/mian-shi-ti-56-ii-shu-zu-zhong-shu-zi-chu-xian-d-4/
class Solution {
    public int singleNumber(int[] nums) {
        int[] counts = new int[32];
        for(int num : nums) {
            for(int j = 0; j < 32; j++) {
                counts[j] += num & 1;
                num >>>= 1;
            }
        }
        int res = 0, m = 3;
        for(int i = 0; i < 32; i++) {
            res <<= 1;
            res |= counts[31 - i] % m;
        }
        return res;
    }
}



HASH表做法1,不满足空间复杂度
class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i:nums){
            if(map.containsKey(i)){
                int temp = map.get(i);
                map.put(i, temp+1);
            }
            else
                map.put(i, 1);
        }
        for(int i : map.keySet()){
            if(map.get(i) == 1)
                return i;
        }
        return 0;
    }
}

hash表做法2,不满足空间复杂度
//有点坑,不加这个引用过不去
import java.util.Map.Entry;
class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        for (int num : nums) {
            Integer val = map.get(num);
            if (val == null) {
                map.put(num, 1);
            } else {
                map.put(num, val + 1);
            }
        }

        for (Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }

        return -1;
    }
}


面试题57. 和为s的两个数字

有序序列,使用双指针
class Solution {
    public int[] twoSum(int[] nums, int target) {
       int i=0,j=nums.length-1;
       while(i<j){
           int s = nums[i] + nums[j];
           if(s==target) return new int[]{nums[i],nums[j]};
           if(s<target) i++;
           if(s>=target) j--;
       }
       //没有找到结果
       return new int[0];
    }
}



面试题57 - II. 和为s的连续正数序列

https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/
class Solution {
    public int[][] findContinuousSequence(int target) {
      //滑动窗口题 常左闭右开
      //滑动窗口 先减去当前值 再移动边界值

      List<int[]> ans=new ArrayList<>();
      //从1开始 保证下标和数值一致
      int i=1,j=1,sum=0;
      while(i<=target/2){
         if(sum<target){
             sum+=j;
             j++;
         }
         else if(sum>target){
             sum-=i;
             i++;
         }
         else{
             int[] tmp=new int[j-i];
             //左闭右开
             for(int k=i;k<j;k++){
                tmp[k-i]=k;
             }
             ans.add(tmp);
             //处理边界,先减去当前值!再左边界右移!
            
             sum-=i;
              i++;
         }
      }
      return ans.toArray(new int[ans.size()][] );
    }
}



面试题58 - I. 翻转单词顺序
class Solution {
    public String reverseWords(String s) {
        String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
        StringBuilder res = new StringBuilder();
        for(int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
            if(strs[i].equals("")) continue; // 遇到空单词则跳过
            res.append(strs[i] + " "); // 将单词拼接至 StringBuilder
        }
        return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
    }
}


面试题59 - I. 滑动窗口的最大值
//给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。 
//
// 示例: 
//
// 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
//输出: [3,3,5,5,6,7] 
//解释: 
//
//  滑动窗口的位置                最大值
//---------------               -----
//[1  3  -1] -3  5  3  6  7       3
// 1 [3  -1  -3] 5  3  6  7       3
// 1  3 [-1  -3  5] 3  6  7       5
// 1  3  -1 [-3  5  3] 6  7       5
// 1  3  -1  -3 [5  3  6] 7       6
// 1  3  -1  -3  5 [3  6  7]      7 
//
// 
//
// 提示: 
//
// 你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。 
//
// 注意:本题与主站 239 题相同:https://leetcode-cn.com/problems/sliding-window-maximum/ 
// Related Topics 栈 Sliding Window


import sun.misc.Queue;

import java.util.LinkedList;

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
       if(nums==null|| k < 1 || nums.length < k){
           return new int[0];
       }

        //滑动窗口
        LinkedList<Integer> queue=new LinkedList<>();
        //结果数组
        int[]res=new int[nums.length-k+1];
         int index=0;
        for (int i = 0; i <nums.length ; i++) {
            //while循环保证小等于 的全部出队列  进行值的比较而不是下标
           while(!queue.isEmpty()&&nums[queue.peekLast()]<=nums[i]){
               queue.pollLast();
            }
            //存下标
            queue.addLast(i);
           //多余队头元素处理
            if(queue.peekFirst()==i-k){
                queue.poll();
            }
            //结果输出返回值而不是下标,保证形成窗口
            if(i+1>=k){
               res[index++]=nums[queue.peek()];
            }

        }
        return res;
    }
}
//leetcode submit region end(Prohibit modification and deletion)


面试题59 - II. 队列的最大值
class MaxQueue {
    Queue<Integer> queue;
    Deque<Integer> deque;
    public MaxQueue() {
       queue=new LinkedList<>();
       deque=new LinkedList<>();
    }

    //求最大值,只需返回deuque的队首
    public int max_value() {
      return deque.size()>0?deque.peek():-1;
    }

    //入queue,之后deque更新最大值
    public void push_back(int value) {
        queue.offer(value);
        //防止deque空指针
        while(deque.size()>0 && deque.peekLast()<value){
            deque.pollLast();//将deq队尾小于value的元素删掉
        }
        deque.addLast(value);
    }

    //出queue的首元素不一定是最大的
    public int pop_front() {
        int tmp=queue.size()>0?queue.poll():-1;  //获得队首元素保证队首不为空
        //如果出queue的时候,是最大需要把deque的值也弹出
        if(deque.size()>0 && deque.peek().equals(tmp)){
             deque.poll();
        }
        return tmp;
    }
}


面试题60. n个骰子的点数
class Solution {
    public double[] twoSum(int n) {
        //整体思路:各个点数出现的次数/总共点数出现的次数
         //i个骰子出现s点的次数
         int[][] dp=new int[n+1][6*n+1];
         //表示一个骰子掷出i点的次数为1
         for(int i=1;i<=6;i++){
                dp[1][i]=1;
         }

         //遍历n个骰子
         for(int i=2;i<=n;i++){
             //可能的总点数和
             for(int s=i;s<=6*i;s++){
                   //当前可投出的点数
                   for(int j=1;j<=6;j++){
                        
                         //i个骰子的总点数为s
                         //如果s比最后一个骰子的点数j和之前(i-1)个骰子的最小点数之和(i-1)的和(即j+(i-1))还要小
                         //那么这种情况不存在。
                         if(s<j+(i-1)) break;
                         dp[i][s] += dp[i-1][s-j];
                    }
             }          
         }

       double total = Math.pow((double)6,(double)n);//掷出n次点数出现的所有情况
       double[] ans = new double[5*n+1];//因为最大点数6n-最小点数n:--》出现6*n - n +1中点数
        for(int i=n;i<=6*n;i++){
            ans[i-n]=((double)dp[n][i])/total;//第i小的点数出现的概率
        }
        return ans;
 
    }
}


面试题61. 扑克牌中的顺子
class Solution {
    public boolean isStraight(int[] nums) {
      //构成顺子     ---   max-min<5 且 无重复值
      Set<Integer> set=new HashSet<>();
      int max=0;
      int min=14;
      for(int num:nums){
          //大小王时候跳过
        if(num==0) continue;
        //判断重复 :加入集合失败->表示已经重复有这个元素,返回false
         if(!set.add(num)) return false;
         
         //求最大,最小值赋值给max和min
         max=Math.max(max,num);
         min=Math.min(min,num);

      }
      return max-min<5;
    }
}



面试题62. 圆圈中最后剩下的数字
class Solution {
    public int lastRemaining(int n, int m) {
        int ans = 0;
        // 最后一轮剩下2个人,所以从2开始反推
        // i代表此时轮次
        for (int i = 2; i <= n; i++) {
            ans = (ans + m) % i;
        }
        return ans;
    }
}

 


 面试题63. 股票的最大利润
class Solution {
    public int maxProfit(int[] prices) {
      //数组当中最低的价格
      int cost=Integer.MAX_VALUE;
      int ans=0;
      for(int price :prices){
         cost=Math.min(cost,price);
         ans=Math.max(ans,price-cost);
      }
      return ans;
    }
}


剑指Offer 64

class Solution {
    int res = 0;
    public int sumNums(int n) {
        boolean x = n > 1 && sumNums(n - 1) > 0;
        res += n;
        return res;
    }
}


简洁写法
利用短路运算 n>1 时候才会执行后面的操作
class Solution {
    public int sumNums(int n) {
        boolean x = n > 1 && (n += sumNums(n - 1)) > 0;
        return n;
    }
}



面试题65. 不用加减乘除做加法
class Solution {
    public int add(int a, int b) {
        //进位为0出口
        if (b == 0) {
            return a;
        }
        
        // 转换成非进位和 + 进位
        return add(a ^ b, (a & b) << 1);
    }
}



面试题66. 构建乘积数组

规律
https://leetcode-cn.com/problems/gou-jian-cheng-ji-shu-zu-lcof/solution/mian-shi-ti-66-gou-jian-cheng-ji-shu-zu-biao-ge-fe/

class Solution {
    public int[] constructArr(int[] a) {
        if(a.length == 0) return new int[0];
        int[] b = new int[a.length];
        b[0] = 1;
        int tmp = 1;
        for(int i = 1; i < a.length; i++) {
            b[i] = b[i - 1] * a[i - 1];
        }
        for(int i = a.length - 2; i >= 0; i--) {
            tmp *= a[i + 1];
            b[i] *= tmp;
        }
        return b;
    }
}
 

面试题67. 把字符串转换成整数



class Solution {
    public int strToInt(String str) {
       char[] c=str.trim().toCharArray();
       if(c.length==0)  return 0;
       int sign=1;//符号位
       int i=1;//数字起始位,必须给符号位空一个
       int bndry = Integer.MAX_VALUE / 10;//定义边界
       int res=0;//数字结果值

       if(c[0]=='-') sign=-1;
       // else if 符号位只处理一次   :符号位不为 +号 此时遍历i从0开始  
       else if(c[0]!='+') i=0;
       //遍历处理数字
       for(int j=i;j<c.length;j++){
           //读取到字符
          if(c[j]<'0'||c[j]>'9') break;
          //边界处理 根据符号位正负返回对应的值
           if(res > bndry || res == bndry && c[j] > '7') return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
 
          res=res*10+ (c[j]-'0');
       }
       //res需要进行Int强制转换(int)res
       return sign * (int)res;
    }
}


面试题68 - I. 二叉搜索树的最近公共祖先

迭代

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
       //交换保证p小q大 节省后面的比较
        if(p.val>q.val){
          TreeNode t=p;
          p=q;
          q=t;
        }
        
        while(root!=null){
            //p.q均在当前root的右子树
           if(root.val<p.val){
              root = root.right; // 遍历至右 子节点 
           }
           //p.q均在当前root的左子树
           else if(root.val>q.val){
               root=root.left;//遍历至左 子节点
           }
           //p.q在当前root的不同侧 子树
           else{
               break;
           }
        }
        return root;
    }
}


面试题68 - II. 二叉树的最近公共祖先
递归

/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章