Next Permutation 求下一个排序@LeetCode

完全没有思路,参考了 http://www.cnblogs.com/etcow/archive/2012/10/02/2710083.html

分三步:

  1. 从后往前找falling edge,下降沿。(下降之后的那个元素)

  2. 从下降沿开始往后找出替换它的元素。(就是第一个比它小的前一个元素)

  3. 反转后面所有元素,让他从小到大sorted(因为之前是从大到小sorted的)

  例如 “547532“

  1. “547532”, 4是下降沿。

  2. “547532”, 5是要替换的元素, 替换后得到 “ 557432”

     3. "557432",   7432反转,得到 “552347”。



package Level4;

/**
 * Next Permutation
 * 
 *  Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place, do not allocate extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
 *
 */
public class S31 {

	public static void main(String[] args) {

	}
	
	public void nextPermutation(int[] num) {
        if(num.length <= 1){
        	return;
        }
        
        // 1. 从后往前找falling edge,下降沿。(下降之后的那个元素)
        int edge = -1;
        for(int i=num.length-2; i>=0; i--){
        	if(num[i] < num[i+1]){
        		edge = i;
        		break;
        	}
        }
        
        if(edge > -1){
        	// 2. 从下降沿开始往后找出替换它的元素。(就是第一个比它小的前一个元素)
        	for(int i=edge+1; i<num.length; i++){
        		if(num[edge] >= num[i]){
        			nextPermutationSwap(num, edge, i-1);
        			break;
        		}
        		if(i == num.length-1){
        			nextPermutationSwap(num, edge, i);
        			break;
        		}
        	}
        }
        
        // 3. 反转后面所有元素,让他从小到大sorted(因为之前是从大到小sorted的)
        for(int i=edge+1, j=num.length-1; i<=edge+(num.length-edge-1)/2; i++, j--){
        	nextPermutationSwap(num, i, j);
        }
        
    }
	
	public void nextPermutationSwap(int[] num, int i, int j){
		int tmp = num[i];
		num[i] = num[j];
		num[j] = tmp;
	}

}


