NowCoder(3):从数组全排列到求和——好未来2017校招真题

1.数组全排列(Leetcode)

给定一个含有不同数字的集合,返回所有可能的全排列。比如,[1,2,3] 具有如下排列:

[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

1.1思路

进行深度搜索,组合出所有的可能排列方式。在每次进行搜索的时候,通过交换的方法把当前要压栈的元素挪到前面。这样每次进行递归的时候数组后面全部都是没有使用到的数字
注意这个题目中所有的数字都是不同的,所以不用考虑两个数组排列出来相同的情况。

1.2代码

class Solution {
    //这个题都是不同的数字,就没有考虑去除重复的情况,在牛客网上有一个题目需要去除重复的情况。
public:
    vector<vector<int>> res;
    vector<int> temp;
    vector<vector<int>> permute(vector<int>& nums) {

        permute(nums, nums.size(), 0);
        return res;
    }

    void permute(vector<int>& nums, int n, int index) {

        if (index == n - 1) {
            //已经到了最后一个元素了,不需要再交换了,直接放入temp中,再放入res中
            temp.push_back(nums[index]);
            res.push_back(temp);
            temp.pop_back();//推出这个值,然后遍历下一种情况
        }
        else
            //index表示当前正在考虑的元素的下标
            for (int i = index; i < n;i++) {
                swap(nums[index], nums[i]);//先进行交换,然后递归
                temp.push_back(nums[index]);
                permute(nums, n, index + 1);
                swap(nums[index], nums[i]);//交换回去,还原容器,方便下次使用
                temp.pop_back();//推出这个值,考虑下一个情况
            }
    }       
    void swap(int &a, int &b)
    {
        int t;
        t = a;
        a = b;
        b = t;
    }
};

2.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

2.1思路

和上面一个题思路相同 ”’分别以任何一个字符为第一个字符,然后进行递归,在操作的时候可以先将当前选中的第一个字符与实际的第一个字符进行交换”’
但是在这个题中会出现字母重复的情况,因此为了避免输出结果重复,需要对重复情况进行考虑。

2.2代码

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        '''分别以任何一个字符为第一个字符,然后进行递归,在操作的时候可以先将当前选中的第一个字符与实际的第一个字符进行交换'''
        if len(ss) == 0:
            return []
        s = ''
        result = []
        arr = list(ss)
        self.Per(arr, s, result)
        return result
    def Per(self, arr, s, result):
        if len(arr) == 1:
            s += str(arr[0])
            result.append(s)
        for i in range(0, len(arr)):
            repetition = 0#判断当前字母是否为重复字母
            if i > 0:#对于当前的元素的下一个元素开始,检查是否有重复
                for j in range(0, i):
                    if arr[j] == arr[i]:
                        repetition = 1#如果有重复
                        break;
                if repetition == 0:#如果没有重复就交换
                    t = arr[i]
                    arr[i] = arr[0]
                    arr[0] = t
            if repetition == 1:#如果出现重复就不考虑该元素,这里如果同样进行了递归操作,会导致有重复结果
                repetition = 0
                continue
            self.Per(arr[1:], s + str(arr[0]), result)#如果没有重复,进行递归

2.3扩展

如果把第一个题目的红色字体去掉,即如果不能保证数组中的数字没有重复的存在,我们可以用相同的思路来解决

class Solution {
public:
    vector<vector<int>> res;
    vector<int> temp;
    vector<vector<int>> permute(vector<int>& nums) {
        sort(nums.begin(), nums.end());//进行一下排序,这个是为了后面交换的时候排除重复的
        permute(nums, nums.size(), 0);
        return res;
    }
    void permute(vector<int>& nums, int n, int index) {
        if (index == n - 1) {
            //已经到了最后一个元素了,不需要再交换了,直接放入temp中,再放入res中
            temp.push_back(nums[index]);
            res.push_back(temp);
            temp.pop_back();
            return;
        }
        //index表示当前正在考虑的元素的下标
        for (int i = index; i < n;i++) {
            //设置i>index,首先要保证数组不越界。其次,当i = index且重复的时候,进行交换无所谓。
            //当i!=index且重复的时候,说明前面已经进行过相同的交换了,如果再进行交换就会重复,因此不考虑了。
            if (i > index && nums[i] == nums[i - 1])
                continue;   
            swap(nums[index], nums[i]);//先进行交换,然后递归
            temp.push_back(nums[index]);
            permute(nums, n, index + 1);
            swap(nums[index], nums[i]);//交换回去
            temp.pop_back();
        }
    }
    void swap(int &a, int &b)
    {
        int t;
        t = a;
        a = b;
        b = t;
    }
};
int main()
{
    vector<int> nums;
    //测试数据
    nums.push_back(1);
    nums.push_back(2);
    nums.push_back(4);
    nums.push_back(3);
    Solution s;
    s.permute(nums);
    for (int i = 0; i < s.res.size(); i++)
    {
        for (int j = 0; j < s.res[i].size();j++)
            cout << s.res[i][j] << " ";
        cout << endl;
    }
    system("pause");
}

3.求和

输入两个整数 n 和 m,从数列1,2,3…….n 中随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来
输入:

每个测试输入包含2个整数,n和m

输出:

按每个组合的字典序排列输出,每行输出一种组合

3.1思路

模仿前面全排列的思路,进行深度的搜索

3.2代码

#include<iostream>
#include<algorithm>
#include <vector>
using namespace std;
vector<vector<int> > res;
vector<int> temp;
void recursion(vector<int> nums, int n, int m, int index, int total);
int main()
{
    int n, m;
    cin >> n >> m;
    vector<int> nums;
    for (int i = 0; i <= n; i++)
        nums.push_back(i);
    recursion(nums, n, m, 1, 0);
    for (int i = 0; i < res.size(); i++)
    {
        for (int j = 0; j < res[i].size() - 1; j++)
            cout << res[i][j] << " ";
        cout << res[i][res[i].size() - 1] << endl;
    }
}
void recursion(vector<int> nums, int n, int m, int index, int total)//index表示当前正在考察哪个元素,total是当前和
{
    if (total == m)//如果当前的和已经等于m,把temp临时数组push进结果中并且return;
    {
        res.push_back(temp);
        return;
    }
    if (index == n+1) {//如果当前已经没有数据可以考虑了,那么直接return;
        return;
    }
    else//total比m小,还可以往后面继续找
        for (int i = index; i <= n; i++) {//从index开始考虑

            if (total + nums[i] > m)//total的下一个加起来就已经超出了m了,说明这种情况下往后面找已经没有意义了,return
                return;
            temp.push_back(nums[i]);//如果没有超出,就把当前的i压入栈
            recursion(nums, n, m, i + 1, total + nums[i]);//进行递归,从当前位置的下一个位置开始考虑
            temp.pop_back();//把当前的值出栈,然后考察下一个值
        }
}

3.3扩展

如果把题目的要求更改为输出所有可能的组合的数目而不需要输出具体的值的话,可以使用动态规划

设置数组dp[n+1][m+1],dp[i][j]表示输入为i,j时所有可能的组合的数目。
很容易知道dp[i][0]=1.
对于一个dp[i][j],这个值可以分为两部分,一个部分是dp[i-1][j],即组成部分不包括数字i的一部分;另一部分是dp[i-1][j-i]即组成部分包括数字i,那么从1到i-1只需要找出和为j-i的可能性。显然j-i>=0时第二种情况才成立。

所以有如下代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int> > dp(n + 1, vector<int>(m + 1, 0));//声明一个n+1 m+1的动态数组,初值为0
    for (int i = 0;i < n + 1;i++)//dp[i][0] = 1
        dp[i][0] = 1;
    for (int i = 1;i < n + 1;i++)
        for (int j = 1;j < m + 1;j++)
        {
            dp[i][j] = dp[i - 1][j];//动态规划的过程,第一部分
            if (j >= i)//第二部分
                dp[i][j] += dp[i - 1][j - i];
        }
    cout<< dp[n][m];
    system("pause");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值