分治算法应用举例_二分检索、快速排序、归并排序

二分检索

基本思想

通过 x 与中位数的比较,将原问题归结为规模减半的子问题,如果 x 小于中位数,则子问题由小于 x 的数构成,否则子问题由大于 x 的数构成。

步骤

  • 假设表中元素是按升序排列
  • 将表中间位置记录的关键字与检索关键字比较,如果两者相等,则检索成功;
  • 否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于检索关键字,则进一步检索前一子表,否则进一步检索后一子表。
  • 重复以上过程,直到找到满足条件的记录,检索成功;或直到子表不存在为止,此时检索不成功。

优点

  • 比较次数少。
  • 检索速度快。
  • 平均性能好。

缺点

  • 待查表为有序表。
  • 待查表必须采用顺序存储结构。
  • 插入删除困难。
  • 只适用于不经常变动而检索频繁的有序列表。

时间复杂度分析

二分检索法充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。

代码实现

#include <stdio.h>

int BinSearch(int key,int a[],int n)
{
    int left,right,mid;
    left=0;
    right=n-1;
    while(left<=right)
    {
        mid=(left+right)/2;
        if(key<a[mid]) //key小于中间值时
            right=mid-1;//确定左子表范围
        if(key>a[mid]) //key 大于中间值时
            left=mid+1;//确定右子表范围
        if(key==a[mid])//当key等于中间值时,证明查找成功
        {
            printf("查找成功!  a[%d]=%d\n",mid,a[mid]);
            break;
        }
    }
    if(left > right)
        printf("查找失败!\n");
    return 0;
}

int main()
{
    int key = 5;
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    BinSearch(key,a,10);
    return 0;
}

快速排序

基本思想
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此让整个数据变成有序序列。

步骤

  1. 设定一个分界值(通常选用数组的第一个数)
    通过该分界值将数组分成左右两部分。
  2. 分组
    将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。
  3. 左边和右边的数据可以独立排序
    对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据做类似处理。
  4. 重复上述过程
    递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

算法步骤

扫描二维码关注公众号,回复: 9521841 查看本文章
  1. 设置两个变量i、j,排序开始时:i=0,j=N-1;
  2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
  3. 从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
  4. 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
  5. 重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束。

代码实现

#include <stdio.h>
void Qsort(int *a, int left, int right)
{
    if(left >= right)return;
    int i = left;
    int j = right;
    int key = a[left];
    while(i < j)  /*控制在当组内寻找一遍*/
    {                
        while(i < j && key <= a[j])
        {
            j--;/*向前寻找*/
        }
        a[i] = a[j];
        while(i < j && key >= a[i])
        {
            i++;
        }
        a[j] = a[i];
    }
    a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
    Qsort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
    Qsort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
}


int main()
{
    int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24};
    Qsort(a, 0, sizeof(a) / sizeof(a[0]));
    for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        printf("%d ",a[i]);
    }
    return 0;
}

效率分析

  • 快速排序算法的时间复杂度与划分的趟数有关。
  • 最好情况下,每次划分所选择的中间数恰好将当前序列几乎等分,时间复杂度为O(nlogn)。
  • 最坏情况下,每次所选的中间数是当前序列中的最大或最小元素,时间复杂度为O(n2)。
  • 为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(10w+high)/2].key,取三者中关键字为中值的元素为中间数。
  • 平均时间复杂度是O(nlogn)。因此,该排序方法被认为是目前最好的一种内部排序方法。
  • 从空间性能上看,快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。
  • 最好情况下,所需栈的最大深度为log2(n+1)。
  • 最坏情况下,所需栈的最大深度为n。
  • 空间复杂度为O(logn))。
  • 快速排序是一种不稳定的排序算法。

归并排序

基本思想

  • 划分
    将原问题归结为规模为n/2 的2 个子问题,继续划分,将原问题归结为规模为n/4 的4 个子问题. 继续…,当子问题规模为1 时,划分结束。
  • 归并
    从规模1到n/2,陆续归并被排好序的两个子数组。每归并一次,数组规模扩大一倍,直到原始数组。
发布了77 篇原创文章 · 获赞 134 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43239560/article/details/104591535