非有序全排列生成算法集锦

 /*
  Name: 非有序全排列生成算法集锦
  Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处
  Author: goal00001111
  Date: 20-01-09 15:55
Description: 实现了七种非有序全排列生成算法。
有关算法的分析讨论详见拙作《非有序全排列生成算法》:
http://blog.csdn.net/goal00001111/archive/2009/01/20/3839688.aspx
*/
#include<iostream>
#include <time.h>

using namespace std;

void Print(int a[], int n);
void Permutation1(int n);
void Permutation2(int n);
void Permutation3(int n);
void Permutation4(int n);
void Permutation5(int n);
void Permutation6(int n);
void Permutation7(int n);
void Recursion1(int a[], int n, int k);
void Recursion2(int a[], int n, int k);
bool Move(int a[], bool p[], int n);
void DiZYingShe(int yShe[], int diZ[], int n);
void DiJYingShe(int yShe[], int diJ[], int n);

int main()
{
    int n = 10;
    time_t startTime;
    time_t endTime;

    time(&startTime);
    Permutation1(n); //普通递归算法
    time(&endTime);
    cout << "time 1 = " << difftime(endTime, startTime) << endl << endl;
    
    time(&startTime);
    Permutation2(n); //全排列循环移位法
    time(&endTime);
    cout << "time 2 = " << difftime(endTime, startTime) << endl << endl;
    
    time(&startTime);
    Permutation3(n); //排列邻位对换法
    time(&endTime);
    cout << "time 3 = " << difftime(endTime, startTime) << endl << endl;
    
    time(&startTime);
    Permutation4(n); //递增进位排列生成算法
    time(&endTime);
    cout << "time 4 = " << difftime(endTime, startTime) << endl << endl;
    
    time(&startTime);
    Permutation5(n); //递减进位排列生成算法
    time(&endTime);
    cout << "time 5 = " << difftime(endTime, startTime) << endl << endl;
    
    time(&startTime);
    Permutation6(n); //循环左移排列生成算法
    time(&endTime);
    cout << "time 6 = " << difftime(endTime, startTime) << endl << endl;

    time(&startTime);
    Permutation7(n); //颠倒的协词典顺序算法
    time(&endTime);
    cout << "time 7 = " << difftime(endTime, startTime) << endl << endl;
    
    system("pause");
    return 0;
}

void Print(int a[], int n)
{
    for (int i=0; i<n; i++)
        cout << a[i];
    cout << endl;
}
普通递归算法
/*
函数名称:Permutation
函数功能:普通递归算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation1(int n)
{
    int *a = new int[n];//用来存储n个自然数
    for (int i=0; i<n; i++) //存储全排列的元素值
        a[i] = i + 1;
    
    Recursion1(a, n, n-1); //调用递归函数     
    
    Print(a, n); //输出最后一个全排列
    
    delete []a;
}

/*
函数名称:Recursion
函数功能:递归输出n个数的所有全排列
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
          int n:数组a[]的长度
          int k:正在处理的k个元素所组成的排列
输出变量:无
*/
void Recursion1(int a[], int n, int k)
{
    if (k == 0) //排列只有一个元素a[k],直接输出
       ;// Print(a, n);
    else
    {
        int temp;
        for (int i=0; i<=k; i++) //穷举,依次让第k个元素与前面的元素交换
        {
            temp = a[i];
            a[i] = a[k];
            a[k] = temp;
            
            Recursion1(a, n, k-1); //递归生成k-1个元素的全排列
            
            temp = a[i]; //再换回来
            a[i] = a[k];
            a[k] = temp;
        }
    }
}

