c++ 字典顺序生成全排列,蛮力算法时间复杂度 Θ(n*n!)

什么是字典顺序: 

                                        1,3,4...n    (不是)

                                        1,3,2,4...n (不是)

                                        1,2,3,4...n (是)

1. 我们先看下(按照字典顺序下一个最大排列是什么?)

    {1,2,3} 字典顺序全排列 {1,2,3}     {1,3,2}     {2,1,3}     {2,3,1}     {3,1,2}     {3,2,1}

            例1:从上面随机选择一个排列 {2,1,3}    字典顺序下一个最大排列    {2,3,1}

            例2:从上面随机选择一个排列 {3,1,2}    字典顺序下一个最大排列    {3,2,1}

            例3:从上面随机选择一个排列 {3,2,1}    字典顺序下一个最大排列    {3,2,1}(是它自身, 没有比它更大的

            例4:那  362541  呢?是不是要先全排列出来,然后再去找?(答案是NO)——PS数字越大  越高      

解:①  从右到左寻找第一个信号由(无或弱)到强突然转弱 的位置 ,也就是底下指向 2 的红色箭头所属的位置 

     ② 取  中大于  最小数,也就是指向 4 的红色箭头所属的位置,然后两个数交换位置 

     ③ 以左到右递增的形式对  进行排序 ,最终结果为 

visual Studio程序直接复制即可运行!

#include<iterator>
#include<iostream>

using namespace std;
template<class T> // 字典顺序生成下一个最大排列 // Θ(n)
void nextPermutation(T list[], int n)
{
	// 解:①
	int j = n - 1;
	while (list[j] > list[j + 1]) // n 次比较
	{
		if (j == 0)
		{
			copy(list, list + n + 1, ostream_iterator<T>(cout, " "));// 打印自身
			return; // 防止 j -= 1 "越位"
		}
		j -= 1;
	}
	// 解:②
	int k = n;
	while (list[j] > list[k]) // n-j 次比较,1<=j<n
		k -= 1;
	swap(list[j], list[k]);
	// 解:③
	int r = n, s = j + 1;
	while (r > s) // (n-j)/2+1 次比较,1<=j<n
	{
		swap(list[r], list[s]);
		r -= 1;
		s += 1;
	}
	// 打印
	copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
	cout << endl;
}
int main()
{
	int arr[6] = { 3,6,2,5,4,1 };
	nextPermutation(arr, 5);

	system("pause");
    return 0;
}

2.  刚刚是下一个, 那(  按照字典顺序上一个最大排列是什么?)

    {1,2,3} 字典顺序全排列 {1,2,3}     {1,3,2}     {2,1,3}     {2,3,1}     {3,1,2}     {3,2,1}

            例1:从上面随机选择一个排列 {2,1,3}    字典顺序上一个最大排列    {1,3,2}

         例2:从上面随机选择一个排列 {3,1,2}    字典顺序上一个最大排列    {2,3,1}

         和上面介绍的原理相同,只要把上面的操作反过来小小的修改即可,这里不在过多介绍,直接上代码

visual Studio程序直接复制即可运行!

#include<iterator>
#include<iostream>

using namespace std;
template<class T> // 字典顺序生成上一个最大排列 // Θ(n)
void previousPermutation(T list[], int n)
{
	int j = n - 1;
	while (list[j] < list[j + 1])
	{
		if (j == 0)
		{
			copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
			return;
		}
		j -= 1;
	}

	int k = n;
	while (list[j] < list[k])
		k -= 1;
	swap(list[j], list[k]);

	int r = n, s = j + 1;
	while (r > s)
	{
		swap(list[r], list[s]);
		r -= 1;
		s += 1;
	}

	copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
	cout << endl;
}
int main()
{
	int arr[6] = { 3,6,4,1,2,5 };
	previousPermutation(arr, 5);

	system("pause");
    return 0;
}

3.  终于进入全排列了 得意

     例1我们已经可以实现 集合下一个最大排列,比如  {1,2,3}    字典顺序下一个最大排列    {1,3,2},可是就只输出一个

               {1,2,3} 字典顺序全排列 {1,2,3}     {1,3,2}     {2,1,3}     {2,3,1}     {3,1,2}     {3,2,1}

   只要nextPermutation 不断的循环下去,  就可以不断的寻找下一个最大排列,其中必须给循环一个停止条件

            {1,2,3}全排列停止条件{3,2,1} ,    因为 {3,2,1}    字典顺序下一个最大排列    {3,2,1}(是它自身, 没有比它更大的)

         ③.1  期间遍历每个排列中的从右到左相邻两元素

          如果满足右到左寻找第一个 “ 信号由(无或弱)到强突然转弱  ” 的位置 也就是指向 2 的红色箭头所属的位置

          循环继续,一直运行到循环的停止条件

      ③.2  期间遍历每个排列中的从右到左相邻两元素,不满足第一个 “ 信号由(无或弱)到强突然转弱  ” 继续

                            

               如果满足右到左寻找第一个 “ 信号由(无或弱)到强突然转弱  ” 的位置 也就是指向 1 的红色箭头所属的位置

               循环继续,一直运行到循环的停止条件

    ④运行到 {3,2,1}   和 不满足第一个 “ 信号由(无或弱)到强突然转弱  ” ,表示全排列结束,退出程序

 visual Studio程序直接复制即可运行!

#include<iterator>
#include<iostream>

using namespace std;
template<class T> // 字典顺序生成全排列 // Θ(n*n!)
void dictionaryPermutation(T list[], int n)
{
	copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
	cout << endl;

	/*动态申请的内存空间要充足,不然会造成内存溢出*/
	T* arr = new T[n + 1];
	for (int i = 0; i <= n; i++)
		arr[i] = list[i];

	/*遍历每一个元素*/
	for (int j = n - 1; j >= 0; j--)
	{
		/*遍历到最大排列的时候结束*/
		while (list[j] < list[j + 1]) // n! 比较次数
		{
			nextPermutation(list, n); // n!*Θ(n) 比较次数
			j = n - 1;
		}
	}
	delete[] arr;
}
int main()
{
	int arr[3] = { 1,2,3 };
	dictionaryPermutation(arr, 2);

	system("pause");
    return 0;
}

    例2我们已经可以实现 集合上一个最大排列,比如  {3,2,1}    字典顺序上一个最大排列    {3,1,2},可是就只输出一个

               {3,2,1} 字典顺序全排列 {3,2,1}     {3,1,2}     {2,3,1}     {2,1,3}     {1,3,2}     {1,2,3}

               只要把 nextPermutation 替换成  previousPermutation,再做小小的修改

visual Studio程序直接复制即可运行!

#include<iterator>
#include<iostream>

using namespace std;
template<class T> // 字典顺序生成全排列 // Θ(n*n!)
void dictionaryPermutation(T list[], int n)
{
	copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
	cout << endl;

	/*动态申请的内存空间要充足,不然会造成内存溢出*/
	T* arr = new T[n + 1];
	for (int i = 0; i <= n; i++)
		arr[i] = list[i];

	/*遍历每一个元素*/
	for (int j = n - 1; j >= 0; j--)
	{
		/*遍历到最小排列的时候结束*/
		while (arr[j] > arr[j + 1])
		{
			previousPermutation(arr, n);
			j = n - 1;
		}
	}
	delete[] arr;
}
int main()
{
	int arr[3] = { 3,2,1 };
	dictionaryPermutation(arr, 2);

	system("pause");
    return 0;
}
例3 如果!!!
     {2,1,3}  字典顺序全排列  {2,1,3}     {2,3,1}     {3,1,2}     {3,2,1}     {1,3,2}     {1,2,3}

         输入:  {2,1,3}   nextPermutation   {2,3,1}     {3,1,2}     {3,2,1}    难过

                     {2,1,3}   previousPermutation   {1,2,3}     {1,3,2}       敲打   

                      说好的全排列呢?给我吃了?            

                      别急,  dictionaryPermutation 中把 nextPermutation previousPermutation,做一个简单组合即可

visual Studio程序直接复制即可运行!

#include<iterator>
#include<iostream>

using namespace std;
template<class T> // 字典顺序生成全排列 // Θ(n*n!)
void dictionaryPermutation(T list[], int n)
{
	copy(list, list + n + 1, ostream_iterator<T>(cout, " "));
	cout << endl;

	/*动态申请的内存空间要充足,不然会造成内存溢出*/
	T* arr = new T[n + 1];
	for (int i = 0; i <= n; i++)
		arr[i] = list[i];

	/*遍历每一个元素*/
	for (int j = n - 1; j >= 0; j--)
	{
		/*遍历到最大排列的时候结束*/
		while (list[j] < list[j + 1]) // n! 比较次数
		{
			nextPermutation(list, n); // n!*Θ(n) 比较次数
			j = n - 1;
		}
		/*遍历到最小排列的时候结束*/
		while (arr[j] > arr[j + 1])
		{
			previousPermutation(arr, n);
			j = n - 1;
		}
	}
	delete[] arr;
}
int main()
{
	int arr[3] = { 2,1,3 };
	dictionaryPermutation(arr, 2);

	system("pause");
    return 0;
}

到此结束

各位如有对此文章讲解的内容有任何想法建议的欢迎留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值