(四)分治法



分治法设计思想。


        分治者,分而治之也。分治法(divide and conquer method)将一个难以解决的大问题划分成为一个规模较小的子问题,分别求解各个子问题,再合并子问题的解。一般来说分治法求解过程分为以下三个阶段。

        (1)划分:把规模为n的问题划分为k个规模较小的子问题。

        (2)求解子问题:各子问题的解法与原问题的解法是相同的,可以用递归的方法求解各子问题,有时递归处理也可以用循环来实现。

        (3)合并:把各子问题的解合并起来,合并的代价因情况不同有很大的差异,分治算法的效率很大程度上依赖于合并的实现。


排序问题中的分治法:


        归并排序:
        归并策略的步骤:
        (1)划分:将待排序序列r1,r2,....rn划分为两个长度相等的子序列 r1,....rn/2和r (n/2+1)。
        (2)求解子问题:分别对这两个子序列划分两个小的子序列。
        (3)合并:将这两个有序子序列合并成一个有序序列。


        【例题】:用归并排序方法对49、38、65、97、76、13、27排序。

        【解题步骤】:
        a、原序列如下所示。
                49、38、65、97、76、13、27
        b、一共是七个数,分为两组如下所示

                49  38  65  97 | 76 13 27 
        c、再进行分组,两个小组分为两组。
                49 38 | 65 97 | 76 13 | 27 
        d、再进行划分如下。
              49 | 38 | 65 | 97 | 76 | 13 | 27

        e、这样我们就把待排序的记录划分完毕。接下来我们两两进行归并,并在归并的时候进行排序。
              38 49 |65 97 | 13 76 | 27 

        f、在上述的基础上两两再进行归并。

            38 49 65 97 | 13 27 76

        g、在上述基础上再进行两个组的归并。
              13 27 38 49 65 76 97
        就这样我们就完成了归并排序。 


        【时间 复杂度】 :归并算法的时间复杂度为  O(nlogn) 。

        【归并算法C语言代码】:
void MergeSort(int r[ ], int s, int t) //进行归并排序。
    { 
       int m,r1[1000]; 
      if(s==t) return;   
         else { 
            m=(s+t)/2;
            Mergesort(r,  s, m);    //归并排序前半个子序列
            Mergesort(r, m+1, t);   //归并排序后半个子序列
            Merge(r, r1, s, m, t);      //合并两个已排序的子序列
         }
     for(int i=s;i<=t;i++)
            r[i]=r1[i];
    }

    void Merge(int r[ ], int r1[ ], int s, int m, int t)//合并子序列。
    {
         i=s; j=m+1; k=s;
        while (i<=m && j<=t)
        {   
            if (r[i]<=r[j]) r1[k++]=r[i++];   //取r[i]和r[j]中较小者放入r1[k]
            else r1[k++]=r[j++]; 
        }
        if (i<=m) while (i<=m)   
         //若第一个子序列没处理完,则进行收尾处理
            r1[k++]=r[i++]; 
        else  while (j<=t)      
    //若第二个子序列没处理完,则进行收尾处理
                    r1[k++]=r[j++]; 
    }



        int r[] 为待排序数组、int s 数组起始下标、int t 数组结束下标。


