算法概论week1 | LeetCode 78. Subsets

LeetCode 78. Subsets

暑假我在leetcode上已经做了200多道题,虽然其中大部分是简单题,但是还是有一些题目令我做到脱发,因此,我想挑几道我暑期收藏的题目,记录下解题过程。


目录:

  • 问题描述
  • 解法一
  • 解法二

问题描述

求子集


解法一

这道题我们并不陌生,就是求一个给定集合的子集。通常,我们是根据元素个数来求子集的,即首先找出元素个数为0的子集,接着找出所有元素个数为1的子集,以此类推,直到找到元素个数与给定集合相等的集合。

解法一便是根据这个思路写出的。

        /*首先,空集是所有集合的子集,因此可以直接在结果中加入空集:*/
        vector<vector<int>> res;
        res.push_back({});

        for(int i = 1; i <= nums.size(); i++) {
            /*每次循环,找出所有元素个数为n的子集*/
            int n = i;

            /*用队列q来装载这次循环得到的子集*/
            queue<vector<int>> q;
            /*由于n>=1,因此可以先找出所有元素个数为1的子集,
            再在此基础上添加其他元素*/
            for(int j = 0; j < nums.size(); j++) {
                q.push({nums[j]});
            }

            /*当q不为空,即还未找全元素个数为n的子集时,
            依次给q中的每个集合添加一个可能的新元素*/
            while(!q.empty()) {
                int size = q.size();
                while(size--) {
                    vector<int> temp = q.front();
                    q.pop();
                    if(temp.size() != n) {
                        int k = 0;
                        //找到temp最后一个元素在nums中的位置
                        for(; k < nums.size(); k++) {
                            if(nums[k] == temp[temp.size() - 1]) {
                                break;
                            }
                        }
                        //从剩余元素中挑选元素,依次与temp组成新子集
                        if(nums.size() - k - 1 >= n - temp.size()) {
                            k++;
                            for(; k < nums.size(); k++) {
                                vector<int> temp2 = temp;
                                temp2.push_back(nums[k]);
                                if(temp2.size() == n) res.push_back(temp2);
                                else q.push(temp2);
                            }
                        }
                    }
                    /*当n为1时,可以直接将此子集加入结果*/
                    else res.push_back(temp);
                }
            }
        }

        return res;

解法二

第一种解法虽然思路简单,但是复杂度挺高的,嵌套了4层循环,而且队列也容易造成空间复杂度过高。换一种思路,我们可以发现,当我们已经求出了集合A的子集,而集合B只比集合A多一个数,那么,集合B的子集就等于集合A的每个子集加上多出来的这个数,再并上集合A的子集,例如:

已知集合 [ 1 ] 的子集为{ }, {1},那么集合 [1,2] 的子集就是集合 [ 1 ] 的每个子集加上2:{2}, {1,2},再并上集合 [ 1 ] 的子集:{ }, {1},即{ }, {1},{2}, {1,2};
同理,集合 [1,2,3] 的子集就是{ { 3}, {1,3},{2,3}, {1,2,3} }∪{ { }, {1},{2}, {1,2} } = { { }, {1},{2}, {1,2}, {3}, {1,3},{2,3}, {1,2,3} }

根据这种思路,我们可以写出解法二:

        vector<vector<int>> res;
        /*每次循环可以求出nums前i+1项的子集*/
        for(int i = 0; i < nums.size(); i++) {
            if(i == 0) res.push_back({});
            int size = res.size();
            //给当前的每个子集加上一个数
            for(int j = 0; j < size; j++) {
                res.push_back(res[j]);
                res.back().push_back(nums[i]);
            }
        }
        return res;

解法二不仅代码量比解法一少了很多,复杂度也下降了,不过当给定数组比较长时,leetcode会超出输出限制,因此没能测试更多用例。不过,这两个算法都能通过leetcode当前的测试样例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值