
int a, b;
// scanf 返回值为变量的个数,如果没有返回 -1,EOF 是一个预定义的常量 -1
while (scanf("%d %d", &a, &b) != EOF) {
// ...
}int a, b;
while (cin >> a >> b) {
// ...
}// 示例 1
int a, b;
while (scanf("%d %d", &a, &b) != EOF && (a != 0 && b != 0)) {
// ...
}
// 或者
while (scanf("%d %d", &a, &b) != EOF && (a || b)) {
// ...
}
// 示例 2
int n;
while (scanf("%d", &n) != EOF && n != 0) {
// ...
}// 示例 1
int a, b;
while (cin >> a >> b) {
if (a == 0 && b == 0)
break;
// ...
}
// 示例 2
int n;
while (cin >> n && n != 0) {
// ...
}int n;
scanf("%d", &n);
int a, b;
for (int i = 0; i < n; i++) {
scanf("%d %d", &a, &b);
// ...
}int n;
cin >> n;
int a, b;
while(n--) {
cin >> a >> b;
}n = int(input())
for _ in range(n):
# ...int n;
while (cin >> n && n != 0) {
int a, b;
for (int i = 0; i < n; i++) {
cin >> a >> b;
// ...
}
}const int MAXN = 1000;
char buff[MAXN];
// C
gets(buff);
puts(buff); // 输出
// C++
cin.getline(buff, MAXN); // 第三个参数默认是 '\n'
cin.getline(buff, MAXN, '\n');string s;
getline(cin, s); // 第三个参数默认是 '\n'
getline(cin, s, '\n');int n;
cin >> n;
cin.get(); // 否则,n 也会计入下面的 getline(),导致少一组数据
while (n--) {
string s;
getline(cin, s);
}FILE *cfin = fopen("in.txt", "r");
FILE *cfout = fopen("out.txt", "w");
int a, b;
// 注意要传入文件指针
while (fscanf(cfin, "%d %d", &a, &b) != EOF) { // 类似的,把 scanf 替换成 fscanf
fprintf(cfout, "%d\n", a + b); // 把 printf 替换为 fprintf
}
fclose(cfin);
fclose(cfout);ifstream fin("in.txt");
ofstream fout("out.txt");
int a, b;
while (fin >> a >> b) {
fout << a + b << endl;
}
fin.close();
fout.close();LeetCode - 31. 下一个排列
题目描述
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1思路
从后往前找需要改变的 hi,即元素的位置
从后往前找需要交换的 lo,即 nums[hi] 的位置
交换 nums[lo] 与 nums[hi]
反转
1 5 8 5 7 6 4 3 1
↓ ↓ ↓ ↓ ↓
1 5 8 5 1 3 4 6 7
↑ ↑
hi lo (hi 位置不变)C++
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return;
int hi = n - 2;
// 1. 从后往前找需要改变的**高位** hi,即第一个降序元素的位置
while (hi >= 0 && nums[hi + 1] <= nums[hi])
hi--;
if (hi >= 0) {
// 2. 从后往前找需要交换的**低位** lo,即第一个大于 nums[hi] 的位置
int lo = n - 1;
while (lo >= 0 && nums[lo] <= nums[hi])
lo--;
// 3. 交换 nums[lo] 与 nums[hi]
swap(nums[hi], nums[lo]);
}
// 4. 反转 hi 之后的序列,即 nums[hi+1: n)
reverse(nums.begin() + hi + 1, nums.end());
// 当 i == -1 时,该操作会使序列从字典序最大转为最小,这与 STL 中提供的 next_permutation 略有不同
}
};LintCode - 51. 上一个排列
问题描述
给定一个整数数组来表示排列,找出其上一个排列。
排列中可能包含重复的整数
样例
给出排列[1,3,2,3],其上一个排列是[1,2,3,3]
给出排列[1,2,3,4],其上一个排列是[4,3,2,1]思路
C++
class Solution {
public:
/*
* @param nums: A list of integers
* @return: A list of integers that's previous permuation
*/
vector<int> previousPermuation(vector<int> &nums) {
int n = nums.size();
if (n <= 1) return nums;
int hi = n - 2;
// 1. 从右往左找**第一个升序**的位置 hi
while (hi >= 0 && nums[hi] <= nums[hi + 1])
hi--;
if (hi >= 0) {
int lo = n - 1;
// 2. 从右往左找**第一个小于** nums[hi] 的位置 lo
while (lo >= 0 && nums[lo] >= nums[hi])
lo--;
// 3. 交换 nums[lo] 和 nums[hi]
swap(nums[lo], nums[hi]);
}
// 4. 反转 hi 之后的位置
reverse(nums.begin() + hi + 1, nums.end());
return nums; // 注意这里要你返回一个值
}
};STL 提供了两个函数用于生成排列
这两个函数均以比较函数 为基础生成下一个或上一个排列
因此在使用这两个函数前,需要先对原序列进行
C++
LeetCode - 60. 第k个排列
问题描述
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是 [1, n!]。
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"思路
因为字典序的性质,实际不需要求出前 k-1 个序列
整体思路有点像
以 为例,找出其中第 个序列
C++
class Solution {
public:
string getPermutation(int n, int k) {
// nums: {1, 2, 3, ..., n}
// 换成其他字符,按字典序存放到对应位置即可
vector<int> nums(n + 1, 0);
for (int i = 0; i < n; i++) // 注意:桶的下标是从 0 开始的
nums[i] = i + 1;
// dp: {0!=1, 1!, 2!, ..., n!}
vector<int> dp(n + 1, 1); // 根据上面的推导,dp[0]=1 正好可以处理最后一轮
for (int i = 1; i <= n; i++)
dp[i] = dp[i - 1] * i;
k--;
stringstream ss;
for (int i = 1; i <= n; i++) { // 从 1 开始
int index = k / dp[n - i]; // 实际上没有用到 dp[n] = n!
ss << nums[index];
nums.erase(nums.begin() + index); // 注意,每轮删除已处理的元素
k = k % dp[n - i];
}
return ss.str();
}
};LeetCode 46. 全排列
题目描述
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]思路 1
思路 2
n! 种不同的排列;class Solution {
vector<vector<int> > ret;
vector<int> tmp;
vector<bool> used;
int n = 0;
void dfs(vector<int>& nums, int step) {
if (tmp.size() == n) {
ret.push_back(tmp);
return;
}
for (int i = 0; i < n; i++) { // i 每次从 0 开始,因为元素可以重复使用
if (used[i]) continue; // 但是在每一轮中,如果用过了需要跳过
// 每一轮指的是生成一次排列的过程
used[i] = 1; // 标记使用
tmp.push_back(nums[i]);
dfs(nums, step + 1);
tmp.pop_back(); // 回溯
used[i] = 0;
}
}
public:
vector<vector<int> > permute(vector<int>& nums) {
n = nums.size();
used.resize(n, 0);
dfs(nums, 0);
return ret;
}
};class Solution {
vector<vector<int> > ret;
//void dfs(vector<int> nums, int step) { // 值传递
void dfs(vector<int>& nums, int step) { // 引用传递
if (step >= nums.size()) {
ret.push_back(nums);
return;
}
for (int i = step; i < nums.size(); i++) { // 注意:这里 i 从 step 开始
swap(nums[step], nums[i]);
dfs(nums, step + 1);
swap(nums[step], nums[i]); // 如果 nums 是值传入,则不需要这步;否则不能省略
}
}
public:
vector<vector<int> > permute(vector<int>& nums) {
dfs(nums, 0);
return ret;
}
};LeetCode - 47. 全排列 II
题目描述
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]思路 1
思路 2
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
vector<bool> used;
int n = 0;
void dfs(vector<int>& nums, int step) {
if (tmp.size() == n) {
ret.push_back(tmp);
return;
}
for (int i = 0; i < n; i++) {
if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]))
continue; // 这里的 !used[i - 1] 稍难理解,可以配合 IDE 或者手推一下整个过程
used[i] = 1;
tmp.push_back(nums[i]);
dfs(nums, step + 1);
tmp.pop_back();
used[i] = 0;
}
}
public:
vector<vector<int> > permuteUnique(vector<int>& nums) {
n = nums.size();
used.resize(n, 0);
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
};class Solution {
vector<vector<int> > ret;
//void dfs(vector<int>& nums, int step) { // 传引用无法得出正确结果
void dfs(vector<int> nums, int step) { // 注意这里应该使用**值传递**
int n = nums.size();
if (step >= n - 1) {
ret.push_back(nums);
return;
}
for (int i = step; i < n; i++) {
if (i != step && nums[i] == nums[step])
continue;
swap(nums[i], nums[step]);
dfs(nums, step + 1);
//swap(nums[i], nums[step]); // 传引用配合回溯无法得出正确结果,
// 原因在于此时会破坏剩余数组的有序性
}
}
public:
vector<vector<int> > permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
};n! 个,所以算法的时间复杂度至少为 O(N!)LeetCode - 77. 组合
问题描述
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp; // 保存中间结果
int K;
void dfs(vector<int>& nums, int step) {
if (tmp.size() >= K) {
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
tmp.push_back(nums[i]); // nums[i] == i,所以这里直接 push(i) 也可以
dfs(nums, i + 1);
tmp.pop_back();
}
}
public:
vector<vector<int> > combine(int n, int k) {
K = k;
vector<int> nums;
for (int i = 0; i < n; i++)
nums.push_back(i + 1);
dfs(nums, 0);
return ret;
}
};(未验证)
LeetCode - 39. 组合总和
思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int target = 0;
void dfs(vector<int>& nums, int step) {
if (cur >= target) {
if (cur == target)
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
cur += nums[i];
tmp.push_back(nums[i]);
dfs(nums, i); // 因为每个数组可以重复使用,所以是 dfs(i) 而不是 dfs(i+1)
cur -= nums[i];
tmp.pop_back();
}
}
public:
vector<vector<int> > combinationSum(vector<int>& candidates, int target) {
this->target = target;
//sort(candidates.begin(), candidates.end()); // 不需要
dfs(candidates, 0);
return ret;
}
};LeetCode - 40. 组合总和 II
思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int target;
void dfs(vector<int>& nums, int step) {
if (cur >= target) {
if (cur == target)
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
if (i > step && nums[i] == nums[i - 1]) // 代码说明 1
continue;
cur += nums[i];
tmp.push_back(nums[i]);
dfs(nums, i + 1); // i+1 而不是 i,因为不能重复使用
tmp.pop_back();
cur -= nums[i];
}
}
public:
vector<vector<int> > combinationSum2(vector<int>& candidates, int target) {
this->target = target;
sort(candidates.begin(), candidates.end()); // 因为存在重复,需要先排序
dfs(candidates, 0);
return ret;
}
};代码说明 1
if (i > step && nums[i] == nums[i - 1])i == step 的情况i > step 的情况LeetCode - 216. 组合总和 III
问题描述
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int K;
void dfs(int n, int step) {
if (cur >= n) {
if (cur == n && tmp.size() == K) // 同时满足才加入结果集
ret.push_back(tmp);
return;
}
for (int i = step; i <= 9; i++) {
cur += i;
tmp.push_back(i);
dfs(n, i + 1); // 因为数字不能重复使用,所以是 dfs(i+1)
tmp.pop_back();
cur -= i;
}
}
public:
vector<vector<int> > combinationSum3(int k, int n) {
K = k;
dfs(n, 1);
return ret;
}
};高位与低位
对序列中任意两个位置而言,靠近左侧的为,靠近右侧的为低位
生成排列的过程就是不断增大,减小的过程
for(i=0;..) 与 for(i=step;..) 的说明for(i=0;..)used
for(i=step;..)
简单来说,以 为例
dfs(step+1)、dfs(i+1)、dfs(i) 的说明(以下均为个人小结,并没有严格验证)
dfs(step+1) 和 dfs(i+1)简单来说, 指的是生成 序列中的第 个位置; 指的是使用 中的第 个元素
以不重复集合 为例说明:
dfs(i+1) 和 dfs(i)在问题中,还用到了
for (int i = step; i < nums.size(); i++) {
// ...
dfs(nums, i);
// ...
}i 而不是 stepdfs(i) 而不是 dfs(i+1)