快速排序:

        【思路】

        (1)划分:选定一个记录作为轴值,以轴值为基准将整个序列划分为两个子序列,轴值的位置i在划分的过程中确定,并且前一个为子序列中的记录小于或者等于轴值; 后一个子序列中的记录均大于或者等于轴值。

        (2)求解子问题,分别对划分后的子问题进行递归处理。

        (3)把子序列就地排序,并不需要任何的合并就得到一个排序序列。

        【例题】:

                初始序列为 23 13 35 6 19 50 28  


        【想法】:首先对待排序列记录进行划分,划分的轴值应该遵循平衡子问题的原则,使划分后的两个子序列的长度尽量相等,这是决定快速排序算法时间性能的关键。轴值的选择有很多种,我们可以随机选出一个记录作为轴值,从而期望划分是较平衡的。


        上述例题我们以第一数为轴值,划分轴值左侧的都小于轴的值,轴值右侧都大于轴值。


        a、初始键值序列 23 13 35 6 19 50 28   i指示初值23  j指示最右侧数字。


        b、从右侧扫描,j不断移动找到比23小的数字,当j移动到19时,进行比较发现19在23的右侧,但是却是小于23,于是我们把19 和23 的位置进行交换。


        c、交换后的序列如下,同时指针i++

                19 13 35 6 23 50 28 


        d、左侧进行扫描直到 i指向的数字大于23 ,扫描到35 ;35 和23 进行交换,交换结果如下所示。

                19 13 23 6 35 50 28 

        e、j向左侧移动一位,j由指向35 移动到指向6,同时判断j指向的是否小于23 ,然后进行交换,交换结果如下所示。

                19 13 6 23 35 50 28

        f、交换后i++,此时i和j同时指向23 ,这样我们第一次划分结束。

                {19 13 6 }  23   { 35 50 28 }


        以轴值为基准将待排序列划分为两个子序列后,对每一个子序列分别递归进行处理。处理过程如下所示。


        键值初始值:23 13 35 6 19 50 28  


        第一次划分之后:{19 13 6 }  23  { 35 50 28 }


        分别进行快速排序:{ 6  13 } 19  23 { 28 } 35  { 50 }


        6 { 13 } 19 23 28 35 50 


        最终结果为:6  13 19 23 28 35 50



【C代码如下所示:】


一次划分函数。

int Partition(int r[ ], int first, int end)
     {
          i=first; j=end;         //初始化
          while (i<j)
          {  
       while (i<j && r[i]<= r[j]) j--;  //右侧扫描
               if (i<j) { 
                  r[i]←→r[j];            //将较小记录交换到前面
                  i++; 
               }
              while (i<j && r[i]<= r[j]) i++;  //左侧扫描
              if (i<j) {
                 r[j]←→r[i];            //将较大记录交换到后面
                 j--; 
              }
          }
          retutn i;    // i为轴值记录的最终位置
    }


不断的确定轴值。
void QuickSort(int r[ ], int first, int end)
  {
     if (first<end) {      
        pivot=Partition(r, first, end);  
          //问题分解,pivot是轴值在序列中的位置
        QuickSort(r, first, pivot-1); 
          //递归地对左侧子序列进行快速排序
        QuickSort(r, pivot+1, end);
         //递归地对右侧子序列进行快速排序
     }
  }




