• 算法学习(一) 全排列的几种递归算法


          全排列是算法学习的一个初级问题,也是近几年IT公司比较热衷的问题。最近因为一个朋友的实际问题用到了类似全排列的算法,所以把相关的代码总结一下。

    一、问题描述

          全排列的问题非常简单,比如给定三个数字1、2、3,请将三个数字的所有排列组合按大小顺序给出。这样我们期待的结果就是:123,132,213,231,312,321

    二、第一种递归算法分析

          对于给定的n个数字,显然有n!种排列方式。关键在于怎样将所有的排列得到,一种显然的方式是首先选出第一位上的数字,然后回溯选择第二位上的数字,然后是第三位……只需要确保每一位上选择的数字不重复就可以了。这种算法比较好理解,递归也比较好设计,先上代码(c++):

     1 #include<iostream>
     2 #include<fstream>
     3 using namespace std;
     4 
     5 ifstream fin;
     6 ofstream fout;
     7 int flags[100];
     8 int numbers[100];
     9 int n;
    10 
    11 void search(int loc){
    12     if(loc==n){
    13         fout<<numbers[0]+1;
    14         for(int i=1;i!=n;i++)
    15             fout<<","<<numbers[i]+1;
    16         fout<<endl;
    17     }else{
    18         for(int i=0;i!=n;i++)
    19             if(flags[i]){
    20                 flags[i]=0;
    21                 numbers[loc]=i;
    22                 search(loc+1);
    23                 flags[i]=1;
    24             }
    25     }
    26 }
    27 
    28 int main(){
    29     
    30     //fin.open("input.txt",ios::in);
    31     fout.open("QPL.txt",ios::out);
    32     
    33     cin>>n;
    34     for(int i=0;i!=n;i++)
    35         flags[i]=1;
    36     search(0);
    37 }

          算法的关键就是search函数的实现。首先是退出条件的确定,search函数从左到右每个坑里填一个数字,当loc==n的时候填完所有的坑,这样一个排列便完成了。然后是往每个坑里填数字的过程,就是那个for循环,对于每个坑数字从0到n填,同时判定没有重复使用。

          这种实现的好处是比较直观,可拓展性比较强。大家可以试一下修改边界条件的判定标准会有什么效果。

    三、第二种递归算法分析

          网上还有这样一种看似简洁的递归算法。算法通过观察全排列的产生方式,以123为例,他的全排列为123,132,213,231,312,321.通过观察可以发现,123的全排列就是1+(23的全排列)加上2+(13的全排列)加上3+(12的全排列)这里的递归的设计就是1+perm(23)然后交换1和2再执行2+perm(13)然后交换2和3执行3+perm(23)。

          代码设计如下:

     1 #include <iostream>
     2 #include <fstream>
     3 #include <stdlib.h>
     4 
     5 using namespace std;
     6 
     7 void swap(char *a,char *b)
     8 {
     9     char temp;
    10     temp=*a;
    11     *a=*b;
    12     *b=temp;
    13 }
    14 
    15 void Perm(char *pszStr, int k, int m)
    16 {
    17     if (k == m)
    18     {
    19         static int s_i = 1;
    20         cout<<"the "<<s_i ++<<" line"<<pszStr<<endl;
    21     }
    22     else
    23     {
    24         for (int i = k; i <= m; i++) //第i个数分别与它后面的数字交换就能得到新的排列
    25         {
    26             swap(pszStr + k, pszStr + i);
    27             Perm(pszStr, k + 1, m);
    28             swap(pszStr + k, pszStr + i);//恢复现场
    29         }
    30     }
    31 }
    32 
    33 int main(int argc, const char * argv[])
    34 {
    35     char str[]="1234";
    36     Perm(str,0, 3);
    37     return 0;
    38 }
    作者: 福尔摩斯の子弟
    出处: http://www.cnblogs.com/holmestian/
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    窗体控件随窗体大小改变(包括字体大小)
    Silverlight数据加载时,等待图标显示与隐藏(Loading)
    鼠标经过时,地图上的每个城市变颜色并且有提示框
    开始博客生活
    光纤
    静态路由配置(Static Routing)
    对称加密与非对称加密
    RIP Debug 过程
    WORD 固定表头自动生成/在Word表格接续页加上重复表格标题
    RIP路由
  • 原文地址:https://www.cnblogs.com/holmestian/p/3901528.html
Copyright © 2020-2023  润新知