<think>嗯,我现在要了解一下next_permutation这个知识点。首先,我得弄清楚它是什么,是哪个编程语言里的函数。好像听说过在C++的STL里有这个函数,对吧?那它的作用是什么呢?可能和排列生成有关?比如给定一个序列,生成它的下一个排列。那什么是“下一个排列呢?应该是指按照字典序排列的下一个更大的排列吧。比如,如果当前排列是1,2,3,那么下一个排列就是1,3,2,对吗?不过有时候可能没有下一个排列,这时候函数会不会返回false或者什么? 然后,我需要想一下它的使用方法。函数原型是什么样的?可能是bool next_permutation(first, last),接受迭代器作为参数,然后修改容器内的元素到下一个排列,如果存在的话返回true,否则返回false。比如,如果当前已经是最大的排列,比如3,2,1,那么调用之后会变成最小的排列1,2,3,并返回false吗?或者返回false表示没有下一个排列,但此时排列会被重置?可能需要查证一下。 接下来,具体的例子。比如数组初始是1,2,3,使用next_permutation的话,会生成哪些排列?可能的顺序是123,132,213,231,312,321,对吗?那每次调用之后,数组会被修改为下一个排列,直到所有排列遍历完毕,函数返回false。这时候数组应该回到初始状态吗?或者说最后一个排列调用之后,函数返回false,数组变成初始状态?或者不会变? 另外,这个函数是否处理重复元素的情况?比如,如果有元素重复的话,比如1,1,2,那么生成的排列会不会有重复?或者函数会自动处理成唯一的排列?比如,1,1,2的下一个排列应该是1,2,1,再下一个是2,1,1,对吗?这时候next_permutation是否能正确处理这些情况,跳过重复的排列? 还有,关于如何自定义比较函数。比如,如果希望按降序生成排列,可能需要使用greater比较器,但这时候next_permutation的行为会不会改变?比如,初始排列是3,2,1,使用默认的less比较器的话,调用next_permutation会生成更大的排列,但如果用greater的话,可能行为不同。可能需要举例说明。 另外,这个函数的时间复杂度是多少?应该是O(n)的,因为每次生成下一个排列需要线性时间。而全排列总共有n!种,所以生成所有排列的时间是O(n!*n)。不过具体实现可能更高效。 再想一个应用场景。比如,需要遍历所有可能的排列来解决问题,比如旅行商问题的暴力解法,或者需要生成所有排列进行测试的情况。使用next_permutation可以方便地生成,而不需要自己实现排列生成算法。 可能需要注意的事项:在调用next_permutation之前,需要确保序列是排序好的,否则可能无法生成所有排列。比如,如果初始序列不是最小的字典序,那么调用next_permutation只能生成当前排列之后的那些排列,而无法遍历全部。所以通常做法是先排序,然后循环调用直到返回false。 例如,如果有一个数组是3,1,2,那么直接调用next_permutation的话,可能不会得到所有排列,因为初始状态不是最小的。所以正确做法是先排序为1,2,3,然后循环调用。 最后,编写一个示例代码可能会帮助理解。比如,在C++中: #include <algorithm> #include <vector> #include <iostream> using namespace std; int main() { vector<int> v = {1, 2, 3}; do { for (auto i : v) cout << i << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); return 0; } 这段代码会输出所有排列,每个排列一行。注意这里用的是do-while循环,保证第一个排列(初始状态)也会被处理。而next_permutation在最后一个排列时返回false,退出循环。 那如果有重复元素的情况呢?比如,v是{1,1,2},那么生成的排列应该是三个:1,1,2;1,2,1;2,1,1。这个时候next_permutation会不会正确处理,不生成重复的排列?比如,初始排列是1,1,2,调用next_permutation得到1,2,1,再次调用得到2,1,1,再调用则返回false,对吗?是的,这时候函数会自动处理重复元素,生成唯一的排列。 总结一下,next_permutation是C++ STL中的一个函数,用于生成序列的下一个字典序排列。它的基本用法是接受两个迭代器作为参数,修改序列到下一个排列,并返回是否成功。通常需要先排序,然后循环调用直到返回false。需要注意处理重复元素的情况,以及正确使用比较函数。</think>`next_permutation` 是 C++ STL 中的一个重要函数,用于生成序列的**下一个字典序排列**。以下是关键知识点与示例: --- ### **核心知识点** 1. **功能** 修改序列为当前排列的**下一个字典序更大的排列**。若已是最大排列,则将其重置为最小排列并返回 `false`。 2. **函数原型** ```cpp bool next_permutation(Iterator first, Iterator last); ``` - 输入:迭代器范围 `[first, last)`。 - 返回值:`true` 表示存在下一个排列,`false` 表示已到最大排列。 3. **字典序规则** - 类似字母表顺序,例如 `1,2,3` 的下一个排列是 `1,3,2`。 - 若当前排列是最大排列(如 `3,2,1`),则循环回到最小排列 `1,2,3`。 4. **时间复杂度** - 单次调用时间复杂度为 $O(N)$,生成所有排列的总时间为 $O(N! \times N)$。 5. **重复元素处理** - 自动跳过重复排列。例如 `{1,1,2}` 仅生成 3 种唯一排列。 6. **自定义排序规则** 可通过自定义比较函数实现降序排列: ```cpp bool next_permutation(first, last, greater<T>()); ``` --- ### **使用步骤** 1. **排序序列** 需先将序列排序为最小字典序,否则无法遍历所有排列。 2. **循环调用** 使用 `do-while` 循环确保包含初始排列: ```cpp sort(v.begin(), v.end()); do { // 处理当前排列 } while (next_permutation(v.begin(), v.end())); ``` --- ### **示例代码** ```cpp #include <algorithm> #include <vector> #include <iostream> using namespace std; int main() { vector<int> v = {1, 2, 3}; // 1. 必须排序 sort(v.begin(), v.end()); // 2. 生成所有排列 do { for (int num : v) cout << num << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); return 0; } ``` **输出**: ``` 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 ``` --- ### **重复元素示例** 输入 `{1,1,2}`: ```cpp vector<int> v = {1, 1, 2}; sort(v.begin(), v.end()); do { for (int num : v) cout << num << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); ``` **输出**: ``` 1 1 2 1 2 1 2 1 1 ``` --- ### **注意事项** - **必须预先排序**:未排序的序列无法遍历所有排列。 - **返回值意义**:返回 `false` 表示当前已是最末排列,序列被重置为最小排列。 - **性能**:适合小规模数据(如 $N \leq 10$),大规模数据需避免暴力枚举。 --- ### **应用场景** - 暴力搜索问题(如旅行商问题)。 - 需要遍历所有排列的测试用例生成。 - 组合数学或排列相关的算法题(如 LeetCode 的排列题目)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值