• 经典算法学习之分治法(以排列、组合程序为例)


    分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

    分治法在每层递归是遵循的三个步骤:

    (1)分解原问题为若干个子问题,这些子问题是原问题的规模较小的实例。

    (2)解决这些子问题,队规的求解各个子问题,当子问题规模足够小的时候,直接求解。

    (3)合并这些子问题的解构成原问题的解。

    显然归并排序是一个非常经典规矩的分治法的例子,鉴于之前已经写过一篇关于归并排序的博文,这里不在使用归并排序作为例子。

    注意分治法的每一层递归中的第一步分解,可能产生两个子问题(如归并排序、二分查找等),也可能产生多个子问题(如排列、组合等),产生两个子问题的时候当然比较容易理解,而产生多个子问题的时候需要使用罗列这些子问题。

    下面就以排列和组合算法为例,介绍产生多个子问题的分治算法。

    一、排列

     问题:输入一个字符串,打印出该字符串中字符的所有排列。

     分析:利用分治法的思想,

    (1)先将原问题分解,假如输入的字符串长度是n,那么第一次选择可能是第一个字符、也可能是第二个、。。。也可能是第n个,但是不管是哪一个,只要选出第一个字符,就可以在剩下的n-1个字符里面继续选择一个了,所以需要将原问题分解为n个子问题(每个子问题为第一步选择的是i,然后再对除了i之外的字符进行全排列),到现在可以发现如果直接按照顺序分解之后,对除了i之外的字符进行全排列,不是那么容易实现递归,于是想到将每个元素(包括第一个元素)都与第一个元素交换,然后分解成的子问题就是先将每个元素与第一个元素交换并选出,然后对第二个到最后的所有元素全排列。注意每次个子问题考虑完之后需要将交换的元素换回。

    (2)利用递归解决每个子问题

    (3)当所有问题都解决的时候,子问题的解组合起来就是原问题的解了

    如:输入字符串为abc ,排列函数为permutation()那么分解成的子问题为a+permutation(bc)、b+permutation(ac)、c+permutation(ab)

     1 #include "stdafx.h"
     2 #include<iostream>
     3 using namespace std;
     4 void print(char *str)
     5 {
     6     char *p=str;
     7     while(*p)
     8     {
     9         cout<<*p<<' ';
    10         p++;
    11     }
    12 }
    13 void bianli(char *str,int begin,int length)
    14 {
    15     char temp;
    16     int i;
    17     if(begin==length-1)
    18     {
    19         print(str);
    20         cout<<endl;
    21         return ;
    22     }
    23     //可以选取某一个值(包括begin自己)与begin的位置交换,然后对剩下的字符全排列
    24     //所以对于每一个位置要么选择先交换,然后递归,要么选择不交换(即交换两次)
    25     for(i=begin;i<length;i++)
    26     {
    27         temp=str[begin];
    28         str[begin]=str[i];
    29         str[i]=temp;
    30 
    31         bianli(str,begin+1,length);
    32 
    33         temp=str[begin];
    34         str[begin]=str[i];
    35         str[i]=temp;
    36     }
    37 }
    38 
    39 int _tmain(int argc, _TCHAR* argv[])
    40 {
    41     char str[4]="123";
    42     bianli(str,0,3);
    43     return 0;
    44 }

    二、组合

    问题:找出从自然数1、2、3。。。n中任取r个元素的所有组合

    分析:

    1、分解:与排列不同,组合里每个元素在一种只出现一次,所以并不需要交换元素,而是每次从n个数中按照某种顺序取一个元素,然后考虑全面了即可,如每次取一个最大值,那么只要元素个数>k则是子问题的一种,剩下的思想和排列差不多。

     1 #include<iostream>
     2 using namespace std;
     3 int a[100];//用于存放组合的结果
     4 void zuhe(int n,int k)
     5 {
     6     for(int i=n;i>=k;i--)//顺序选取组合中最大的数
     7     {
     8         a[k]=i;
     9         if(k>1)
    10         {
    11             zuhe(i-1,k-1);
    12         }
    13         else
    14         {
    15             for(int i=1;i<=a[0];i++)
    16             {
    17                 cout<<a[i]<<" ";
    18             }
    19             cout<<endl;
    20         }
    21     }
    22 }
    23 int main()
    24 {
    25     int n,k;
    26     cin>>n>>k;
    27     a[0]=k;
    28     zuhe(n,k);
    29     return 0;
    30 }

  • 相关阅读:
    Python常见问题
    经典SQL语句大全(转)
    VMware ESX常用命令
    每天一个linux命令(目录)
    Linux 技巧:让进程在后台可靠运行的几种方法(转)
    软件测试随手记(转)
    linux下查看磁盘空间
    MQ5.3在redhat9上的安装
    我的MQ笔记
    RedHat Linux下MQ安装步骤及MQ常用命令
  • 原文地址:https://www.cnblogs.com/bewolf/p/4483821.html
Copyright © 2020-2023  润新知