全排列算法
暴力破解
时间复杂度:O(n*n!) 【n个数的全排列有n!种,每一个排列都有n个数据】
对一个数组进行全排列。
可以看成当前这个a[0]-a[i]固定,对后面进行全排列,就完成了这个a[0]-a[i]的全部情况
去重复:a[m]-a[i]之间出现过的数据,不再进行交换了,已经交换过
//在 str 数组中,[m,i) 中是否有与 a[i] 元素相同的
bool IsSwap(int a[], int m, int i)
{
for (; m < i; m++)
{
if (a[m] == a[i])
return false;
}
return true;
}
//数组,长度,当前的位往后
void Permutation(int a[],int len, int m)
{
//跳出递归,到最后一个了
if (m == len)
{
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
else
{
for (int i = m; i < len; i++)
{
//交换,递归,换回
//判断交不交换,m-i的部分出现过,就不用了
if (IsSwap(a,m,i))
{
swap(a[m], a[i]);
//排后面的所有可能
Permutation(a, len, m + 1);
//换回去
swap(a[i], a[m]);
}
}
}
}
字典序
时间复杂度:O(m*n)
【求一个排列的下一个排列的时间复杂度,我们可以看到是O(n)级别的。而这里要变换m次,所以总时间复杂度是O(n * m)级别的】
【数组开始应该是单调递增的】
- 在原排列中从后往前找,找到第一个比它后面数小的数。(a[pos] < a[pos + 1])。简而言之就是找到原排列的最长单调递减的后缀的前一个数。
- 在原排列的最长单调递减的后缀中从前往后找到最后一个大于a[pos]的数a[k]。
- 调换a[pos]和a[k]。
- 对a[pos+1]……a[n]进行转置,此时的排列就是原排列的下一个排列。
//二分搜索
int binary_sort(int a[], int start, int end,int num)
{
int min = (start + end) / 2;
if (a[min] == num) return min;
if (a[min] < num)
binary_sort(a, start, min,num);
else
binary_sort(a, min + 1, end,num);
//没找到
return -1;
}
//字典序列
/*
1、从最后开始找,找到连续的递减序列的前一个,第一个a[pos]<a[pos+1].
2、该序列从前到后遍历,找到最后一个比a[pos]大的数a[k]
3、swap(a[k],a[pos])
4、该序列进行置换 a[pos]~a[end]
*/
//数组,长度,当前的位往后
bool Permutation(int a[],int len)
{
//找到最后一个,全递减就是做完了
int pos = len - 2;
for (; pos >=0; pos--)
{
if (a[pos] < a[pos + 1])
break;
}
//全递减,做完了
if (pos == -1) return false;
//序列中找第一个比a[pos]小的前一个,交换
int i = pos+1;
for ( ; i < len; i++)
{
//这个i就是了
if (a[pos] > a[i])
break;
}
//交换
swap(a[pos], a[i-1]);
//进行一个置换
for (int j = pos + 1; j <= (len+pos)/2; j++)
{
swap(a[j], a[len+pos-j]);
}
return true;
}
//main函数里面的调用
do{
//输出数组内容
}while(Permutation(a,len));
利用STL的next_permutation()
void stl_Permutation(int a[],int n){
//保存原来的数组
int b[n];
for(int i=0;i<n;i++){
b[i]=a[i];
}
//先利用stl的函数进行正序排序
sort(b,b+n);
do{
cout<<b[0];
for(int i=1;i<n;i++) cout<<" "<<b[i];
cout<<endl;
}while(next_permutation(b,b+n));
}
输出第M个序列
int i=1;
do{
if(i==m)
{
//输出数组内容
}
i++;
}while(Permutation(a,len));
该序列是第几个/第几N序列是什么
问题一:问一个数属于字典序中的第几个排列?
一个排列
p
1
p
2
p
3
…
p
n
−
1
p_1p_2p_3…p_{n-1}
p1p2p3…pn−1对应一个序数:
a
1
a
2
…
a
n
−
1
a_1a_2…a_{n−1}
a1a2…an−1
a
i
a_i
ai表示
p
i
p_i
pi的右边比
p
i
p_i
pi小的数字的个数。
在字典序中,排序为
p
1
p
2
p
3
…
p
n
−
1
的
序
数
d
:
p_1p_2p_3…p_{n-1}的序数d:
p1p2p3…pn−1的序数d:
d
=
a
1
∗
(
n
−
1
)
!
+
a
2
∗
(
n
−
2
)
!
+
.
.
.
+
a
n
−
i
∗
(
n
−
(
n
−
i
)
)
!
+
.
.
.
+
a
n
−
1
∗
1
!
d=a_1*(n-1)!+a_2*(n-2)!+...+a_{n-i}*(n-(n-i))!+...+a_{n-1}*1!
d=a1∗(n−1)!+a2∗(n−2)!+...+an−i∗(n−(n−i))!+...+an−1∗1!
注 意 : d = 0 , 1 , 2 , … , n ! − 1 注意:d=0,1,2,…,n!−1 注意:d=0,1,2,…,n!−1
eg:有一个序列为123456,问431256是字典排序的第几个序列
d = 3 ∗ 5 ! + 2 ∗ 4 ! + 0 ∗ 3 ! + 0 ∗ 2 ! + 0 ∗ 1 ! = 3 ∗ 120 + 2 ∗ 24 = 408 d =3*5!+2*4!+0*3!+0*2!+0*1!\\=3*120+2*24=408 d=3∗5!+2∗4!+0∗3!+0∗2!+0∗1!=3∗120+2∗24=408
408是从0开始的,第几个数列是从1开始的,所以结果是409
问题二:问字典序中的第几个排列是多少?
eg: 假设一个字典序排列由1到4组成,问第21个排列是多少?(排名从0开始数,当然也可以从1开始)
- 该字典排序的种类有:n=4!=432*1=24种
- 第一位数为1:
最小排序:1234
d = 0 ∗ 3 ! + 0 ∗ 2 ! + 0 ∗ 1 = 0 d=0*3!+0*2!+0*1=0 d=0∗3!+0∗2!+0∗1=0
最大排序:1432
d = 0 ∗ 3 ! + 2 ∗ 2 ! + 1 ∗ 1 ! = 5 d=0*3!+2*2!+1*1!=5 d=0∗3!+2∗2!+1∗1!=5
- 第二位数为2的距离:1*3! ~ 2*3!-1 = 6 ~ 11
第三位数为3的距离 :2*3! ~ 3*3!-1=12 ~ 17
第四位数为4的距离:3*3! ~ 4*3!-1 = 18~23
所有:0~23 :24种 - 确定第一位为4,开始查找下一位,与查找第一位类似
4123: d=33!+02!+0*1!=18
21-18=3=1*2!+1*1!
则序列为:4231
数组之间的排列遍历输出
- vector<vector>strArr:存储二维数组
- vectorvalue:存储strArr中一维数组的长度
- vectorNUM(value.size(), 0):记录当前遍历到的位置
//遍历的全排序输出函数
void Arrange(vector<vector<string>>strArr, vector<int>value)
{
//定义一个数组,并初始化为0
vector<int>NUM(value.size(), 0);
int n = 1;
int i = 0;
while (i < value.size())
{
n *= value[i];
i++;
}
cout << n << endl;
int j = 0;
while (n > 0)
{
i = 0;
//一次遍历输出
while (i < strArr.size())
{
cout << strArr[i][NUM[i]] << " ";
i++;
}
//修改定位
j = 0;
NUM[j]++;//NUM[0]
//刚好到临界值
while (NUM[j] == value[j] && j < strArr.size() - 1)
{
//修改值
NUM[j] = 0;
//后一个增加一
NUM[j + 1]++;
//看后一个是否改变
j++;
}
cout << endl;
n--;
}
}
相关的字符串切割内容
- 重要的函数:
-
getline(cin,s)
类似cin.getlin(char*,len)函数,这里读取后的类型为string -
stringstream ss(s);
定义一个stringstream类,用string类型初始化它 -
getline(ss, line, ’ ‘)
把stringstream类的一串数据按空格’ ’ 分割,并存放在string类型的line中。
-
#include<string>
#include<sstream>//stringstream ss(s);
void inputString()
{
string s;
vector <string> str;
vector <vector<string>> strArr;
string line;
int num=0,i=0;
vector<int>value;
cin >> num;
getline(cin, s);//先把数字的那一个读取掉
while (i<num)
{
getline(cin, s);
//把string s转为stringstream类型,可以遍历
stringstream ss(s);
while (getline(ss, line, ' '))//按空格分割
{
if (!line.empty())
str.push_back(line);
}
strArr.push_back(str);
value.push_back((int)str.size());
//cout << value[i] << " ";
str.clear();
i++;
}
Arrange(strArr, value);
}