日常刷题 Leetcode 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],
]
分析题意:
其实就是让我们实现一个数学公式,C(n,k),从n个数中取k个数,返回所有的组合。这题一看,就肯定要有个遍历的行为,然后向下进行k次递归遍历,每次遍历都应该从上一次遍历的后一个位置进行遍历,当遍历层次达到k次后,即保存并返回。用经典的递归解法即可,下面是我的代码。
实现代码:

class Solution {
private:
    void adjust(int i,int n,int k,vector<int>&temp,vector<vector<int>>&result){
        if(n-i+temp.size()<k)//如果已经遍历的元素数量加上剩余所有元素的数量都不足K,应该及时结束,节省资源。
            return;
        if(temp.size()==k){//遍历数量达到K个后即装入result,返回。
            result.push_back(temp);
            return;
        }

        for(int j=i+1;j<n+1;++j){//每一次遍历都从上一次遍历位置的下一位置开始。
            temp.push_back(j);//装车
            adjust(j,n,k,temp,result);//递归
            temp.pop_back();//复原
        }
        return;
    }
public:
    vector<vector<int>> combine(int n, int k) {
        if(k>n || k==0)
            return {};
        vector<vector<int>>result;
        vector<int>temp;
        adjust(0,n,k,temp,result);//i的初始值取0
        return result;
    }
};

代码风格:
首先分析原题给的主干函数,只有n和k两个参数,若我们想返回二维数组,就必须在每层中维持一个一维数组temp,该数组装满k个后就该装入二维数组result,因此至少temp需要层层向下传递,所以主干函数并不能直接用于递归,必须另编写一个辅助函数作为递归体。而且就算这题修改一下,主干函数可以做递归体,其vector<vector<int>>类型的返回值也极大限制了其函数返回时的性能,那时为了性能的考虑也必须另行编写一个辅助函数做递归体。遇到递归类的题,先判断函数主体能否用做递归体是必要的,如果可行并且对性能影响不大,还是只用一个函数递归自身的好,看着简洁,否则最好另建递归体。然后对于递归体中的数组类型,能用引用类型的都要用引用类型,这也是STL编程思想中所要求的,引用可以避免数组的复制,在数据量大的时候节省的时间是惊人的。所以result和temp我都设为了引用类型,我在别人的博客里看到有把temp写为值传递的,以为在递归中每层的temp保存的信息是不一样的,所以用引用会发生错误,其实这是没有对程序的运行逻辑把握到位,
temp.push_back(j);//装车
adjust(j,n,k,temp,result);//递归
temp.pop_back();//复原
这三行代码就已经保证了每次下层递归返回的时候,temp是复原的了,此时j++之后,再调用adjust,向下传递的temp其实保存的是全新的信息,即使不是值传递,也保证数据不会有错乱,因此该用引用传递提高性能。
另外下面这行代码,我见别人的解法大都没有添加。
if(n-i+temp.size() < k)//如果已经遍历的元素数量加上剩余所有元素的数量都不足K,应该及时结束,节省资源。
return;
effective c++中说过,优化措施永远不嫌多,只要我们不是贸然行动。本行代码在n和k足够大的情况下,可以减少相当多的循环次数,比如当k=999,n=1000的时候,假如这时adjust(i,n,k,temp,result)中i=3,这就保证接下来绝不可能满足条件,但是若没有这行代码,程序还是会一直递归998*997*……*2*1次直到最后无功而返,这是多么大的浪费啊,因此在实现了代码的主体框架后,对其进行优化是必要的,因为在数据量很大的情况下,一个微不足道的优化都会有巨大的收益。STL中,如果两次if可以并做一次if,它都会牺牲一点自己源码的可读性取换取这一优化,因为源码作者深知STL这样频繁调用的核心算法中的优化可获得的收益有多大。我们虽然在自己的日常编程中没必要,可能也做不到向STL那样的精益求精,但是在自己力所能及的范围里,对自己编写的代码做最大的优化,是每个程序员应尽的职责,即便只是一道leetcode.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值