STL - next_permutation 详细原理剖析

2 篇文章 0 订阅

先来看一下啥是全排列

1. 数组的全排列
由题入坑

求给定数组的全排列。如:
输入: 1,2,3

输出:
        1 2 3 
        1 3 2
        2 1 3 
        2 3 1 
        3 2 1 
        3 1 2 

这里来一道算法题:

算法题:实现一个整型数组的全排列,
void perm(int list[], int k, int m)
参数说明:list,数组;k开始位置,m个数。
用递归算法实现代码如下:
void perm(int list[], int k, int m)
{
    if ( k==m ) 
    {
        copy(list,list+m,ostream_iterator<int>(cout," "));
        cout<<endl;

        return;
    }

    for (int i=k; i<m; i++) // 注意 i 的取值范围
    {   
        swap(list[k],list[i]);
        perm(list,k+1,m);
        swap(list[k],list[i]);
    }
}

int main()  
{   
    int List[] = {1,2,3} ;
    perm(List, 0, sizeof(List)/sizeof(int));

    return 0;  
}  

测试结果:
1.list是{1,2,3,4} 时,
        1 2 3 4
        1 2 4 3
        1 3 2 4
        1 3 4 2
        1 4 3 2
        1 4 2 3
        2 1 3 4
        2 1 4 3
        2 3 1 4
        2 3 4 1
        2 4 3 1
        2 4 1 3
        3 2 1 4
        3 2 4 1
        3 1 2 4
        3 1 4 2
        3 4 1 2
        3 4 2 1
        4 2 3 1
        4 2 1 3
        4 3 2 1
        4 3 1 2
        4 1 3 2
        4 1 2 3

2.list是{1,2,3} 时
        1 2 3
        1 3 2
        2 1 3
        2 3 1
        3 2 1
        3 1 2

2. next_permutation

明白什么是全排列后,再来看看next permutation这道题,递归的方法是较简单并且容易想到的,我们尝试非递归的解法。关于原理部分,基本上都是摘抄自《STL源码剖析》。

- 《STL源码剖析》: 在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。

看具体例子,一个排列为 124653,如何找到它的下一个排列。

第一步:找最后面1个数字的下一个全排列。

124653, 显然最后1个数字3不具有下一个全排列。

第二步:找最后面2个数字的下一个全排列。
1246 53, 显然最后2个数字53不具有下一个全排列。

第三步:找最后面3个数字的下一个全排列。
124653,显然最后3个数字 653 不具有下一个全排列。

插曲:到这里相信大家已经看出来,如果一个序列是递减的,那么它不具有下一个排列

第四步:找最后面4个数字的下一个全排列。
12 4653, 我们发现显然最后4个数字,4563 具有下一个全排列。因为它不是递减的,例如6453, 5643

总结上面的操作,并总结出重复上面操作的两种终止情况:

1:从后向前比较相邻的两个元素,直到前一个元素小于后一个元素,停止
2:如果已经没有了前一个元素,则说明这个排列是递减的,所以这个排列是没有下一个排列的。

我们找到了 这个 "i",接下来要从尾端寻找 j (*i < *j

12 4653, 从后向前找第一个比 “4”大的, 找到 “5”,将 这两个元素进行交换。得到: 12 5643

将第 ii 个元素之后(包括ii)的所有元素颠倒排序。此时 ii 指向的是 6

所以: 643 颠倒为: 346

so, 124653 的下一个序列为: 125346

代码:

void nextpermutation(vector<int> & num)
{
    if (num.size() <= 1)
        return;
    int i = (int)num.size() - 1;
    while (i > 0 && num[i] <= num[i-1])
        --i;
    if (i == 0)
    { //整个数组是倒叙排列的
        reverse(num.begin(), num.end());
        return;
    }
    --i;
    int j = (int)num.size() - 1;
    while (num[i] >= num[j])
        --j;
    swap(num[i], num[j]);
    reverse(num.begin() + i + 1, num.end());
}

int main()
{
    vector<int> vec = { 1, 2, 4, 6, 5, 3 };
    nextpermutation(vec);
    for (auto &i:vec)
        cout << i << "  ";;
    system("pause");
    return 0;
}

参考文献:
http://jishublog.iteye.com/blog/1945410
http://blog.csdn.net/qq575787460/article/details/41215475

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值