排列组合问题算法分析与C++实现Leetcode46

排列组合问题算法分析与C++实现

排列组合经常出现在编程问题中,如从集合选取组合或排列。Leetcode 46、77分别是排列、组合问题。

排列问题

  • 采用递归思路实现:对于全排列,每次从集合取出一个元素放在首位,然后对余下的集合实现全排列。
    C++实现
vector<vector<int>> PermutationAll(const list<int> &nums) {
  vector<vector<int>> ans;
  size_t size = nums.size();
  if (size == 1) {
    return {{nums.front()}};
  }
  if (size > 1) {
    for (auto it = nums.begin(); it != nums.end(); it++) {
      vector<int> tmp;
      tmp.push_back(*it);
      list<int> cp(nums);
      cp.erase(std::find(cp.begin(), cp.end(), *it));
      auto p = PermutationAll(cp);
      for (auto x : p) {
        vector<int> cpp(tmp);
        cpp.insert(cpp.end(), x.begin(), x.end());
        ans.push_back(cpp);
      }
    }
  }
  return ans;
}
  • 上面实现了全排列,那么部分元素的排列如何做,同样采用递归实现,代码略有不同:
vector<vector<int>> Permutation(const list<int> &nums, int size) {
  vector<vector<int>> ans;
  size_t n = nums.size();
  if (n < size) {
    throw std::runtime_error("2nd Para in Permutation() is error.");
  }
  if (size == 1) {
    for (auto x : nums) {
      vector<int> tmp{x};
      ans.push_back(tmp);
    }
    return ans;
  }
  if (size > 1) {
    for (auto it = nums.begin(); it != nums.end(); it++) {
      vector<int> tmp;
      tmp.push_back(*it);
      list<int> cp(nums);
      cp.erase(std::find(cp.begin(), cp.end(), *it));
      auto p = Permutation(cp, size - 1);
      for (auto x : p) {
        vector<int> cpp(tmp);
        cpp.insert(cpp.end(), x.begin(), x.end());
        //O(1) operation
        // cpp.splice(cpp.end(),x);
        ans.push_back(cpp);
      }
    }
  }
  return ans;
}
  • 完整测试代码
#include <algorithm>
#include <iostream>
#include <list>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::list;
using std::vector;

vector<vector<int>> PermutationAll(const list<int> &nums) {
  vector<vector<int>> ans;
  size_t size = nums.size();
  if (size == 1) {
    return {{nums.front()}};
  }
  if (size > 1) {
    for (auto it = nums.begin(); it != nums.end(); it++) {
      vector<int> tmp;
      tmp.push_back(*it);
      list<int> cp(nums);
      cp.erase(std::find(cp.begin(), cp.end(), *it));
      auto p = PermutationAll(cp);
      for (auto x : p) {
        vector<int> cpp(tmp); 
        cpp.insert(cpp.end(), x.begin(), x.end());
        // O(1) operation
        // cpp.splice(cpp.end(),x);
        ans.push_back(cpp);
      }
    }
  }
  return ans;
}

