数据结构与算法-----快速排序

  快速排序的原理是,每一次排序都找一个基准数,然后比基准数大的元素,放到基准数的右侧,比基准数小的元素放到基准的左侧,那么最核心的就是找到基准数的位置, 把基准数放到它应在的位置。现在我们对6 1 2 7 9 3 4 5 10 8 进行排序。

   首先找一个基准数,就是一个参照数, 用来进行比较。 为了简单起见,那就选第一个数6作为基准数好了,我们要做的事就是找到6 所在的位置,让6右边的数比它大,让6左边的数比它小 如下形式。

  3 1 2 5 4 6 7 9 10 8 

  我们怎么才能找到6 应该在的位置呢? 方法很简单,那就是从序列3 1 2 5 4 6 7 9 10 8 的两端进行探测。先从序列的右侧进行查找, 只要找到一个比基准数6大的数就停下来,然后再从序列的左边进行查找,只要找到一个比基准数6小的数就停止,这时交换它们两个的位置。 这时我们可以用两个变量i 和 j 分别指向序列的最左侧和最右侧。刚开始的时候,变量i 指向序列的最左边,就是数字6, j指向序列的最右边,指向数字8, 如下图所示

 

  首先j 从右向左移动,只要找到比6小的数就停止下来,j到了5的位置。这时i 再从左向右出发, 只要找到比6大的数就停止下来,所以它到了7的位置

  这时交换这两个数的位置

 

  第一次交换结束,序列变成了 6, 1, 2, 5, 9 , 3 5, 7, 10, 8.  数字5 和数字7进行了交换。现在j 接着向左走,到了4的位置就停下来,因为4也比6小。这时i 接着从左向右走,到了9的位置,9比6 大。

  然后再交换它们的位置

  又一次的交换结束了,我们的序列变成了 6 1 2 5 4 3 9 7 10 8 。 现在接着继续探测, j 接着向左走,到了3的位置就停止了,它比6小。 接着i向右走,突然发现i和j 相遇了,都到了3的位置上,表明探测结束了。

  基准数6的位置应该在3的位置上,我们来交换3和6位置

  到这时我们终于找到的6的位置,比6大的数在它的右侧,比6小的数在它的左侧。

  现在用代码把上面的步骤实现一下,我们有10个数字,用数组给它们存起来是最好不过了。我们要声明一个变量let arr = [6, 1, 2, ..]; 其次声明一个函数quickSort 用于快速排序。在函数内部声明4个变量:base 用于保存基准数, 还有上面的进行移动的i, j. 进行交换的变量t. 我们首先要做的是让i, j 分别指向数组的头部或末尾, 头部就是0, 但是末尾要传入进来,所以函数需要两个参数left, right. 现在要做的就是移动和比较,移动和比较的过程肯定是循环,而循环的结束条件是 i 和 j 相等, 只要i 和j 不相等就要循环。

        let arr = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8];
        // 函数接受两个参数left, right, 指定排序时进行移动的 i, j 的位置
        function quickSort(left, right) {
            let base;   // 基准数变量
            let i, j;   // 进行移动的变量,寻找值
            let t;      // 用于进行交换
            base = arr[left];   // 序列最左边的数设为基准数
            i = left;           // 把序列最左边的数赋给i
            j = right;          // 把序列最右边的数赋值给j
            // 只要i 和 j 不相等就要循环
            while (i !== j) {

                // 右侧的j 向左移动,找到一个比基准数小的值就停止
                while (arr[j] >= base && i < j) {
                    j--;
                }
                // 左侧的 i 向右移动,找到一个比基准数大的值就停止
                while (arr[i] <= base && i < j) {
                    i++;
                }
                // 当 i 和j 都找到值以后,交换它们两个位置,当然 i < j 时,如果i等于j,则是基准数的交换了 
                if (i < j) {
                    t = arr[i];
                    arr[i] = arr[j];
                    arr[j] = t;
                }
            }

            // 当循环结束后,我们发现基准数应有的位置, 就是i的位置
            arr[left] = arr[i];
            arr[i] = base;
        }

  现在我们找到了基准数的位置,同时也把原来的序列分成了两个序列, 左边的序列 3 1 2 5 4; 右边的序列9 7 10 8; 可以分别对两个序列进行排序。我们还是采用找基准数的方法进行排序,这时对于左边的序列我们还是把第一个数3作为基准数,然后把序列的最左侧的值3赋给i, 最右边的数4赋值给j,  进行移动。 j 是从右向左移动, 到了2的位置,这时i 再向右移动,i 和 j 重合, 把3,2 位置进行交换 2 1 3 5 4.   

  现在3 又把序列分为了两个部分,2 1 和 5 4,  现在再把2 1 找基准数的方式进行排列。2 作为基准数,然后把序列2,1 最左边的数2赋值 i ,最右边的数1 赋值给j 进行移到,j 移动到了2的位置,i 本来就在2, 交换,变了1, 2. 这时按基准数2 进行序列分割, 但右边只有1了。最左侧的数和最右侧的数相等了,所以就不用进行排序了。右边的序列进行排序,也是这个逻辑。

  你会发现,我们所做的和上面的代码是一模一样的事情,只是序列更短了,直到只有一个数值,我们仍然可以调用quickSort, 这就是一个递归,递归结束的条件就是序列只有 一个数。

let arr = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8];
// 函数接受两个参数left, right, 指定排序时进行移动的 i, j 的位置
function quickSort(left, right) {
    let base;   // 基准数变量
    let i, j;   // 进行移动的变量,寻找值
    let t;      // 用于进行交换
    base = arr[left];   // 序列最左边的数设为基准数
    i = left;           // 把序列最左边的数赋给i
    j = right;          // 把序列最右边的数赋值给j
    console.log(left, right);
    // 这是后面的递归的结束的条件, 因为序列只有一个数的时候,就不用排序了,对于左侧的序列 left =0, i -1 = -1
    if (left > right) {
        return;
    }

    // 只要i 和 j 不相等就要循环
    while (i !== j) {

        // 右侧的j 向左移动,找到一个比基准数小的值就停止
        while (arr[j] >= base && i < j) {
            j--;
        }
        // 左侧的 i 向右移动,找到一个比基准数大的值就停止
        while (arr[i] <= base && i < j) {
            i++;
        }
        // 当 i 和j 都找到值以后,交换它们两个位置,当然 i < j 时,如果i等于j,则是基准数的交换了 
        if (i < j) {
            t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    // 当循环结束后,我们发现基准数应有的位置, 就是i的位置
    arr[left] = arr[i];
    arr[i] = base;

    quickSort(left, i - 1);   // 继续对左边的序列进行排序, i就是基准数6的位置,它不用排列,所以要减1
    quickSort(i + 1, right);  // 对右边的序列进行排序, 要 i + 1;
}

quickSort(0, arr.length - 1); // 调用排序函数

console.log(arr);            // 输出结果

 

posted @ 2017-12-23 09:34  SamWeb  阅读(537)  评论(0编辑  收藏  举报