next-permutation原理及及运用

next-permutation是STL中的一个模板函数,它的功能是按默认operator<生成指向容器的下一个词典排列,即全排列中的下一个词典序列。词典序列呢,举个例子,一看就明白了:
1 2 3的全排列如下:
1 2 3 , 1 3 2 , 2 1 3 , 2 3 1 , 3 1 2 , 3 2 1
一:STL中的next-permutation原型:
template<class Bidit>
bool next-permutation(BidIt first, BidIt last);//default
template<class Bidit,class Pred>
bool next-permutation(BidIt first, BidIt last,Pred pr);//用pr代替operator<
调用方式:
next-permutation(vec.begin(),vec.end());
二:实现原理:
大概分为:
1:从右往左找到第一个比右边小的数,记录其位置为m;
2:再从右往左找到比m位置大的数中最小的一个,记录其位置为n(有点绕。。。下面会举例子)
3:然后交换m,n位置的数
4:对m之后的位置,进行升序排列
最后便可以得到某一个序列的下一个词典序列啦;
Example:
vector<int>vec{1,5,3,6,4,2};
1:按照上述步骤1即找到了‘3’,记录其下标:m=2
2:根据上步骤2找到了‘4’,记录其下标:n=4
3:根据步骤3,交换vec[m],vec[n],即得到了{1,5,4,6,3,2}
4:对下标为m=2之后的位置进行排序(4之后)得到:1,5,4,2,3,6 这便是1,5,3,6,4,2的下一个词典序列。
三:算法实现:
(看懂原理就行了哈,形参和上面的不一样,只是用vector作为形参习惯了~,改下就行)
这是只是找下个排列的算法,要找全排列,大家想看的话,代码在我的GitHub里边哈,大家可以给下,给个star是最好不过啦。。
https://github.com/yinjun12/AbsoluteSortedSequence/tree/master
下面是实现代码:
void nextPermutation(vector<int>& nums) {
int size = nums.size();
int i = size - 1, j = size - 1;
for (; i >= 0; i--)
{
if (i != size - 1 && nums[i]<nums[i + 1])//步骤1,找到用i记录
break;
}
if (i == -1) sort(nums.begin(), nums.end());
else
{
int cnt = i + 1;
for (; j>i; j--)
{
if (nums[j]>nums[i] && nums[j] <= nums[cnt])//步骤2,用j记录
cnt = j;
}
swap(nums[cnt], nums[i]);//步骤3
sort(nums.begin() + i + 1, nums.end());//步骤4
}
}
四:应用
全排列在其它一些算法中是很有用的哦,有全排列可以将时间复杂度降低很大哦(所以说STL是真的厉害。。。。)
下面给大家举两个常用的例子:
1:对某一个序列,求它的子集,子集必须满足和等于某一个定值。
vector<int>vec{10,20,30,40,50,60},求它所有的子集,使其和等于60;
可以用递归实现,不过时间复杂度比较高,代码是我的GitHub里,今天不给大家在这里说了
https://github.com/yinjun12/Combination-Sum/blob/master/algorithm
下面说下如何用next-permutation来实现:
就以上面的例子说明:
我们可以用个数相等的布尔向量表示{0,0,0,1,1,1}上述序列,其中1代表有这个元素;也就是说从6个数据中取1个是否等于60?{0,0,0,0,0,1},取2个是否等于60?{0,0,0,0,1,1}……从6个数据取6个是否等于60{1,1,1,1,1,1}?
因此转化为从布尔向量{0,0,0,0,0,1}到{1,1,1,1,1,1},并求出和看是否等于定值。
这种方法比上述用递归实现时间复杂小很多
代码实现:
bool ValidOrder(const vector<int>&v, const vector<int>&vec, int total)//判断向量对应的子集的和是否等于total
{
int sum = 0;
for (int i = 0; i < v.size(); i++)
{
if (v[i] == 1)
sum = sum + vec[i];
}
return sum == total;
}
int main
{
vector<int>vec{ 10, 20, 30, 40, 50, 60 };
vector<int>v(vec.size());
for (int i = v.size()-1; i>=0 ; i--)
{
fill(v.begin(), v.begin() + i, 0);//将v从{0,0,0,0,0,1}到{1,1,1,1,1,1}
fill(v.begin() + i, v.end(), 1);
do
{
if (ValidOrder(v, vec, 60))
{
for_each(v.begin(), v.end(), [](int i){cout << i << " "; });
cout << endl;
}
} while (next_permutation(v.begin(), v.end()));//对其中一个向量求下一个排列,相当于对vec求下一个子集
}
}
2:第二个是八皇后的问题,八皇后就不描述了,直接给上代码:
bool EightApress(const vector<int> &v)//用来判断是否在对角线上
{
for (int i = 0; i < v.size() - 1; i++)
{
for (int j = i + 1; j < v.size(); j++)
{
if (j-i==abs(v[i]-v[j]))
return false;
}
}
return true;
}
int main()
{
vector<int>v{ 0, 1, 2, 3, 4, 5, 6, 7 };
do
{
if (EightApress(v))
{
for_each(v.begin(), v.end(), [](int i){cout << i << " "; });
cout << endl;
}
} while (next_permutation(v.begin(), v.end()));
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值