vector<vector<int>> Permutation(const list<int> &nums, int size) {
  vector<vector<int>> ans;
  size_t n = nums.size();
  if (n < size) {
    throw std::runtime_error("2nd Para in Permutation() is error.");
  }
  if (size == 1) {
    for (auto x : nums) {
      vector<int> tmp{x};
      ans.push_back(tmp);
    }
    return ans;
  }
  if (size > 1) {
    for (auto it = nums.begin(); it != nums.end(); it++) {
      vector<int> tmp;
      tmp.push_back(*it);
      list<int> cp(nums);
      cp.erase(std::find(cp.begin(), cp.end(), *it));
      auto p = Permutation(cp, size - 1);
      for (auto x : p) {
        vector<int> cpp(tmp);
        cpp.insert(cpp.end(), x.begin(), x.end());
        ans.push_back(cpp);
      }
    }
  }
  return ans;
}
bool IsEqual(const vector<int> &a, const vector<int> &b) {
  size_t na = a.size();
  size_t nb = b.size();
  if (na != nb) {
    return false;
  }
  size_t i = 0;
  while (i < na) {
    if (a[i] != b[i]) {
      return false;
    }
    i++;
  }
  return true;
}
int main() {
  list<int> nums{1, 2, 3, 4, 5};
  cout << "Enter your permutation size: ";
  int size = 0;
  cin >> size;
  cout << endl;
  auto x = Permutation(nums, size);
  auto y = PermutationAll(nums);
  cout << "Permutations number: " << x.size() << endl;
  cout << "PermutationAll number: " << y.size() << endl;
  int np = 1;
  size_t n = nums.size();
  int cnt = 0;
  while (cnt < size) {
    np *= n--;
    cnt++;
  }
  cout << "Check number Permutation: " << np << endl;
  cnt = 0;
  np = 1;
  while (cnt < n) {
    np *= n--;
    cnt++;
  }
  cout << "Permutation all: " << endl;
  std::for_each(y.begin(), y.end(), [](const vector<int> &k) {
    std::for_each(k.begin(), k.end(),
                  [](const int &d) { std::cout << d << " "; });
    cout << endl;
  });
  for (auto &b : x) {
    std::sort(b.begin(), b.end());
  }
  std::sort(x.begin(), x.end());
  //   auto unique_end = std::unique(x.begin(), x.end(), IsEqual);
  auto unique_end = std::unique(x.begin(), x.end());
  cout << "Combinations: " << endl;
  std::for_each(x.begin(), unique_end, [](const vector<int> &k) {
    std::for_each(k.begin(), k.end(),
                  [](const int &d) { std::cout << d << " "; });
    cout << endl;
  });
  return 0;
}

组合问题

  • 组合过程,思路分析:从n个元素选出m个元素,每次选出一个元素(如从前到后次序),然后从选出的元素位置后的部分求出选m-1个元素的组合,如此利用递归求解。
    完整测试代码:
#include <algorithm>
#include <iostream>
#include <vector>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::vector;
// TODO: use template
vector<vector<int>> Combination(const vector<int> &nums, int m,
                                vector<int>::const_iterator &begin,
                                vector<int>::const_iterator &end) {
  if (nums.empty()) {
    // TODO: which is better exception or error
    throw std::runtime_error("size of nums is zero.");
  }
  if (m > end - begin) {
    // throw std::runtime_error("combination's size is bigger than size of
    // nums.");
    cerr << "combination's size is bigger than size of nums." << endl;
  }
  vector<vector<int>> ans;
  if (m == 0) {
    ans = {nums};
  }
  if (m == 1) {
    for (auto x = begin; x != end; x++) {
      vector<int> tmp{*x};
      ans.push_back(tmp);
    }
  }
  if (m > 1) {
    for (auto it = begin; it != end - m + 1; it++) {
      vector<int> tmp;
      tmp.push_back(*it);
      auto nbegin = it + 1;
      auto p = Combination(nums, m - 1, nbegin, end);
      for (auto x : p) {
        vector<int> cpp(tmp);
        cpp.insert(cpp.end(), x.begin(), x.end());
        ans.push_back(cpp);
      }
    }
  }
  return ans;
}

int main() {
  vector<int> vn{1, 2, 3, 4, 5};
  cout << "Enter your combination size: ";
  int size = 0;
  cin >> size;
  auto begin = vn.cbegin();
  auto end = vn.cend();
  auto C = Combination(vn, size, begin, end);
  std::for_each(C.begin(), C.end(), [](const vector<int> &k) {
    std::for_each(k.begin(), k.end(),
                  [](const int &d) { std::cout << d << " "; });
    cout << endl;
  });
  return 0;
}

Leetcode 77题参考:

vector<vector<int>> combine(int n, int k) {
  vector<int> nums;
  for (int i = 1; i < n + 1; i++) {
    nums.push_back(i);
  }
  auto begin = nums.cbegin();
  auto end = nums.cend();
  return Combination(nums, k, begin, end);
}

另一种耗时更短的解法:

class Solution {
 private:
  vector<vector<int>> res;
  void dfs(int n, int k, int start, vector<int> &tmp) {
    if (tmp.size() == k) {
      res.push_back(tmp);
      return;
    }
    for (int i = start; i <= n; i++) {
      tmp.push_back(i);
      dfs(n, k, i + 1, tmp);
      tmp.pop_back();
    }
    return;
  }

 public:
  vector<vector<int>> combine(int n, int k) {
    if (n <= 0 && k <= 0 && k > n) return res;
    vector<int> tmp;
    dfs(n, k, 1, tmp);
    return res;
  }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值