#include <iostream>
#include <bits/stdc++.h>
using namespace std;
/*
递归打印全排列
方法1:next_permuation()
方法2:多重循环
方法3:递归
第一层:4个
1 2 3 4
2 1 3 4
3 2 1 4
4 2 3 1
第二层:3个
1 2 3 4 为例
1 2 3 4
1 3 2 4
1 4 3 2
最后4*3*2*1 得到全排列
*/
int num = 0;//用来计数
//dep代表深度 也代表从哪里开始置换 end-1代表有多少层 从0层开始计算
void Perm(vector<int> data, int dep, int end)
{
if (dep == end){
num++;
for (int i = 0; i <= end; i++)
cout << data[i]<< " ";
cout << endl;
}
else {
for (int i = dep; i <= end; i++) {
swap(data[dep], data[i]);
Perm(data,dep+1,end);
swap(data[dep], data[i]);
}
}
}
int main() {
int i;
cin >> i;
vector<int> data;
for (int k = 0; k < i; k++)
{
int num;
cin >> num;
data.push_back(num);
}
Perm(data,0,i-1);
return 0;
}
今天重新温习递归,重点在于掌握思想 层的概念,所有层执行相同操作,最后设置终止条件,类似于树一样,举个例子 :
1234 ->
1234 2134 3214 4231->
1234 1324 1432 。。。
。。。
当递归到最终层数时候打印即可。4*3*2*1中可能
这时候我们可能想到如何得到n个数挑m个数的全排列 很简单减少递归层数即可,
当我们调用递归第一层时候 有四种情况 第一位各不相同 第二层调用时候 第一位和第二位互不相同
所以 当你想选m个数全排列的时候只需要递归m层输出前m个数即可,以下是代码修改和注释:
void Perm(vector<int> data, int dep, int end,int m)
{
if (dep == m){//m从0开始计数 m=1 代表一层
num++;
for (int i = 0; i <= m-1; i++)//因为传递了m层 只有前m位互不相同 从0开始计数 所以是m-1
cout << data[i]<< " ";
cout << endl;
}
else {
for (int i = dep; i <= end; i++) {
swap(data[dep], data[i]);
Perm(data,dep+1,end);
swap(data[dep], data[i]);
}
}
}
打印CNM:
原理就是n个数每个元素设为0/1 总共有2^n种可能性
找1的个数为m的即可 得到对应十进制数获得数组下标
操作:
找1:kk=kk&(kk-1)会将最低位1变0 右侧0变1 与原来的kk相与 去掉最低位1 循环次数为1的个数
获得当前排列:
例如我们已经得到1101 怎末获得排列:
每个二进制位对应一个元素 共n位
遍历每一位
for(int j=0;j<n;j++)
(1<<j)&当前2进制 j位是1 会获得1 输出j就是对应的元素下标
代码如下:
#include <bits/stdc++.h>
using namespace std;
/*
打印n个元素 m个元素的子集C n m
前置知识:
位操作符
按位与 (&):
示例:result = a & b; // 按位与操作
按位或 (|):。
示例:result = a | b; // 按位或操作
按位异或 (^):
示例:result = a ^ b; // 按位异或操作
按位取反 (~):
示例:result = ~a; // 按位取反操作
左移 (<<):
用于将位向左移动,并在右侧补零。
示例:result = a << 1; // 左移一位
右移 (>>):
用于将位向右移动。
示例:result = a >> 1; // 右移一位
接下来我举例说明
1 2 3 的子集 2^3个 每个元素可以当作0 1
所以求m个元素的集合 就是找 元素排列中 1的个数为m的个数
*/
void print_set(int n, int m)//求n个元素 m个元素的子集
{
//遍历 2^n中可能
for (int i = 0; i <= (1 << n); i++)//(1<<n)代表1左移n位代表2^n
{
int num = 0;//对1的位置进行计数
int kk = i;
//关键求1的个数操作:kk=kk&(kk-1) 直到kk=0
while (kk)
{
kk = kk & (kk - 1);
num++;
}
if (num == m)//说明1的个数为m正确
{
//得到对应的1 共有n位
for (int j = 0; j < n; ++j)
{
if ((1 << j) & i)//对应位置的1判断
cout << j<<" ";
}
cout << endl;
}
}
}
int main() {
print_set(4, 2);
return 0;
}
关键在于掌握 :
(kk-1)&kk
(1<<n)