zoukankan      html  css  js  c++  java
  • 分治思想:合并排序和快速排序

    分治思想:合并排序和快速排序

      分治思想(Divide-and-conquer):

      作为程序设计的一种方法,有时为了解决一个给定的问题,算法要一次或多次地递归调用自身来解决相关的子问题。这些算法通常采用分子的策略:将一个问题划分成n个规模更小并且结构和原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

      一般采用分治方法解决问题,按如下三个步骤不断循环:

    • 分解(  Divide  ): 将原问题分解成许多个规模更小并且和原问题结构相似的小问题
    • 控制(Conquer): 从小问题的层次上着手解决问题。(在递归层面上:1. 小问题如何解决 2. 做下一层递归的条件如何设置)
    • 合并(Combine): 将子问题得到的解如何归并成原问题的解。

      快速排序(Quick sort)

      基本思想:

      选择一个数组中一个元素作为主元(pivot), 以该主元作为标杆将数组的元素分为大于(或大于等于)主元和小于(或小于等于)主元的两部分(区分的同时也进行位置调换操作)。这时形成的两个子数组在结构形式上和原数组相同,再寻找它们的主元,以此再分。如此不断循环,原数组被分割成许多小数组,当小数组的元素小于等于三个时,按照分割的标准这时已经形成了有序的排列。当这些小数组有序时,合并形成的大数组就是有序的。

      用分治的思想描述(从小到大排序):

      分解: 选择一个主元A[p], 将数组A[p,..r]划分成两个子数组A[p,..q-1]和A[q+1,..r](通过原地操作实现位置调换), 使得A[p,..q-1]全部小于等于A[p]和A[q+1,..r]全部大于等于A[p],下标p也在这个划分过程中计算。

      控制:通过递归调用,对两个子数组排序

      合并:因为两个子数组是就地排序,对他们进行合并不需要操作,整个数组已实现排序。

      复杂度:

      最佳情况:每次划分形成子数组的大小都相同或者相差一个,这时时间复杂度为O(nlgn)

      最坏情况:每次划分形成的子数组大小为1和n-1,这时时间复杂度为Θ(x2)。

      快速排序的C代码如下:

      

     1 void swap(int *a,int *b)
     2 {
     3     int temp;
     4     temp=*a;
     5     *a=*b;
     6     *b=temp;
     7 }
     8 
     9 int partiotion(int A[],int p,int r)
    10 {
    11     int pivot;
    12     int i,j;
    13 
    14     /**   划分数组的数随机产生版本
    15     int randnum;
    16     //使用时间作为随机种子
    17     srand(time(NULL));
    18 
    19     //产生p-r之间的随机数
    20     randnum=rand()%(r-p)+p;
    21 
    22     swap(&A[randnum],&A[r]);
    23     **/
    24     pivot=A[r];
    25 
    26     i=p-1;
    27 
    28     for(j=p;j<r;j++)
    29         if(A[j]<=pivot)
    30         {
    31             i=i+1;
    32             swap(&A[i],&A[j]);
    33         }
    34 
    35     swap(&A[i+1],&A[r]);
    36         
    37     return i;
    38 }
    39 
    40 void quickSort(int A[],int p, int r)
    41 {
    42     int temp;
    43 
    44     if(p<r)
    45     {
    46         temp=partiotion(A,p,r);
    47         //划分成2个子数组,再使用递归调用
    48         quickSort(A,p,temp);
    49         quickSort(A,temp+1,r);
    50     }
    51 
    52 }

      合并排序(Merge Sort):

      基本思想:

      对两个已经排序的数组,合并成一个有序的数组时间复杂度为θ(n)和 n 个额外的存储空间。对一个数组排序,首先将数组划分成单个元素,两两实行合并,再对合并形成的小数组合并,如此不断循环。如图1 实例所示:

    图 1. 合并排序的一个实例(图片来源《算法导论》)

      分治思想(从小到大排序):

      分解:将原数组化分成两个子数组

      控制:将子数组实现合并到一个数组形成有序序列

      合并:采用递归调用时,将上次合并的结果作为这次的输入。由于递归最底层的数组大小为1, 可以确保两个数的合并是有序的。这样每次递归调用返回的结果也是有序的。最终形成的数组自然是有序的。

      复杂度:输入的结果对时间复杂度影响不大,只和排序的规模有关。时间复杂度为Θ(nlgn)。另外需要O(n)个额外的存储空间。

      合并排序的代码如下(从小到大):  

     1 void combine(int A[],int p, int q, int r)
     2 {
     3     
     4     int *temp1;
     5     int *temp2;
     6     int i,j,k;
     7 
     8     temp1=(int *)malloc(sizeof(int)*(q-p+2));
     9     temp2=(int *)malloc(sizeof(int)*(r-q+2));
    10 
    11     //注意子数组的长度
    12     for(i=0;i<q-p;i++)
    13     {
    14         temp1[i]=A[p+i];
    15     }
    16 
    17     //哨兵元素:很巧妙,本来应该设置一个无限大的数,这里只取一个实数
    18     temp1[i]=357834;
    19     
    20     for(j=0;j<r-q+1;j++)
    21     {
    22         temp2[j]=A[q+j];
    23     }
    24 
    25     //哨兵元素
    26     temp2[j]=357834;
    27 
    28     i=0;
    29     j=0;
    30 
    31     for(k=p;k<=r;k++)
    32         if(temp1[i]<temp2[j])
    33         {
    34             A[k]=temp1[i];
    35             i++;
    36         }
    37         else
    38         {
    39             A[k]=temp2[j];
    40             j++;
    41         }
    42 
    43 }
    44 
    45 
    46 
    47 void mergeSort(int A[],int p,int q)
    48 {
    49     if(p<q)
    50     {
    51         int pi=(int)((p+q)/2);
    52         mergeSort(A,p,pi);
    53         mergeSort(A,pi+1,q);
    54 
    55         combine(A,p,pi+1,q);
    56     }
    57 
    58 }

      合并排序和快速排序递归差异:

      合并排序递归是通过将更深一层次递归的结果作为这一次的输入(上浮),而快速排序递归是将这次操作为下次递归做准备,作为下次操作的输入(下沉)。

     

      

  • 相关阅读:
    android scroll 中 scroll Bar 修改
    android 在代码中安装apk的方法
    android JSON 的应用
    android 混淆相关 proguard
    listView 相关的优化设置
    android 名称解释
    android Gallery 两侧阴影实现
    Service 详解
    使用alias 简化命令
    android 动画小结
  • 原文地址:https://www.cnblogs.com/zhengyou/p/3588452.html
Copyright © 2011-2022 走看看