//全排列循环移位法///
/*
函数名称:Permutation
函数功能:全排列循环移位法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation2(int n)
{
    int *a = new int[n];//用来存储n个自然数
    for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
    {
        a[i] = i + 1;
    }
    
    Recursion2(a, n, 1);
    
    Print(a, n); //输出最后一个全排列
    
    delete []a;
}

/*
函数名称:Recursion
函数功能:递归输出n个数的所有全排列
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
          int n:数组a[]的长度
          int k:正在处理的k个元素所组成的排列
输出变量:无

*/
void Recursion2(int a[], int n, int k)
{
    if (k > n)
        ;//Print(a, n);
    else
    {
        int temp;
        for (int i=0; i<k; i++)//循环左移
        {
            temp = a[0];
            for (int j=1; j<k; j++)
                a[j-1] = a[j];
            a[k-1] = temp;
            
            Recursion2(a, n, k+1);
        }
    }
}

/排列邻位对换法
/*
函数名称:Permutation
函数功能:排列邻位对换法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation3(int n)
{
    int  *a = new int[n];  //用来存储n个自然数
    bool *p = new bool[n]; //用来存储n个元素的指向:向左为false,向右为true
    for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
    {
        a[i] = i + 1;
        p[i] = false; //开始均指向左侧
    }
    
    do
    {
      //  Print(a, n); //输出第一个全排列
        if (n == a[n-1])//若n在最右侧,将其逐次与左侧的元素交换,得到n - 1个新的排列
        {
            for (int i=n-1; i>0; i--)
            {
                int  temp = a[i]; a[i] = a[i-1]; a[i-1] = temp;
                bool flag = p[i]; p[i] = p[i-1]; p[i-1] = flag;
              //  Print(a, n);
            }
        }
        else //若n在最左侧,将其逐次与右侧的元素交换,得到n - 1个新的排列
        {
            for (int i=1; i<n; i++)
            {
                int temp = a[i]; a[i] = a[i-1]; a[i-1] = temp;
                bool flag = p[i]; p[i] = p[i-1]; p[i-1] = flag;
              //  Print(a, n);
            }
        }
    } while (Move(a, p, n));
    
    Print(a, n); //输出最后一个全排列
    
    delete []a;
    delete []p;
}

/*
函数名称:Move
函数功能:寻找最大可移数,可移数m,将m与其箭头所指的邻数互换位置,
          并将所得新排列中所有比m大的数p的方向调整
输入变量:int a[]:存储了1,2,3,...,n共n个自然数的数组
          bool p[]:存储了n个元素的指向的数组:向左为false,向右为true
          int n:数组a[]的长度
输出变量:排列中存在最大可移数,则做了相关操作后返回真,否则直接返回假
*/
bool Move(int a[], bool p[], int n)
{
    int max = 1;
    int pos = -1;
    
    for (int i=0; i<n; i++)
    {
        if (a[i] < max)
            continue;
        if ((p[i] && i < n-1 && a[i] > a[i+1]) || //指向右侧  
            (!p[i] && i > 0 && a[i] > a[i-1]))    //指向左侧
        {
            max = a[i];
            pos = i;
        }
    }
    
    if (pos == -1) //都不能移动
        return false;
   
    //与其箭头所指的邻数互换位置
    if (p[pos]) //指向右侧
    {
        int  temp = a[pos]; a[pos] = a[pos+1]; a[pos+1] = temp;
        bool flag = p[pos]; p[pos] = p[pos+1]; p[pos+1] = flag;
    }
    else //指向左侧
    {
        int  temp = a[pos]; a[pos] = a[pos-1]; a[pos-1] = temp;
        bool flag = p[pos]; p[pos] = p[pos-1]; p[pos-1] = flag;
    }
    
    //将所得排列中所有比max大的数p的方向调整
    for (int i=0; i<n; i++)    
    {
        if (a[i] > max)
            p[i] = !p[i];
    }
    
    return true;
}

