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);
        }
    }

 

 

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