分治法——快速排序,归并排序

原创-转载请注明出处。

分治法

分治法是一种很重要的算法,也就是“分而治之”的意思,就是把一个复杂的问题分解成两个或者多个相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

比如二分搜索算法,排序算法中的快速排序和归并排序都属于分治法的一种。下面我们来看看归并排序和快速排序算法的实现。

归并排序

简介

维基百科

归并排序(Merge sort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。最优时间复杂度,O(n),平均时间复杂度为O(n log n)。由上图我们可以了解到归并排序的过程。

实例分析

以数组6 5 3 1 8 7 2 4为例。首先递归的将数组一分为2,并对子数组排序

 [6, 5, 3, 1]  [8, 7, 2, 4]
 
 [6, 5]  [3, 1]  [8, 7]  [2, 4]
                        
 [6], [5]  [4], [3]  [7], [8]  [2], [4]

然后向上回溯,将两个数组合并成有序数组

[6], [5]  [4], [3]  [7], [8]  [2], [4]
               
[5, 6]  [3, 4]  [7, 8]  [2, 4]
            
[3, 4, 5, 6] [2, 4, 7, 8]           
            
[1, 2, 3, 4, 5, 6, 7, 8]

动图如下所示

维基百科

两个有序数组排序

    /**
     *
     * @param a 有序数组a
     * @param b 有序数组b
     * @param result 结果数组
     */
    public static void merge2(int[] a,int [] b, int[] result){

        int i = 0 , j = 0 , k = 0 ;
        while (i < a.length && j < b.length){
            if (a[i] < b[j]){
                result[k++] = a[i++];
            }else {
                result[k++] = b[j++];
            }
        }

        while (i < a.length){
            result[k++] = a[i++];
        }

        while (j < b.length){
            result[k++] = b[j++];
        }
        print(result);
    }

看会了两个有序数组的排序,则知道了如何实现归并排序

Java代码实现

private static void merge(int[] arr, int[] result, int start, int end) {

        if (start >= end) return;

        int center = (start + end) / 2;
        int start1 = start, end1 = center;
        int start2 = center + 1, end2 = end;
        merge(arr, result, start1, end1);
        merge(arr, result, start2, end2);
        int k = start1;
        while (start1 <= end1 && start2 <= end2) {
            if (arr[start1] < arr[start2]) {
                result[k++] = arr[start1++];
            } else {
                result[k++] = arr[start2++];
            }
        }
        while (start1 <= end1) {
            result[k++] = arr[start1++];
        }

        while (start2 <= end2) {
            result[k++] = arr[start2++];
        }
        for (k = start; k <= end; k++) {
            arr[k] = result[k];
        }
        print(arr);

    }

快速排序

简介

使用快速排序法对一列数字进行排序的过程——维基百科

快速排序(Quicksort),是一种排序算法,最坏情况复杂度:Ο(n2),最优时间复杂度:Ο(n log n),平均时间复杂度:Ο(n log n)。快速排序的基本思想也是用了分治法的思想:找出一个元素X,在一趟排序中,使X左边的数都比X小,X右边的数都比X要大。然后再分别对X左边的数组和X右边的数组进行排序,直到数组不能分割为止。

具体操作

ok,我们来看一下具体操作:

1.设置一个长度为n的数组A,定义两个变量i = 0,j = n - 1;
2.从数组中挑选出一个元素作为基准元素,复制给key;
3.从j开始从后向前搜索,j--,找到比key小的值,将A[j]与A[i]互换;
4.从i 开始向后搜索,i++,找到比key大的值,将A[i]与A[j]互换;
5.递归的,重复2,3,4步,直到i == j ;

举个栗子:

  • 存在一个数组A:6 2 7 3 8 9 ,创建i = 0 ; j = 5,选择一个基准元素 k = 6
  • j 从右向左查找比k小的元素,发现,当 j = 3 时,发现元素3比k小,则另A[i] 与 A[j]交换,得到3 2 7 6 8 9;
  • i 从左向右进行查找,当 i = 2时,发现元素 7 比k大,则另A[i] 与A[j]进行交换,得到 3 2 6 7 8 9;
  • 接着,再减小j,重复上面的循环。
  • 但是我们发现,在本例中,一次循环后j与i就相等了,他们的下标同时指向了2.这时候,我们就进行分组,将3 2分为一组,7 8 9分为一组继续上述的比较,最终得到排序好的数组。

Java代码实现

public static void quickSort(int[] arr, int start, int end) {
        if (start >= end)
            return;

        int mid = arr[end];
        int left = start;
        int right = end - 1;

        while (left < right) {
            while (arr[left] <= mid && left < right) {
                left++;
            }
            while (arr[right] >= mid && left < right) {
                right--;
            }
            swap(arr, left, right);
        }

        if (arr[left] >= arr[end]) {
            swap(arr, left, end);
        } else {
            left++;
        }
        quickSort(arr, start, left - 1);
        quickSort(arr, left + 1, end);

        print(arr);
    }
    
    
    private static void swap(int[] arr, int x, int y) {
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }

总结

可以看出分治法的策略还是递归的去解决问题,基本分为三个步骤:

分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

合并:将各个子问题的解合并为原问题的解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 归并排序和快速排序都用到了分治思想,非常巧妙。我们可以借鉴这个思想,来解决非排序的问题。 归并排序 归并排序的核心...
    被吹落的风阅读 1,313评论 0 3
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,282评论 0 2
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,149评论 0 52
  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an 输...
    code武阅读 643评论 0 0
  • 香山_周江阅读 221评论 0 0