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. 二叉樹的最近公共祖先
遞歸

/

 

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