//递增进位排列生成算法
/*
函数名称:Permutation
函数功能:递增进位排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation4(int n)
{
    int *p = new int[n];//用来存储各个全排列的值,p[i]=i!
    p[0] = 1;
    for (int i=1; i<n; i++)
    {
        p[i] = (i + 1) * p[i-1]; //cout << p[i] << "  ";
    }

    int *b = new int[n];      //用来存储新的全排列
    int *yShe = new int[n-1]; //将原中介数加上序号i的递增进位制数得到新的映射
    int *diZ = new int[n-1];  //用来存储序号i的递增进位制数,设所有元素的初值为0
    for (int i=0; i<n-1; i++)
    {
        diZ[i] = yShe[i] = 0;
    }
    
    diZ[n-2] = 1; //存储序号1,表示每次递增1
    for (int c=0; c<p[n-1]; c++)
    {
        DiZYingShe(yShe, diZ, n); //每次递增1后得到得到新的映射
        
        for (int i=0; i<n; i++) //新的全排列空格初始化
            b[i] = 0;
        
        int num = n;
        for (int j=0; j<n-1; j++) //获取前n-1个数字
        {
            int s = 0;
            int k = n - 1;
            while (s < yShe[j])
            {
                if (b[k] == 0) //该处未填数字
                    s++;
                k--;
            }
            while (b[k] != 0) //跳过已填数字
                k--;
            b[k] = num--;
        }
        for (int i=0; i<n; i++)//填充最后一个数字1
        {
            if (b[i] == 0)
            {
                b[i] = 1;
                break;
            }
        }
        
      //  Print(b, n);
    }
    
    Print(b, n); //输出最后一个全排列
    
    delete []b;
    delete []p;
    delete []diZ;
    delete []yShe;
}

/*
函数名称:DiZYingShe
函数功能:递增进位排列生成算法的加法运算:yShe[] += dZJ[]
输入变量:int yShe[]:原递增进制中介数;
          int diZ[]:递增进制数加数
          int len:最大自然数n的值
输出变量:int yShe[]:新的递增进制中介数
*/
void DiZYingShe(int yShe[], int diZ[], int n)
{
    int pos = n - 2;
    int r = 0;
    while (pos >= 0)
    {
        yShe[pos] += diZ[pos] + r;
        if (yShe[pos] >= n - pos)
        {
            yShe[pos] -= n - pos;
            r = 1;
        }
        else
            r = 0;
        pos--;
    }
}

///递减进位排列生成算法///
/*
函数名称:Permutation
函数功能:递减进位排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation5(int n)
{
    int *p = new int[n];//用来存储各个全排列的值,p[i]=i!
    p[0] = 1;
    for (int i=1; i<n; i++)
    {
        p[i] = (i + 1) * p[i-1]; //cout << p[i] << "  ";
    }

    int *b = new int[n];//用来存储新的全排列
    int *yShe = new int[n-1]; //将原中介数加上序号i的递减进位制数得到新的映射
    int *diJ = new int[n-1];
    for (int i=0; i<n-1; i++)
    {
        diJ[i] = yShe[i] = 0;
    }
    
    diJ[n-2] = 1;
    for (int c=0; c<p[n-1]; c++)
    {
        DiJYingShe(yShe, diJ, n);  //每次递增1
        
        for (int i=0; i<n; i++) //新的全排列空格初始化
            b[i] = 0;
        
        int num = n;
        for (int j=n-2; j>=0; j--) //获取前n-1个数字
        {
            int s = 0;
            int k = n - 1;
            while (s < yShe[j])
            {
                if (b[k] == 0) //该处未填数字
                    s++;
                k--;
            }
            while (b[k] != 0) //跳过已填数字
                k--;
            b[k] = num--;
        }
        for (int i=0; i<n; i++)//填充最后一个数字1
        {
            if (b[i] == 0)
            {
                b[i] = 1;
                break;
            }
        }
        
        //Print(b, n);
    }
    
    Print(b, n); //输出最后一个全排列
    
    delete []b;
    delete []p;
    delete []diJ;
    delete []yShe;
}

/*
函数名称:DiJYingShe
函数功能:递减进位排列生成算法的加法运算:yShe[] += dZJ[]
输入变量:int yShe[]:原递减进制中介数;
          int diJ[]:递减进制数加数
          int n:最大自然数n的值
输出变量:int yShe[]:新的递减进制中介数
*/
void DiJYingShe(int yShe[], int diJ[], int n)
{
    int pos = n - 2;
    int r = 0;
    while (pos >= 0)
    {
        yShe[pos] += diJ[pos] + r;
        if (yShe[pos] >= pos + 2)
        {
            yShe[pos] -= pos + 2;
            r = 1;
        }
        else
            r = 0;
        pos--;
    }
}

