next permutation 的实现
参考博客:https://www.cnblogs.com/DWVictor/p/10301666.html
https://www.cnblogs.com/xingzhg/p/3927453.html
一,思路
观察全排列的第一个 12345 和 最后一个 54321 可以发现
全排列的第一个排列是升序的,最后一个排列是降序的,
这一点能得到什么 结论呢 ?(敲黑板,画重点)
当然是—— 只看 降序 部分的话,没有下一序列,也就是说—— 想要有下一序列的话,必须出现 升序对 ,不连在一起也还 OK 啦
今天我们就用这一点解决如何某一排列的下一个。
如何利用这一点呢 ?
给定排列:21354 ,如何得到下一排列呢 ?
① 因为是按字典序排序的,所以下一个是 比他大的最小值,所以我们先从后面观察,因为后面的数变了,大小差的小。
② 又因为由相同数字组成的,所以要得到下一个数,一定会经过数字的易位,所以我们要观察的不能是一个数,至少是两个数。
接下来就是重头戏了,( ‘-ωก̀ )
根据 ①② 两点及上面的推论,
我们先看 54,不行,是 降序 的,抬走下一个
我们再看 354,出现了 升序对了,可以,这时候怎么办呢?
不要慌,喝口药。蛇皮走位,观察战局,我们发现 改变绝对不可能只在 54 之间发生(这一点就是之前看 54 时候排除的)
所以我们找到了机会,就是 3 一定参与改变,这时候为了满足 ①,我们只要在 3 后面找到一个最小的比 3 大的数,两者易位,然后再把 后面的 降序 逆序 成 升序 就可以了
所以,找到 4,得到 453,逆序后面的数,得到 435,最后结果为 21435
此时,推广至任意序列:
给定任意排列: ABCDE (他们的分别代表 1,2,3,4,5,位置任意)
观察 DE,若 DE 升序,两者交换,得到结果。
若 DE 降序,待定,加下一个数字
观察 CDE ,若 CDE 升序 (不可能出现这种情况了,之前已经排除了)
若 CDE 既有升序,又有降序,则必有,CD 升序,DE 降序(因为 DE 已然 降序 )
所以此时,C 必改变(因为只有 DE 改变的,之前已经排除了),
所以在后面找一个比 C 大的最小的数,因为 CD 升序,DE 降序,所以从最后一位开始向前查找,第一个比 C 大的就是了。
然后将找到的数字与 C 交换,然后再把原先 C 位置后面的 降序 逆序 成 升序 就可以了,得到结果
若 CDE 降序,待定,加下一个数字
观察 BCDE,重复上述操作。。。。。。。
若最后 ABCDE 是 降序的,则没有下一个序列,返回 0,当然为了特定情况,可以认为 EDCBA 即第一个序列,是最后一个序列的下一个。
到此终了,其他一些细节可见代码的注释.
题目:
给出正整数n,则1 到n 这n 个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n = 3 时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1 六个排列。
任务描述: 给出某个排列,求出这个排列的下k 个排列,如果遇到最后一个排列,则下1 排列为第1个排列,即排列1 2 3…n。
比如:n = 3,k = 2 给出排列2 3 1,则它的下1 个排列为3 1 2,下2 个排列为3 2 1,因此答案为3 2 1。
#define _CRT_SECURE_NO_WARNINGS #include<string.h> #include<stdio.h> #include<stdlib.h> void swap(char *s1, char *s2) { char t = *s1; *s1 = *s2; *s2 = t; } void reverse(char *s, char* e) { for (e--; s < e; s++, e--) swap(s, e); } int next(char* begin, char* end) // begin 指向s[0].end 指向 '\0' { if (begin == end) // 如果没有元素 return 0; if (++begin == end) // 如果只有一个元素 return 0; begin--; char *i = end; i--; while (1) // i,j 从后面往前查找 查找第一对出现 升序 的相邻元素,则 i 后面肯定是 降序 的1 { char* j = i; // j 指向最后一个元素 i--; // i 在 j 前面 if (*i < *j) // i 代表右边都是降序的最小的下标值。 { char* k = end; while (*i >= *--k) // 在右边 找到第一个大于 i 的值, ; swap(i, k); // 交换两者的之值, reverse(j, end); // 逆序 右边的降序变成 升序 return 1; } if (i == begin) // 整体升序,所以是最后一个排列,所以下一个就是第一个排列,也就是 最后一个排列的逆序 { reverse(begin, end); return 0; } } } int main(void) { printf("请输入 n 和 k 的值:"); int n, k; char a[100]; scanf("%d%d", &n, &k); printf("请输入给定的序列:"); for (int i = 0; i < n; i++) { scanf(" %c", &a[i]); } for (int i = 0; i < k; i++) next(a, a + n); printf("该序列的下 %d 个是:", k); for (int i = 0; i < n; i++) { printf("%c ", a[i]); }puts(""); system("pause"); return 0; } /* 测试数据 3 1 3 2 1 */
========== ======== ======= ====== ===== ==== ==== === == =
stop ! stop it Ray ! Even if you are facing a bitter aspect of life , drugs and murder are foul without any excuse ,
deserve a red card for a loser . —— Conan
True , I was a cheater , a bad player and I lost the game . I must be adjudicative ,
for both my late wife and a fan like you . —— Ray