全配列非再帰
1793 ワード
(二)非再帰的な全配列アルゴリズム
アルゴリズムの考え方:
1つのシーケンスについて、firstで開始位置、lastで終了位置、区間で[first,last]と表すことができ、初期状態のシーケンスの値は増加する.
1、隣接する2つのデータ(i=last-1、j=last、毎回比較後i--かつj--)を後から前へ比較し、前者が後者より小さい場合(*i<*j、*iはi位置に対応する値を示す)、前者の値がX 1(X 1=*i)(位置PX=i)であることを示す.
2、再度X 1以下の最初のデータを後から前へ検索し、値はX 2とする.
3、交換X 1、X 2;
4、[PX+1,last]マーク範囲を逆にする.
ポイント:なぜこれで最小限の増加が保証されるのか.
隣接する2つのデータを後方から比較すると,上記のアルゴリズムは位置PXを得ると,[PX+1,last]は常に減少し,[first,PX)は変化せず,[PX+1,last]の全配列が得られたことを意味する.X 2位置後の値はX 1よりも小さく、X 2位置前の値はX 1よりも大きいため、X 1,X 2を交換した後も[PX+1,last]は減少する.X 2>X 1であるため、X 2の後ろがどのように並んでも元の数列より大きくなり、[PX+1,last]を反転してこのサブ数列(インクリメント)を最小にする.
これにより、保証される新しい数列は、元の数列の辞書順にnextを配列する.
コード#コード#
アルゴリズムの考え方:
1つのシーケンスについて、firstで開始位置、lastで終了位置、区間で[first,last]と表すことができ、初期状態のシーケンスの値は増加する.
1、隣接する2つのデータ(i=last-1、j=last、毎回比較後i--かつj--)を後から前へ比較し、前者が後者より小さい場合(*i<*j、*iはi位置に対応する値を示す)、前者の値がX 1(X 1=*i)(位置PX=i)であることを示す.
2、再度X 1以下の最初のデータを後から前へ検索し、値はX 2とする.
3、交換X 1、X 2;
4、[PX+1,last]マーク範囲を逆にする.
ポイント:なぜこれで最小限の増加が保証されるのか.
隣接する2つのデータを後方から比較すると,上記のアルゴリズムは位置PXを得ると,[PX+1,last]は常に減少し,[first,PX)は変化せず,[PX+1,last]の全配列が得られたことを意味する.X 2位置後の値はX 1よりも小さく、X 2位置前の値はX 1よりも大きいため、X 1,X 2を交換した後も[PX+1,last]は減少する.X 2>X 1であるため、X 2の後ろがどのように並んでも元の数列より大きくなり、[PX+1,last]を反転してこのサブ数列(インクリメント)を最小にする.
これにより、保証される新しい数列は、元の数列の辞書順にnextを配列する.
コード#コード#
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
//
void Swap(char *a, char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
void Reverse(char *a, char *b)
{
//
while(a < b)
Swap(a++, b--);
}
bool Permutation(char a[], int n)
{
char *pEnd = a + n -1;
char *p, *q, *pFind;
p = pEnd;
while(p != a)
{
q = p;
p--; //p ,q
if(*p < *q) //
{
pFind = pEnd; //
while(*pFind <= *p) // p
--pFind;
Swap(pFind, p); //
Reverse(q,pEnd); //
return true;
}
}
return false; //
}
int main()
{
char a[100];
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
a[i] = i+'1';
a[n] = '\0';
int i = 1;
do{
printf(" %3d \t%s
", i++, a);
}while (Permutation(a, n));
return 0;
}