循环左移排列生成算法///
/*
函数名称:Permutation
函数功能:循环左移排列生成算法:输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation6(int n)
{
    int *p = new int[n];//用来存储各个全排列的值,p[i]=i!
    p[0] = 1;
    for (int i=1; i<n; i++)
    {
        p[i] = (i + 1) * p[i-1]; //cout << p[i] << "  ";
    }

    int *b = new int[n];//用来存储新的全排列
    int *yShe = new int[n-1]; //将原中介数加上序号i的递减进位制数得到新的映射
    int *diJ = new int[n-1];
    for (int i=0; i<n-1; i++)
    {
        diJ[i] = yShe[i] = 0;
    }
    
    diJ[n-2] = 1;
    for (int c=0; c<p[n-1]; c++)
    {
        DiJYingShe(yShe, diJ, n);  //每次递增1
        
        for (int i=0; i<n; i++) //新的全排列空格初始化
            b[i] = 0;
        
        int num = n;
        int pos = n - 1;
        for (int j=n-2; j>=0; j--) //获取前n-1个数字
        {
            int s = 0;
            int k = pos;
            while (k >= 0 && s < yShe[j])
            {
                if (b[k] == 0) //该处未填数字
                    s++;
                k--;
            }
            
            //跳过已填数字
            while (k >= 0 && b[k] != 0)
                k--;
            if (b[k] != 0)//如果到了最左侧还没有找到适合的位置,从最右侧继续分析
            {
                k = n - 1;
                while (k > pos && b[k] != 0)//跳过非空位:b[j] == 0表示j位置是空位
                    k--;
            }
            
            //如果到了最左侧还没有找到适合的位置,从最右侧继续累积s
            if (s < yShe[j])
            {
                k = n - 1;
                while (k >= 0 && s < yShe[j])
                {
                    if (b[k] == 0)
                        s++;
                    k--;
                }
                while (k >= 0 && b[k] != 0) //跳过已填数字
                    k--;
            }
            b[k] = num--;
            pos = k;
        }
        
        for (int i=0; i<n; i++)//填充最后一个数字1
        {
            if (b[i] == 0)
            {
                b[i] = 1;
                break;
            }
        }
        
      //  Print(b, n);
    }
    
    Print(b, n); //输出最后一个全排列
    
    delete []b;
    delete []p;
    delete []diJ;
    delete []yShe;
}

/颠倒的协词典顺序算法
/*
函数名称:Permutation
函数功能:颠倒的协词典顺序算法:逆序输出n个数的所有全排列
输入变量:int n:1,2,3,...,n共n个自然数
输出变量:无
*/
void Permutation7(int n)
{
    int *a = new int[n];//用来存储n个自然数
    for (int i=0; i<n; i++) //存储全排列的元素值,并计算全排列的数量
    {
        a[i] = i + 1;
    }
    
    int temp, left, right;
    while (1)
    {
       // Print(a, n);
        for (right=1; right<n; right++)//找出第一个比右边数字小的数字的序号right-1
        {
            if (a[right-1] < a[right])
                break;
        }
        if (right == n) //表示已经输出所有全排列
            break;
        //找到右边界左边数组中比a[right]小的最大的元素,这个就是要取代a[right]的元素
        for (left=0; a[left] >= a[right]; left++)
            ;
        temp = a[left]; a[left] = a[right]; a[right] = temp; //交换a[right]和a[left]
        
        left = 0;
        right--; //右边界减1,保证此时左右边界之间的元素是一个递减排列
        while (left < right)//逆置左右边界之间的元素,使其按增序排列
        {
            temp = a[left]; a[left] = a[right]; a[right] = temp;
            left++;
            right--;
        }
    }
    
    Print(a, n);
    
    delete []a;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值