数据结构与算法-----快速排序
快速排序的原理是,每一次排序都找一个基准数,然后比基准数大的元素,放到基准数的右侧,比基准数小的元素放到基准的左侧,那么最核心的就是找到基准数的位置, 把基准数放到它应在的位置。现在我们对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); // 输出结果