全排列算法2 (非递归版本)
roadtang
在上一次,我写一个简单的全排列算法, 用了递归. 很容易看出来, 用递归很简洁和方便的.
但是递归版的全排列有一个很大的问题.
什么问题呢?
大家知道,排列在我们眼里实际上一个序列, 一个按特殊顺序排列的序列,我们可能会想第一个,第二个,第三个这个样去访问它. 那么在递归版本里,我们只能用一个很大很大的空间,去将递归的结果全部存储起来,然后再循环着去处理每个元素. 这会浪费很多空间.
再说, 如果我们只想知道第100个排列的样子是什么的呢? 那我们就必须修改上一次写的那个permutation的代码, 让他正好在100那个位置停下来,如果是你自己的代码,这个是可行的, 但是,当你想写一个库的时候, 该怎么办?
我们能不能写一个函数permutation2 ,我们每调用一次它, 就可以得到下一个排列的样子呢?
答案是肯定的.
我们可以使用堆栈来模拟,递归的行为, 从而使以前的递归变得行为可控.
** 注意, 这里并不需要 输入序列是可排序的.. 而且这里是真正的模拟, 不是狗皮膏药**
================= code start ====================
/**-----------------------
permutation (non-recurision version)
status permutation.
road 2008/09/04
--------------------------*/
#include <stack>
#include <stdio.h>
#define SWAP(x,y) do { x=x^y; y=x^y; x=x^y; } while(0)
using namespace std;
char str[1024];
int len;
enum {
F_ETNER,
F_COMEOUT
};
struct pe_ent {
int i;
int flag;
char *s;
int len;
pe_ent (int idx, char*str, int slen): i(idx), s(str), len(slen) {}
};
typedef stack<pe_ent> PermStack;
void read()
{
scanf("%s", str);
len = strlen(str);
}
void per_step(PermStack& stk)
{
if (stk.empty())
return;
while (1)
{
pe_ent& e = stk.top();
if (e.i<e.len && e.len == 1) // if in the bottom level.
{
//printf("%s", /n); //do.
e.i = e.i + 1; // mark we have done.
return;
}
if (e.i < e.len) // this level isn't done, still need do next permutation.
{
if (e.i)
SWAP(e.s[0], e.s[e.i]);
stk.push(pe_ent(0, e.s+1, e.len-1));
}
if (e.i >= e.len) // this level permutation is done, up a level to see if any further actions.
{
stk.pop();
if (stk.empty())
return;
pe_ent& e = stk.top();
if (e.i)
SWAP(e.s[0], e.s[e.i]);
e.i++;
}
}
}
void permutation ()
{
PermStack stk;
char c;
stk.push(pe_ent(0, str, len)); // push the init seq into the stack. ( call the first func)
while (!stk.empty())
{
per_step(stk);
printf("%s/n", str);
}
}
int main()
{
read ();
permutation();
}
=============== code end ==================
注:
1. struct pe_ent 代表每一个递归,栈上的空间内容.
2. 每调一次per_step, str里就会产生下一个在全排列中序列, permutation反复调用per_step完成任务
3. 你应该再检察一遍, 以确保我没有使用递归调用. ^_^
roadtang
在上一次,我写一个简单的全排列算法, 用了递归. 很容易看出来, 用递归很简洁和方便的.
但是递归版的全排列有一个很大的问题.
什么问题呢?
大家知道,排列在我们眼里实际上一个序列, 一个按特殊顺序排列的序列,我们可能会想第一个,第二个,第三个这个样去访问它. 那么在递归版本里,我们只能用一个很大很大的空间,去将递归的结果全部存储起来,然后再循环着去处理每个元素. 这会浪费很多空间.
再说, 如果我们只想知道第100个排列的样子是什么的呢? 那我们就必须修改上一次写的那个permutation的代码, 让他正好在100那个位置停下来,如果是你自己的代码,这个是可行的, 但是,当你想写一个库的时候, 该怎么办?
我们能不能写一个函数permutation2 ,我们每调用一次它, 就可以得到下一个排列的样子呢?
答案是肯定的.
我们可以使用堆栈来模拟,递归的行为, 从而使以前的递归变得行为可控.
** 注意, 这里并不需要 输入序列是可排序的.. 而且这里是真正的模拟, 不是狗皮膏药**
================= code start ====================
/**-----------------------
permutation (non-recurision version)
status permutation.
road 2008/09/04
--------------------------*/
#include <stack>
#include <stdio.h>
#define SWAP(x,y) do { x=x^y; y=x^y; x=x^y; } while(0)
using namespace std;
char str[1024];
int len;
enum {
F_ETNER,
F_COMEOUT
};
struct pe_ent {
int i;
int flag;
char *s;
int len;
pe_ent (int idx, char*str, int slen): i(idx), s(str), len(slen) {}
};
typedef stack<pe_ent> PermStack;
void read()
{
scanf("%s", str);
len = strlen(str);
}
void per_step(PermStack& stk)
{
if (stk.empty())
return;
while (1)
{
pe_ent& e = stk.top();
if (e.i<e.len && e.len == 1) // if in the bottom level.
{
//printf("%s", /n); //do.
e.i = e.i + 1; // mark we have done.
return;
}
if (e.i < e.len) // this level isn't done, still need do next permutation.
{
if (e.i)
SWAP(e.s[0], e.s[e.i]);
stk.push(pe_ent(0, e.s+1, e.len-1));
}
if (e.i >= e.len) // this level permutation is done, up a level to see if any further actions.
{
stk.pop();
if (stk.empty())
return;
pe_ent& e = stk.top();
if (e.i)
SWAP(e.s[0], e.s[e.i]);
e.i++;
}
}
}
void permutation ()
{
PermStack stk;
char c;
stk.push(pe_ent(0, str, len)); // push the init seq into the stack. ( call the first func)
while (!stk.empty())
{
per_step(stk);
printf("%s/n", str);
}
}
int main()
{
read ();
permutation();
}
=============== code end ==================
注:
1. struct pe_ent 代表每一个递归,栈上的空间内容.
2. 每调一次per_step, str里就会产生下一个在全排列中序列, permutation反复调用per_step完成任务
3. 你应该再检察一遍, 以确保我没有使用递归调用. ^_^