本节完毕,下一节减治法。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基础篇 1、 算有哪些特点?它有哪些特征?它和程序的主要区别是什么? 2、 算的时间复杂度指的是什么?如何表示? 3、 算的空间复杂度指的是什么?如何表示? 4、 什么是最坏时间复杂性?什么是最好时间复杂性? 5、 什么是递归算?什么是递归函数? 6、 分治的设计思想是什么? 7、 动态规划基本步骤是什么? 8、 回溯与分枝限界之间的相同点是什么?不同之处在哪些方面? 9、 分枝限界的基本思想是什么? 10、 限界函数的功能是什么? 11、 设某一函数定义如下: 编写一个递归函数计算给定x的M(x)的值。 12、 已知一个顺序表中的元素按元素值非递减有序排列,编写一个函数删除表中多余的值相同的元素。 13、 分别写出求二叉树结点总数及叶子总数的算分治术 14、 有金币15枚,已知其中有一枚是假的,而且它的重量比真币轻。要求用一个天平将假的金币找出来,试设计一种算(方案),使在最坏情况下用天平的次数最少。 15、 利用分治策略,在n个不同元素中找出第k个最小元素。 16、 设有n个运动员要进行网球循环赛。设计一个满足以下要求的比赛日程表。 (1)每个选手必须与其它n-1选手各赛一次; (2)每个选手一天只能赛一次。 17、 已知序列{503,87,512,61,908,170,897,275,652,462},写一个自底向上的归并分类算对该序列作升序排序,写出算中每一次归并执行的结果。 贪心 18、 设有n个文件f1,f2,…,fn要求存放在一个磁盘上,每个文件占磁盘上1个磁道。这n个文件的检索概率分别是p1,p2,…,pn,且 =1。磁头从当前磁道移到被检索信息磁道所需的时间可用这两个磁道之间的径向距离来度量。如果文件fi存放在第i道上,1≤i≤n则检索这n个文件的期望时间是 。其中d(i,j)是第i道与第j道之间的径向距离。磁盘文件的最优存储问题要求确定这n个文件在磁盘上的存储位置,使期望检索时间达到最小。试设计一个解此问题的算,并分析算的正确性与计算复杂性。 19、 设有n个正整数,编写一个算将他们连接成一排,组成一个最大的多位整数。用贪心求解本题。 20、 键盘输入一个高精度的正整数N(此整数中没有‘0’),去掉其中任意S个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小(输出应包括所去掉的数字的位置和组成的新的正整数,N不超过240位)。 21、 对于下图给出的有向网,写出用Dijkstra方求从顶点A到图中其它顶点的最短路径的算,并写出执行算过程中顶点的求解次序及从顶点A到各顶点路径的长度。 22、 对于上图给出的有向图,写出最小成本生成树,给出求解算。 动态规划 23、 求出上图中每对结点间的最短距离的算,并给出计算结果。 24、 下图中给出了一个地图,地图中每个顶点代表一个城市,两个城市间的连线代表道路,连线上的数值代表道路的长度。现在,想从城市A到达城市E,怎样走路程最短,最短路程的长度是多少? 25、 已知序列a1,a2,…,an,试设计一算,从中找出一子序列 ai1 < ai2 < … E。试用动态规划的最优化原理求出A->E的最省费用。 29、 已知如下图,写出用动态规划求最短路径的递推关系式,并写出求从源点A0到终点A3 的最短路径过程。给出求解算。 6 A1 A2 5 5 2 A0 A3 3 4 4 B1 B2 5 搜索与遍历问题 30、 已知有向图G=,试设计一算以判断对于任意两点u和v,是否存在一条从u到v的路径,并分析其复杂度。 31、 对于给定的一个二叉树T(如下图) a) 设计一个算,统计二叉树中结点总数; b) 设计一个算,求二叉树最大宽度及最大宽度所在深度。 32、 判近亲问题。给定一个家族族谱,为简化问题起见,假设家族中的夫妻关系只表示男性成员。设用线性表存储家族成员,用成员的父指针指向其生父。编写一个在此种族谱表示方式下的算,判断给定的二个家族成员是否是五代内的近亲。(提示:家族成员的表示方式应与搜索方式相适应。) 33、 完全二叉树定义为:深度为K,具有N个结点的二叉树的每个结点都与深度为K的满二叉树中编号从1至N的结点一一对应。(1)写一个建立二叉树的算。(2)写一个判别给定的二叉树是否是完全二叉树的算。 34、 编写计算整个二叉树高度的算(二叉树的高度也叫二叉树的深度)。 35、 编写计算二叉树最大宽度的算(二叉树的最大宽度是指二叉树所有层中结点个数的最大值)。 回溯 36、 (组合问题)求出从自然数1,2,…,n中任取r个数的所有组合。 37、 传教士与野人渡河问题。有M个传教士和M个野人准备渡河,船一次最多载2人,任何时刻野人数不能多于传教士数,但允许全部为野人。编写算给出合理的渡河计划。 38、 某乡有n个村庄,有一个售货员,他要到各个村庄去售货,各村庄之间的路程s是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1。试设计一个算,帮他选择一条最短的路。 39、 设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设wi,j是从供应商j处购得的部件i的重量,ci,j是相应的价格。试设计一个算,给出总价格不超过c的最小重量机器设计。 40、 设有n件工作分配给n个人。为第i个人分配工作j所需的费用为ci,j 。试设计一个算,计算最佳工作分配方案,为每一个人都分配1 件不同的工作,并使总费用达到最小。 41、 编写程序求解骑士巡游问题:在n行n列的棋盘上(如n=8),假设一位骑士(按象棋中“马走日”的行走)从初始坐标位置(x1,y1)出发,要遍访(巡游)棋盘中的每一个位置一次。请编一个程序,为骑士求解巡游“路线图”(或告诉骑士,从某位置出发时,无遍访整个棋盘 — 问题无解骑士巡游)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值