LeetCode第77题Combinations从143ms击败1.27%优化到3ms击败100%

这个题目不是太难,主要记录一下自己从183ms击败1.3%提交的代码优化到3ms击败100%提交的代码的这个过程。希望能从中可以获取优化代码的思路。

77. Combinations

​Given two integers n and k, return all possible combinations of k numbers out of 1 … n.

Example:

input: n = 4, k = 2
Output:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

原题地址:https://leetcode.com/problems/combinations/description/

​在做leetCode题目77. Combinations 时,通过逐步的优化,将运行时间由180+ms缩短到3ms,从击败1.3%提交的代码,优化到击败100%。现在对这个过程进行一个总结:首先完成基本的功能,通过测试用例。

​通过所有测试用例的代码如下:

class Solution {
    public static List<List<Integer>> combine(int n, int k) {
        if(n<1||k<1||k>n){
            //注意
            return null;
        }
        int[] nums = new int[n];
        Arrays.setAll(nums, i -> i+1);
        return computeCombine(nums, k, new LinkedList<>());
    }

    public static List<List<Integer>> computeCombine(int[] nums, int k, LinkedList<Integer> stack){
        List<List<Integer>> result_list = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] > -1){
                stack.push(nums[i]);
                nums[i] = -1;
                if(k-1 == 0){//不需要再添加元素了
                    List<Integer> Arr_list = new ArrayList<>();
                    Arr_list.addAll(stack);
                    result_list.add(Arr_list);
                }else {
                    int[] nums2 = Arrays.copyOf(nums, nums.length);
                    result_list.addAll(computeCombine(nums2, k-1, stack));
                }
                stack.pop();
            }
        }
        return result_list;
    }
}

​上面代码的运行时间149ms,击败1.27%提交的代码。

然后对代码的时间复杂度分析,发现时间长的原因是搜索的范围太大,还有就是有Set集合的复制,有很多的存在性判断。第一次优化,针对上面的问题,改进了算法,缩小的搜索范围,不再需要存在性判断。提交的代码击败了37%的提交代码。

​第一次优化后的代码如下:

class Solution {
    public static List<List<Integer>> combine(int n, int k) {
        if(n<1||k<1||k>n){
            //注意
            return null;
        }
        List<List<Integer>> result_list = new ArrayList<>();
        computeCombine(n, k, 1, new LinkedList<>(), result_list);
        return result_list;
    }

    public static  void computeCombine(int n, int k, int start, LinkedList<Integer> stack, List<List<Integer>> result_list){
        for (int i = start; i <=n ; i++) {
            stack.push(i);
            if(k==1){
                result_list.add(new ArrayList<>(stack));
            }else {//递归调用
                computeCombine(n, k-1, i+1, stack, result_list);
            }
            stack.pop();
        }
    }
}

​ 第一次优化后,代码通过所有测试用例的时间为37ms,击败37.84%提交的代码。优化力度很大,缩小了搜索范围,并且不再需要元素是不是已经使用过的存在性判断。

​ 第二次优化,然后发现,还是有很多的搜索是无用的,这些搜索在一开始就注定不能搜索到合适的结果。通过剪枝策略,将这部分一开始就注定没有用的搜索分支过滤掉。提交代码后发现通过测试用例的时间缩短为3ms,击败了100%提交的代码。还是比较开心的,所以把这个过程记录下来,希望可以从中感受优化代码运行效率的过程。

​ 第二次优化后的代码如下:

class Solution {
    public static List<List<Integer>> combine(int n, int k) {
        if(n<1||k<1||k>n){
            //注意
            return null;
        }
        List<List<Integer>> result_list = new ArrayList<>();
        computeCombine(n, k, 1, new LinkedList<>(), result_list);
        return result_list;
    }

    public static  void computeCombine(int n, int k, int start, LinkedList<Integer> stack, List<List<Integer>> result_list){
        //对for循环的结束条件进行了优化,有"i <= n"变为" i <= (n-k+1)",过滤掉所有没有结果的搜索。
        for (int i = start; i <=(n-k+1) ; i++) {  //注意: 在这儿for的结束条件变为i <= (n-k+1)
            stack.push(i);
            if(k==1){
                result_list.add(new ArrayList<>(stack));
            }else {//递归调用
                computeCombine(n, k-1, i+1, stack, result_list);
            }
            stack.pop();
        }
    }
}

​第二次优化后,代码通过所有测试用例的时间为3ms,击败了100%提交的代码。也算是自己优化了一早上没有白费功夫。主要做的优化,就是将for循环的结束条件由” i <= n”改变为” i <= (n - k +1)”,可以过滤掉所有不能产生结果的搜索,这样就很大的提升了算法搜索效率。

​对这次整个的优化过程进行一个提炼,也方便以后参考。

​优化过程: 提炼: 分析时间复杂度-> 找到最耗时操作->改进算法->再分析时间复杂度-> 消除冗余运算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值