百度面试题——需找下一个排列(Find next permuation, POJ 1883)
2012-08-17 10:08
645 查看
面试中关于排列的题目还是蛮常见的,之前有个同学面百度,就遇到了一个排列题目,分享一下。
例如, 下面的数据,就是按照排列序生成的四组数据。
3 1 2 4 5
3 1 2 5 4
3 1 4 2 5
3 1 4 5 2
百度一下发现POJ上居然有一个一模一样题目: http://poj.org/problem?id=1833.
虽然有个函数叫next_permutation, 不过做OJ还好,面试这个一定不行啦,所以还是自己分析一下。
生成给定全排列的下一个排列
所谓一个的下一个就是这一个与
下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
算法分三个步骤:
一般而言,设P是[1,n]的一个全排列。
1. P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn
j=max{i|Pi<Pi+1},k=max{i|Pi>Pj}
2. 对换Pj,Pk,将Pj+1…Pk-1PjPk+1…Pn翻转,
3. P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个。
代码如下:
不过既然提到求出排练顺便再给出一个生成所有排列的方法。就是组合数学里面说的“交换法”
这个方法运行的流程如下:
考虑{1,2…n}的一个排列,其上每一个整数都给了一个方向,我们称整数k是可移的(Mobile&Active),如果它的箭头所指的方向的邻点小于它本身。例如
中6、3、5都是可移的。显然1永远不可移,n除了以下两种情形外,它都是可移的:
(1) n是第一个数,且其方向指向左侧
(2) n是最后一个数,且其方向指向右侧
于是,我们可由
按如下算法产生所有排列
算法
1,开始时:
2,当存在可移数时
(a)找最大的可移数m
(b)将m与其箭头所指的邻数互换位置
(c)将所得排列中比m大的数p的方向调整,即改为相反方向。
代码如下:
题目描述:
给你一个数组,如果是按照数字的大小排序,那么请你输出当前数组的下一个排列是什么例如, 下面的数据,就是按照排列序生成的四组数据。
3 1 2 4 5
3 1 2 5 4
3 1 4 2 5
3 1 4 5 2
百度一下发现POJ上居然有一个一模一样题目: http://poj.org/problem?id=1833.
虽然有个函数叫next_permutation, 不过做OJ还好,面试这个一定不行啦,所以还是自己分析一下。
分析:
我们用字典序的排列生成方法:生成给定全排列的下一个排列
所谓一个的下一个就是这一个与
下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
算法分三个步骤:
一般而言,设P是[1,n]的一个全排列。
1. P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn
j=max{i|Pi<Pi+1},k=max{i|Pi>Pj}
2. 对换Pj,Pk,将Pj+1…Pk-1PjPk+1…Pn翻转,
3. P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个。
代码如下:
Memory: 168K Time: 469MS Language: C++ Result: Accepted Source Code #include<stdio.h> #include <memory.h> void swap( int &a, int &b) { a = a ^ b; b = a ^ b; a = a ^ b; } bool GetNextPermutation(int a[], int size) { int m = size - 1; int i,j; while(m > 0 && a[m-1] > a[m]) // step 1 { m--; } //in this case, the current permutation is the last if(m == 0) // { for( i = 0, j = size - 1; i < j; i++, j--) { swap(a[i], a[j]); } return false; } for( j = size - 1; j > m - 1; j--)//step 2 { if(a[j] > a[m-1]) { swap(a[j], a[m-1]); break; } } for( i = m, j = size - 1; i < j; i++, j--)//step 3 { swap(a[j], a[i]); } return true; } void printArray(int a[], int size) { int i; for( i = 0; i < size; i++) { if( i == 0) { printf("%d", a[i]); } else { printf(" %d", a[i]); } } printf("\n"); } int main() { int nSize; int a[1024]; int ncase; scanf("%d", &ncase); int n,k; while(ncase--) { scanf("%d%d", &n, &k); for( int i = 0; i < n; i++) { scanf("%d", &a[i]); } while(k--) { GetNextPermutation(a, n); } printArray(a, n); } return 0; }
不过既然提到求出排练顺便再给出一个生成所有排列的方法。就是组合数学里面说的“交换法”
这个方法运行的流程如下:
考虑{1,2…n}的一个排列,其上每一个整数都给了一个方向,我们称整数k是可移的(Mobile&Active),如果它的箭头所指的方向的邻点小于它本身。例如
中6、3、5都是可移的。显然1永远不可移,n除了以下两种情形外,它都是可移的:
(1) n是第一个数,且其方向指向左侧
(2) n是最后一个数,且其方向指向右侧
于是,我们可由
按如下算法产生所有排列
算法
1,开始时:
2,当存在可移数时
(a)找最大的可移数m
(b)将m与其箭头所指的邻数互换位置
(c)将所得排列中比m大的数p的方向调整,即改为相反方向。
代码如下:
#include <stdio.h> #include <memory.h> enum Direction{LEFT, RIGHT}; //方向枚举 struct Num //每个数有一个方向和值 { Direction d; int value; }; Num permutation[100]; //if return value is -1, it means we can't find the permutation anymore int GetFirstMoveable(int nsize) //在所有数里面找到最大的可以移动的数,返回下标 { int maxValue = 0; int idx = -1; for( int i = 0;i < nsize; i++) { if( i == 0 && permutation[i].d == LEFT)//最左边,并且移动方向是左 { continue; } if( i == nsize - 1 && permutation[i].d == RIGHT)//最右边,并且移动方向向右 { continue; } if(permutation[i].d == LEFT)//如果方向是左,并且比左侧元素大 { if(permutation[i].value > permutation[i-1].value) { if(maxValue < permutation[i].value) { maxValue = permutation[i].value; idx = i; } } } else { if(permutation[i].value > permutation[i+1].value)//方向向右,比右侧大 { if(maxValue < permutation[i].value) { maxValue = permutation[i].value; idx = i; } } } } return idx; } void reverseDirection(int r, int nsize) //交换完了以后,将所有大于当前交换的值的 方向调反 { for( int i = 0; i < nsize; i++) { if(permutation[i].value > permutation[r].value) { if(permutation[i].d == LEFT) { permutation[i].d = RIGHT; } else { permutation[i].d = LEFT; } } } } void swap(Num & a, Num & b) { Num t = a; a = b; b = t; } void swapValue(int r, int &nr)//交换相邻的值 { if(permutation[r].d == LEFT) { swap(permutation[r], permutation[r-1]); nr = r - 1; } else { swap(permutation[r], permutation[r+1]); nr = r + 1; } } void printAll(int nsize) { for(int i = 0; i < nsize; i++) { printf("%d ", permutation[i].value ); } printf("\n"); } int main() { int n; while(scanf("%d",&n) != EOF) { for( int i = 0; i < n;i++) { permutation[i].value = i + 1; permutation[i].d = LEFT; } int r; int nr; printAll(n); while( (r = GetFirstMoveable(n)) != -1)//算法核心循环 { swapValue(r, nr); reverseDirection(nr, n); printAll(n); } } return 0; }
相关文章推荐
- POj 1883 数字排列 next_permutation解决之
- poj 题目1883 排列(STL----next_permutation)
- C++-7.2.4-下一个排列(next_permutation)
- [ACM] poj 1146 ID Codes(字符串的下一个排列)
- POJ 1833 排列(字典序全排列、next_permutation()、copy+流迭代器)
- [ACM] poj 1146 ID Codes(字符串的下一个排列)
- 41. 百度面试题:字符串的排列(字符串)
- poj1833 排列 next_permutation函数
- STL学习笔记之next_permutation(下一个排列)
- poj-1833-排列 stl next_permutation(a,a+n)
- POJ 1146 下一个排列
- extjs+s2sh实现异步Tree的节点搜索和查找下一个(FindNext)
- POJ 3187(stl中全排列next_permutationa()函数的应用)
- 百度面试题:POJ 2192
- [CareerCup] 4.6 Find Next Node in a BST 寻找二叉搜索树中下一个节点
- POj-2718 Smallest Difference--全排列next_permutation()的应用文章标题
- poj 1833 排列 库函数 next_permutation 的调用
- poj 1146 ID Codes (字符串处理 生成排列组合 生成当前串的下一个字典序排列 【*模板】 )
- poj 1256 按一定顺序输出全排列(next_permutation)
- POJ 1833 排列(next_permutation)