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

 

posted @ 2020-05-29 19:08  叫我妖道  阅读(221)  评论(0编辑  收藏  举报
~~加载中~~