LintCode筆記(Java)

持續更新。。。


1、A+B問題(2019年5月5日

描述:給出兩個整數 a 和 b , 求他們的和。

  • 你不需要從輸入流讀入數據,只需要根據aplusb的兩個參數a和b,計算他們的和並返回就行。
public int aplusb(int a, int b) {
    //a ^ b表示a和b相加之後,該進位的地方沒有進位的結果
    //a & b表示a和b相加之後,沒有進位的地方
    //a & b << 1 表示進位之後的結果
    //所以:a + b = (a ^ b) + (a & b << 1)
    //若a & b << 1等於0,則a ^ b爲輸出結果
    int _a=a^b;
    int _b=(a & b) << 1;
    return _b==0?_a:aplusb(_a,_b);
}

2、尾部的零

描述:設計一個算法,計算出n階乘中尾部零的個數。

樣例:

    輸入:  5
    輸出: 1  
    樣例解釋: 
    5! = 120, 結尾的0有1個。

挑戰:O(logN)的時間複雜度

public long trailingZeros(long n) {
    //0是由2*5產生的,而5的數量一定小於2的數量
    //因此5的個數決定了結尾0的個數
    //只要計算n的階乘中,5這個素因子出現多少次即可
    int count=0;
    while(n!=0){
        count+=n/5;
        n/=5;
    }
    return count;
}

3、統計數字

描述:計算數字 k 在 0 到 n 中的出現的次數,k 可能是 0~9 的一個值

樣例:

輸入:
k = 1, n = 12
輸出:
5
解釋:
在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 中,我們發現 1 出現了 5 次 (1, 10, 11, 12)(注意11中有兩個1)。

 public int digitCounts(int k, int n) {
        int count=0;
        for(int i=0;i<=n;i++){
            count+=singleCounts(k,i);
        }
        return count;
    }
    private int singleCounts(int k,int n){
        if(n==0&&k==0){
            return 1;
        }
        int cut=0;
        while(n!=0){
            if(n%10==k)cut++;
            n/=10;
        }
        return cut;
    }

4、醜數

描述:設計一個算法,找出只含素因子235 的第 n 小的數。符合條件的數如:1, 2, 3, 4, 5, 6, 8, 9, 10, 12...

  • 我們可以認爲 1 也是一個醜數

樣例:

輸入:9
輸出:10

挑戰:要求時間複雜度爲 O(nlogn) 或者 O(n)

public int nthUglyNumber(int n) {
        List<Integer> list=new ArrayList<>();
        list.add(1);
        int p2=0,p3=0,p5=0;
        for(int i=0;i<n;++i){
            int temp=list.get(i);
            while(list.get(p2)*2<=temp)p2++;
            while(list.get(p3)*3<=temp)p3++;
            while(list.get(p5)*5<=temp)p5++;
            
            list.add(Math.min(list.get(p2)*2,Math.min(list.get(p3)*3,list.get(p5)*5)));
        }
        return list.get(n-1);
    }

5、第K大的數(2019年6月16日

描述:在數組中找到第 k 大的元素。

  • 你可以交換數組中的元素的位置

樣例:

輸入:n = 1, nums = [1,3,4,2]
輸出:4

挑戰:要求時間複雜度爲O(n),空間複雜度爲O(1)。

    public int kthLargestElement(int n, int[] nums) {
        // write your code here
        //第n大的數,index爲nums.length-n
        //算法:快速排序的變形
        if(nums==null||nums.length==0||n<1||n>nums.length){
            return -1;
        }
        return kthLargestElement(nums.length-n,nums,0,nums.length-1);
        
    }
   private int kthLargestElement(int k,int[] nums,int start,int end){
       int left=start;
       int right=end;
       int base=nums[left];
       while(left<=right){
           while(left<=right&&base<nums[right]){
               right--;
           }
           while(left<=right&&base>nums[left]){
               left++;
           }
           if(left<=right){
               int temp=nums[left];
               nums[left]=nums[right];
               nums[right]=temp;
               left++;
               right--;
           }
       }
       if(k<=right){
           return kthLargestElement(k,nums,start,right);
       }
       if(k>=left){
           return kthLargestElement(k,nums,left,end);
       }
       return nums[k];
   }

6、合併排序數組 II

描述:合併兩個有序升序的整數數組A和B變成一個新的數組。新數組也要有序。

樣例:

輸入: A=[1,2,3,4], B=[2,4,5,6]
輸出: [1,2,2,3,4,4,5,6]    
樣例解釋: 返回合併後的數組。

挑戰:你能否優化你的算法,如果其中一個數組很大而另一個數組很小?

    public int[] mergeSortedArray(int[] A, int[] B) {
        // write your code here
        //算法:快速排序變形
        if(A==null||B==null){
            return null;
        }
        if(A.length>=B.length){
            return mergeSortedArray(A,B,new int[A.length+B.length]);
        }else{
            return mergeSortedArray(B,A,new int[A.length+B.length]);
        }
    }
    private int[] mergeSortedArray(int[] A,int[] B,int[] C){
        int a=0;//數組A的計數變量
        int b=0;//數組B的計數變量
        int c=0;//數組C的計數變量
        while(b<B.length){
            while(a<A.length&&A[a]<=B[b]){
                C[c++]=A[a++];
            }
            C[c++]=B[b++];
        }
        while(a<A.length){
            C[c++]=A[a++];
        }
        return C;
    }

7、二叉樹的序列化和反序列化(2019年6月17日

描述:設計一個算法,並編寫代碼來序列化和反序列化二叉樹。將樹寫入一個文件被稱爲“序列化”,讀取文件後重建同樣的二叉樹被稱爲“反序列化”。

如何反序列化或序列化二叉樹是沒有限制的,你只需要確保可以將二叉樹序列化爲一個字符串,並且可以將字符串反序列化爲原來的樹結構。

  • 對二進制樹進行反序列化或序列化的方式沒有限制,LintCode 將您的 serialize 輸出作爲 deserialize的輸入,它不會檢查序列化的結果。

樣例:

輸入:{3,9,20,#,#,15,7}
輸出:{3,9,20,#,#,15,7}
解釋:
二叉樹 {3,9,20,#,#,15,7},表示如下的樹結構:
      3
     / \
    9  20
      /  \
     15   7
它將被序列化爲 {3,9,20,#,#,15,7}

我們的數據是進行 BFS 遍歷得到的。當你測試結果 Wrong Answer 時,你可以作爲輸入調試你的代碼。

你可以採用其他的方法進行序列化和反序列化。

    public String serialize(TreeNode root) {
        // write your code here
        //二叉樹的序列化,即根據給定的root節點,依次寫入左節點L1,右節點R1;
        //而後依次寫入左節點L1對應的左右子節點,接着是右節點R1的左右子節點;
        //依次類推
        //容器:ArrayList
        if(root==null){
            return "{}";
        }
        ArrayList<TreeNode> list=new ArrayList<TreeNode>();
        list.add(root);
        for(int i=0;i<list.size();++i){
            if(list.get(i)==null){
                continue;
            }
            list.add(list.get(i).left);
            list.add(list.get(i).right);
        }
        while(list.get(list.size()-1)==null){
            list.remove(list.size()-1);
        }
        StringBuilder sb=new StringBuilder();
        sb.append('{');
        sb.append(root.val);
        for(int i=1;i<list.size();++i){
            TreeNode node=list.get(i);
            if(node==null){
                sb.append(",#");
            }else{
                sb.append(',');
                sb.append(node.val);
            }
        }
        sb.append('}');
        return sb.toString();
    }


    public TreeNode deserialize(String data) {
        // write your code here
        //二叉樹的反序列化,即解析給定的字符串,
        //將字符串中的每一個元素,轉化爲二叉樹對應的一個節點的值
        if(data.equals("{}")){
            return null;
        }
        String[] strVals=data.substring(1,data.length()-1).split(",");
        ArrayList<TreeNode> list=new ArrayList<TreeNode>();
        TreeNode root=new TreeNode(Integer.parseInt(strVals[0]));
        list.add(root);
        int index=0;
        boolean isleftnode=true;
        for(int i=1;i<strVals.length;++i){
            if(!strVals[i].equals("#")){
                TreeNode node=new TreeNode(Integer.parseInt(strVals[i]));
                if(isleftnode){
                    list.get(index).left=node;
                }else{
                    list.get(index).right=node;
                }
                list.add(node);
            }
            if(!isleftnode){
                index++;
            }
            isleftnode=!isleftnode;
        }
        return root;
    }

8、旋轉字符串(2019年6月20日

描述:給定一個字符串(以字符數組的形式給出)和一個偏移量,根據偏移量原地旋轉字符串(從左向右旋轉)

  • offset >= 0;str的長度 >= 0

樣例:

輸入:  str="abcdefg", offset = 3
輸出:  str = "efgabcd"    
樣例解釋:  注意是原地旋轉,即str旋轉後爲"efgabcd"

輸入: str="abcdefg", offset = 0
輸出: str = "abcdefg"    
樣例解釋: 注意是原地旋轉,即str旋轉後爲"abcdefg"

輸入: str="abcdefg", offset = 10
輸出: str = "efgabcd"    
樣例解釋: 注意是原地旋轉,即str旋轉後爲"efgabcd"

挑戰:在數組上原地旋轉,使用O(1)的額外空間

    public void rotateString(char[] str, int offset) {
        // write your code here
        if (str == null || str.length == 0)
            return;
            
        offset = offset % str.length;
        reverse(str, 0, str.length - offset - 1);
        reverse(str, str.length - offset, str.length - 1);
        reverse(str, 0, str.length - 1);
    }
    
    private void reverse(char[] str, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = str[i];
            str[i] = str[j];
            str[j] = temp;
        }
    }
    public void rotateString(char[] str, int offset) {
        // write your code her
        //挑戰:在數組上原地旋轉,使用O(1)的額外空間
        if(str.length!=0&&offset>=str.length){
            offset%=str.length;
        }
        if(offset==0||str.length==0){
            return;
        }
        rotateString(str,0,str.length-offset,offset);
        
    }

    private void rotateString(char[] str,int i,int j,int offset){
        for(;i<str.length-offset&&j<str.length;++i,++j){
            char temp=str[i];
            str[i]=str[j];
            str[j]=temp;
        }
        if(i<str.length-offset){
            rotateString(str,i,str.length-offset,offset);
        }
        if(j<str.length){
            rotateString(str,i,j,str.length-j);
        }
    }

9、Fizz Buzz 問題

描述:給你一個整數n. 從 1 到 n 按照下面的規則打印每個數:

  • 如果這個數被3整除,打印fizz.
  • 如果這個數被5整除,打印buzz.
  • 如果這個數能同時被35整除,打印fizz buzz.
  • 如果這個數既不能被 3 整除也不能被 5 整除,打印數字本身

樣例:

比如 n = 15, 返回一個字符串數組:

[
  "1", "2", "fizz",
  "4", "buzz", "fizz",
  "7", "8", "fizz",
  "buzz", "11", "fizz",
  "13", "14", "fizz buzz"
]

挑戰:你是否可以只用一個 if 來實現

    public List<String> fizzBuzz(int n) {
        // write your code here
        List<String> list=new ArrayList<String>();
        int i=1;
        while(i<=n){
            while(i<=n&&i%3!=0&&i%5!=0){
                list.add(i+"");
                i++;
            }
            while(i<=n&&i%3==0){
                if(i%5==0){
                    list.add("fizz buzz");
                }else{
                    list.add("fizz");
                }
                i++;
                break;
            }
            while(i<=n&&i%5==0){
                list.add("buzz");
                i++;
                break;
            }
        }
        return list;
    }

11、二叉查找樹中搜索區間

描述:給定一個二叉查找樹和範圍[k1, k2]。按照升序返回給定範圍內的節點值。

樣例:

輸入:{5},6,10
輸出:[]
        5
它將被序列化爲 {5}
沒有數字介於6和10之間

 

輸入:{20,8,22,4,12},10,22
輸出:[12,20,22]
解釋:
        20
       /  \
      8   22
     / \
    4   12
它將被序列化爲 {20,8,22,4,12}
[12,20,22]介於10和22之間

    public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) {
        ArrayList<Integer> results = new ArrayList<Integer>();
        helper(root, k1, k2);
        return results;
    }
    //從給定的BST的根節點開始查找,如果當前節點大於k1,就向左子樹搜索,
    //如果當前節點小於k2,就繼續向右子樹搜索。如果位於[k1,k2],存入結果。
    private void helper(TreeNode root, int k1, int k2) {
        if (root == null) {
            return;
        }
        if (root.val > k1) {
            helper(root.left, k1, k2);
        }
        if (root.val >= k1 && root.val <= k2) {
            results.add(root.val);
        }
        if (root.val < k2) {
            helper(root.right, k1, k2);
        }
    }
    public List<Integer> searchRange(TreeNode root, int k1, int k2) {
        // write your code here
        List<Integer> list=new ArrayList<Integer>();
        searchRange(root,list,k1,k2);
        return list;
    }
    //前序遍歷
    private void searchRange(TreeNode node,List<Integer> list,int k1,int k2){
        if(node==null){
            return;
        }
        if(k1<=node.val&&node.val<=k2){
            list.add(node.val);
        }
        searchRange(node.left,list,k1,k2);
        searchRange(node.right,list,k1,k2);
    }

12、帶最小值操作的棧

描述:實現一個棧, 支持以下操作:

  • push(val) 將 val 壓入棧
  • pop() 將棧頂元素彈出, 並返回這個彈出的元素
  • min() 返回棧中元素的最小值

要求 O(1) 開銷.

  • 保證棧中沒有數字時不會調用 min()

樣例:

輸入: 
  push(1)
  min()
  push(2)
  min()
  push(3)
  min()
輸出: 
  1
  1
  1

//空間複雜度仍然是 O(n)
public class MinStack {
    LinkedList<Integer> stack1;//棧1,push所有元素
    LinkedList<Integer> stack2;//棧2,push最小元素
    public MinStack() {
        // do intialization if necessary
        stack1=new LinkedList<>();
        stack2=new LinkedList<>();
    }

    /*
     * @param number: An integer
     * @return: nothing
     */
    public void push(int number) {
        // write your code here
        stack1.addFirst(number);
        if(stack2.size()==0){
            stack2.addFirst(number);
        }else{
            stack2.addFirst(Math.min(stack2.getFirst(),number));
        }
    }

    /*
     * @return: An integer
     */
    public int pop() {
        // write your code here
        stack2.removeFirst();
        return stack1.removeFirst();
    }

    /*
     * @return: An integer
     */
    public int min() {
        // write your code here
        return stack2.getFirst();
    }
}

13、字符串查找

描述:對於一個給定的 source 字符串和一個 target 字符串,你應該在 source 字符串中找出 target 字符串出現的第一個位置(從0開始)。如果不存在,則返回 -1

在面試中我是否需要實現KMP算法?

  • 不需要,當這種問題出現在面試中時,面試官很可能只是想要測試一下你的基礎應用能力。當然你需要先跟面試官確認清楚要怎麼實現這個題。

樣例:

輸入: source = "source" , target = "target"
輸出:-1    
樣例解釋: 如果source裏沒有包含target的內容,返回-1

 

輸入: source = "abcdabcdefg" ,target = "bcd"
輸出: 1    
樣例解釋: 如果source裏包含target的內容,返回target在source裏第一次出現的位置

挑戰:O(n2)的算法是可以接受的。如果你能用O(n)的算法做出來那更加好。(提示:KMP)

    public int strStr(String source, String target) {
         if (source == null || target == null) {
            return -1;
        }
        
        for (int i = 0; i < source.length() - target.length() + 1; i++) {
            int j = 0;
            for (j = 0; j < target.length(); j++) {
                if (source.charAt(i + j) != target.charAt(j)) {
                    break;
                }
            }
            // finished loop, target found
            if (j == target.length()) {
                return i;
            }
        }
        return -1;
    }
    public int strStr(String source, String target) {
        // Write your code here
        if(source==null||target==null)return -1;
        char[] s=source.toCharArray();
        char[] t=target.toCharArray();
        if(t.length==0)return 0;
        for(int i=0;i<=s.length-t.length;i++){
            int j=0;
            for(j=0;j<t.length;j++){
                if(s[j+i]!=t[j]){
                    break;
                }
            }
            if(j==t.length){
                return i;
            }
        }
        return -1;
    }

14、二分查找(2019年6月21日

描述:給定一個排序的整數數組(升序)和一個要查找的整數target,用O(logn)的時間查找到target第一次出現的下標(從0開始),如果target不存在於數組中,返回-1

樣例:

樣例  1:
    輸入:[1,4,4,5,7,7,8,9,9,10],1
    輸出: 0
    
    樣例解釋: 
    第一次出現在第0個位置。

樣例 2:
    輸入: [1, 2, 3, 3, 4, 5, 10],3
    輸出: 2
    
    樣例解釋: 
    第一次出現在第2個位置
    
樣例 3:
    輸入: [1, 2, 3, 3, 4, 5, 10],6
    輸出: -1
    
    樣例解釋: 
    沒有出現過6, 返回-1

挑戰:如果數組中的整數個數超過了2^32,你的算法是否會出錯?

    public int binarySearch(int[] nums, int target) {
        // write your code here
       if(nums==null) return -1;
       int s=0,e=nums.length-1;
       int mid=0;
       while(s<e){
           mid=(e-s)/2+s;
           if(nums[mid]>target){
               e=mid-1;
           }else if(nums[mid]<target){
               s=mid+1;
           }else{
               e=mid;
           }
       }
       if(nums[s]==target){
           return s;
       }
       return -1;
    }

15、全排列(2019年6月21日

描述:給定一個數字列表,返回其所有可能的排列。

樣例:

樣例1:

輸入:[1]
輸出:
[
  [1]
]

樣例2:

輸入:[1,2,3]
輸出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

挑戰:使用遞歸和非遞歸分別解決。

遞歸解答

    public List<List<Integer>> permute(int[] nums) {
        // write your code here
        List<List<Integer>> resultList=new ArrayList();
        if(nums==null||nums.length==0){
            resultList.add(new ArrayList());
            return resultList; 
        }
        rank(nums,new ArrayList(nums.length),resultList);
        return resultList;
        
    }
    private static void rank(int[] nums,List<Integer> tempList,List<List<Integer>> resultList){
        if(nums.length==0){//一次排列完成
            resultList.add(new ArrayList(tempList));
        }
        for(int i=0;i<nums.length;i++){
            tempList.add(nums[i]);
            int[] newNums=new int[nums.length-1];//移除已經被排列的那個數
            System.arraycopy(nums,0,newNums,0,i);
            System.arraycopy(nums,i+1,newNums,i,nums.length-i-1);
            rank(newNums,tempList,resultList);//遞歸排列下一個數
        }
        //將最後一個數移除,否則會影響循環過程
        if(tempList.size()!=0){
            tempList.remove(tempList.size()-1);
        }
    }

 

 

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