zoukankan      html  css  js  c++  java
  • 算法学习笔记(一)《快速排序》

    写在前面

    本文学习内容来自《算法图解》一书及百度百科内容

    算法是一组完成任务的指令。任何代码片段都可视为算法。 —— 摘自《算法图解》1


    快速排序

    以下内容摘自百度百科
    快速排序(Quicksort)是对冒泡排序的一种改进。
    快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

    实现思路

    分而治之(divide and conquer, D&C)一种著名的递归式问题解决方法。

    这里有个问题,请读者思考一下

    假如你是一个农场主,现在你有一块土地,想要给它均匀的分成大小相同的方块。
    这里写图片描述

    如果要你来均匀的分开成一个个小方块你会怎么做? 像下面这样?

    这里写图片描述

    显然这是愚蠢的,也是不可能的。那我们应该怎么样去做呢?
    这里就用到了我们上面提到的D&C策略 ,D&C策略是使用递归的,解决这个分成两个步骤:

    1. 找到基准条件,这个条件,必须尽可能的简单。
    2. 竟可能的缩短范围找到基准,将给定条件不断缩小范围。

    1、我们先按照给定的宽度 640m 将长切成 ( 640m + 640m + 400m ) × 640m 大小的三块方块

    这里写图片描述
    这样我们就得到了一个 400m × 640m 的小方块
    这里写图片描述

    2、再按照 400m × 400m 分成 2 块

    640 m × 400 m的土地,可从中划出的最大方块为400 m × 400 m。 这样就会余下一个 400m × 240m 的一个方块。

    这里写图片描述

    3、我们在把余出的 400m × 240m 切成 240m × 240m + 240m × 160m

    这里写图片描述

    4、以此类推

    这里写图片描述这里写图片描述

    最终我们找到了想要的小方块 80m × 80m 这样我们就把最初的 1680m × 640m 分成了 1680 个 80m × 80m 的小方块

    以上讲述的只是一种解决问题的思路,也是D&C策略的具体体现,那我们现在回顾一下解决问题的过程。

    1. 我们拿到问题之后,是想要找到问题的基准条件。
    2. 紧接着我们把适应问题条件的范围不断的缩小。
    3. 在更小的范围去寻找我们想要的答案。

    其实我们在解决这个问题的时候,已经用到了一个算法,那就是《欧几里德算法》,具体大家可以查看百度百科即可


    实际例子

    说了这么多,那如果是实际的问题我们该如何解决呢?

    • 快速排序说的是怎么去给一个数组,或者列表进行一个有序的排列。
    // 如果我们有这样一个数组
    int[] ints = {1};
    // 那我们说,这是不需要排序的,直接返回结果就可以
    [1]

    // 如果是有两个值呢
    int[] ints = {1, 2};
    // 这个也很简单,只需要比较两个数值的大小即可
    [1, 2]

    // 如果是有三个值呢
    int[] ints = {1, 2, 0};
    [?, ?, ?]
    // 如果是有多个值呢
    int[] ints = {1, 23, 12, 34, 87, 45, 68, 15};
    [?, ?, ?, ?, ?, ?, ?, ?]

    同样我们使用的也是D&C策略来解决这个问题,首先要找到一个基准条件。那我们说这个条件怎么去找?

    先别急,我们来看最后一个数组

    int[] ints = {1, 23, 12, 34, 87, 45, 68, 15};

    首先我们要做的事情很简单,找到一个基准条件,缩小我们的排序范围。无论多少个值,我们只进行两个值得比较。

    int temp = ints[0]

    那我们说,temp 就是我们要的小范围的一个基准条件,所有的数字和这个基准条件去比较。那就变成了两个值得比较。

    
    ints [1] = 23; // 大于temp
    ints [2] = 12; // 大于temp
    ints [3] = 34; // 大于temp
    ...
    ...

    比较的之后的值怎么办 ,我们把比较的值分成两组,要么大于基准值,要么小于等于基准值。大于的,我们放到基准值的右侧,小于等于的,我们放到基准值的左侧。并且,如果出现位置转换的时候,我们的基准值得位置是在跟着变动的。我们比较的顺序是从数组的第一个和数组最后一个进行比较。依次像中间靠拢。

    /*
     * 我们如果拿数组的第一个数字进行比较,那数组将会被分成两个部分
     * 要么大于1,要么小于等于1 
     * 结果数组会变成这个样子
     * [1, 23, 12, 34, 87, 45, 68, 15]
     * /

    看过之后你可能会有疑虑,这什么啊?怎么没有变化呢?是的,因为我们选取的基准值1,大于它的放到右侧,因为其它的数字全部大于1,所以我们放到了右侧。接下来再看。

    现在我们可以理解为,原来数组被拆成了两个数组
    由原来的[1, 23, 12, 34, 87, 45, 68, 15]
    变成了现在的[][23, 12, 34, 87, 45, 68, 15]

    其实看似没有变化,实则我们已经缩小了排序的范围,再一次我们需要对两个数组进行之前的方法进行计算
    数组 [23, 12, 34, 87, 45, 68, 15]进行我们接下来的选取基准值,进行比较分开大小。因为我们的基准值选取的是23,所以 12 15 在 23 的左侧,其余的在 23 右侧,那具体的结果是什么样子的呢?

    同样的我们的数组被[23]分隔成了两部分
    分成了[15, 12] [87, 45, 68, 34]


    快速排序实现原理
    可能有的同学看到这里会有疑问,为什么 15 在 12 的左侧,这就是上面我们提到的比较之后改变数字位置的问题,我们开始比较的时候是 23 和 15 去比较,显然 15 比 23 小,所以它要放到 23 的左侧,那此时的 23 将会和 15 的位置互换,这样就达到了小的数字在基准值的左侧,当我们在拿基准值 23 和 左侧的数字比较的时候,23 比 12 大,保持不变,依次 23 比 34 小,此时由要进行位置互调。23 出现在了 34 的位置上,34 则在了 23 的位置上。重点来了,基准条件在做完一次基准值之后,不会再次参与后续排序。


    此时我们又要对拆分出的数组进行比较

    [15, 12] 用同样的基准值办法进行比较显然变成了[12, 15]
    [87, 45, 68, 34] 则变成了 [34, 45, 68, 87]

    以此类推,我们的数组对应的变化如下:

    [1, 23, 12, 34, 87, 45, 68, 15]
    [1, 15, 12, 23, 87, 45, 68, 34]
    [1, 12, 15, 23, 87, 45, 68, 34]
    [1, 12, 15, 23, 34, 45, 68, 87]
    [1, 12, 15, 23, 34, 45, 68, 87]
    [1, 12, 15, 23, 34, 45, 68, 87]

    相信看到现在,多多少少大家已经掌握了快速排序法的原理。

    1. 找到一个基准值
    2. 将其与数字与该基准值进行比较
    3. 比较成功后按大小,改变数字位置,基准值被隔离开来,用来连接数组。(这里比较抽象,需要一定的自行脑补)
    4. 对重新组成的数组再次进行 1 2 3 步骤

    为了大家学习和调试方便,下面提供了快速排序的 java 和 python 的代码

    Java

        public static void main(String args[]) {
            int[] ints = {1, 23, 12, 34, 87, 45, 68, 15};
            quickSort(ints, 0, ints.length - 1);
        }
        private static void quickSort(int[] ints, int low, int high) {
            int h = high;
            int l = low;
            if (l < h) {
                int temp = ints[l];
                while (l < h) {
                    while (l < h && ints[h] > temp) {
                        h--;
                    }
                    ints[l] = ints[h];
                    while (l < h && ints[l] <= temp) {
                        l++;
                    }
                    ints[h] = ints[l];
                }
                ints[l] = temp;
                System.out.println(Arrays.toString(ints));
                quickSort(ints, low, h - 1);
                quickSort(ints, l+ 1, high);
            }
        }
    [1, 23, 12, 34, 87, 45, 68, 15]
    [1, 15, 12, 23, 87, 45, 68, 34]
    [1, 12, 15, 23, 87, 45, 68, 34]
    [1, 12, 15, 23, 34, 45, 68, 87]
    [1, 12, 15, 23, 34, 45, 68, 87]
    [1, 12, 15, 23, 34, 45, 68, 87]

    python

    
    def quickSort(array):
        if len(array) < 2:
            return array
        else:
            pivot = array[0]
            less = [i for i in array[1:] if i <= pivot]
            greater = [i for i in array[1:] if i > pivot]
            return quickSort(less) + [pivot] + quickSort(greater)
    
    
    print(quickSort([1, 23, 12, 34, 87, 45, 68, 15]))
  • 相关阅读:
    UVA 11776
    NEFU 117
    hihocoder 1331
    NEFU 84
    虚拟机类加载机制
    动态规划 -- 01背包问题和完全背包问题
    动态规划 ---- 最长回文子串
    动态规划 ---- 最长公共子序列(Longest Common Subsequence, LCS)
    动态规划 ---- 最长不下降子序列(Longest Increasing Sequence, LIS)
    动态规划(Dynamic Programming, DP)---- 最大连续子序列和 & 力扣53. 最大子序和
  • 原文地址:https://www.cnblogs.com/lvgo/p/13275855.html
Copyright © 2011-2022 走看看