快速排序的几种实现方式
2017-11-30 14:29
302 查看
原理
基准值最左最右双指针
覆盖值标准算法
交换值两头交换法
基准值最左最右单指针遍历两头交换法的另一种实现
简单粗暴的使用数组
以下讨论不同的几种实现方式,都以从小到大排序为准。
如果选取最左边的值为基准值,则需要用比它小的值来放在基准值原来的位置,这个值一定是从右侧开始遍历取到的,因为左边指针遇到比基准值大的停下来,右边指针遇到比基准值小的停下来,所以首先走右边指针,将它指向的值覆盖基准值的位置,然后走左边,遇到比基准值大的数值,覆盖右边指针指的位置,这样依此覆盖和往中间遍历,直到左右指针相遇,这时把基准值放到这个下标位置,完成一轮排序,左边的都比它小,右边的都比它大,具体实现如下:
同理,如果是取右边值为基准值,则先走左边指针:
这里有一个细节,在
首先设定一个下标m,指向S1的起始点,也就是基准值的位置,该下标表示S1区间的结束位置(一开始就是0,然后随着区间变大而增加),然后使用一个下标指针k从m的位置开始依次遍历,遇到比基准值小的,就把m增加一位,然后交换k与m的值,这样逐渐把比基准值小的值都挪到靠近基准值的位置,比基准值大的则不处理,一趟结束后,m左边的都比基准值小,右边都比基准值大,这时交换基准值和m下标上的值,让基准值处于S1和S2的分界处,然后递归排序左边到到m,和m到右边两个区域。具体实现:
也可以拆分成两个函数:
该实现方式来自VISUALGO
这种方式是选取一个基准值,然后新建两个数组,把大于和小于基准值的分别放在两个数组中,递归排序两个数组,然后合并。
基准值最左最右双指针
覆盖值标准算法
交换值两头交换法
基准值最左最右单指针遍历两头交换法的另一种实现
简单粗暴的使用数组
原理
快速排序算法是基于分治思想的,在递归过程中,每次从待排序区间选取一个元素(这个值的位置随意,可以是两端,也可以是中间,也有为了处理基准值而专门写一个方法的)作为基准记录,遍历整个区间,将所有比基准记录小的元素移动到基准记录的左边,而所有比基准记录大的元素移动到基准记录的右边。之后分别对基准记录的左边和右边两个区间进行快速排序,直至将整个区间排序完成。以下讨论不同的几种实现方式,都以从小到大排序为准。
基准值最左/最右+双指针
覆盖值(标准算法)
这种方法将基准值先保存下来,用两个指针分别从左边和右边遍历待排序区间,注意,此处根据基准值在左边或右边的情况不同:如果选取最左边的值为基准值,则需要用比它小的值来放在基准值原来的位置,这个值一定是从右侧开始遍历取到的,因为左边指针遇到比基准值大的停下来,右边指针遇到比基准值小的停下来,所以首先走右边指针,将它指向的值覆盖基准值的位置,然后走左边,遇到比基准值大的数值,覆盖右边指针指的位置,这样依此覆盖和往中间遍历,直到左右指针相遇,这时把基准值放到这个下标位置,完成一轮排序,左边的都比它小,右边的都比它大,具体实现如下:
function quikSort1(arr, left, right) { if (left > right) {return;} var pivot = arr[left]; var l = left, r = right; while (l < r) { while (r > l && arr[r] >= pivot) { r--; } arr[l] = arr[r]; while (l < r && arr[l] <= pivot) { l++; } arr[r] = arr[l]; } arr[r] = pivot; if (left < r) { quikSort1(arr, left, r-1); } if (right > l) { quikSort1(arr, l+1, right); } }
同理,如果是取右边值为基准值,则先走左边指针:
function quikSort1(arr, left, right) { if (left > right) {return;} var pivot = arr[right]; var l = left, r = right; while (l < r) { while (l < r && arr[l] <= pivot) { l++; } arr[r] = arr[l]; while (r > l && arr[r] >= pivot) { r--; } arr[l] = arr[r]; } arr[r] = pivot; if (left < r) { quikSort1(arr, left, r-1); } if (right > l) { quikSort1(arr, l+1, right); } }
这里有一个细节,在
arr[r] = arr[j]之类的覆盖结束后,可以把被覆盖的位置加一或减一即
r--,因为这个位置被覆盖了,可以直接跳过从下一位开始比较。
交换值(两头交换法)
在这种实现方式中,可以使用不断覆盖值,最后把基准值放进来的方法,也有先保存l和r的下标位置,然后直接交换这两个位置数值的方法,在这种情况下,基准值不会被覆盖,也不需要在一轮比较结束后插入,但指针下标在交换后需要各自增长一位。实现如下:function quickSort2(arr, left, right) { var pivot = arr[left], i = left, j = right; // 以最左边的数为基准,i是左zhizhen,往右走,j是右指针,往左走 do{ while (i <= j && arr[i] < pivot) { i++; }// 从左边寻找比基准数大的数 while (i <= j && arr[j] > pivot) { j--; }// 从右边寻找比基准数小的数 if (i <= j) { // 找完了,交换 var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i ++; j --; // 指针继续挪动,然后比较交换,直到指针相遇 } }while(i <= j); // 此时已经分成左右两部分,左边比基准小,右边比基准大 if (left < j) { //对左边部分排序 quickSort2(arr, left, j); } if (i < right) { //对右边部分排序 quickSort2(arr, i, right); } }
基准值最左/最右+单指针遍历(两头交换法的另一种实现)
这种实现方式的思路是,以最左或最右为基准值,假设将待排序区间分为大于小于基准值的两个区间S1和S2,遍历整个区间把数值划分到这两个区域中。首先设定一个下标m,指向S1的起始点,也就是基准值的位置,该下标表示S1区间的结束位置(一开始就是0,然后随着区间变大而增加),然后使用一个下标指针k从m的位置开始依次遍历,遇到比基准值小的,就把m增加一位,然后交换k与m的值,这样逐渐把比基准值小的值都挪到靠近基准值的位置,比基准值大的则不处理,一趟结束后,m左边的都比基准值小,右边都比基准值大,这时交换基准值和m下标上的值,让基准值处于S1和S2的分界处,然后递归排序左边到到m,和m到右边两个区域。具体实现:
function quickSort4(arr, left, right) { var pivot = arr[left]; // p 为基准值 var m = left; // 用S1 and S2 表示基准值两边两个空的区域,m表示排好序区域最右侧的位置下标 for (var k = left+1; k <= right; k++) { // 从第二位开始遍历后面的值 if (arr[k] < pivot) { // 比基准值小 m++; // S1区域增加一个元素 // 交换该值和存储指数值 var temp = arr[k]; arr[k] = arr[m]; arr[m] = temp; } // 对大于基准值的不做处理 } // 交换m位置的值和基准值(m此时在S1和S2交界处) var temp = arr[left]; arr[left] = arr[m]; arr[m] = temp; if(left< right) { quickSort4(arr, left, m-1); quickSort4(arr, m+1, right); } }
也可以拆分成两个函数:
function partition(arr, left, right) { var pivot = arr[left]; // 基准值 var m = left; for (var k = left+1; k <= right; k++) { if (arr[k] < pivot) { // 比基准值小 m++; // 存储指数+1 var temp = arr[k]; arr[k] = arr[m]; arr[m] = temp; } // notice that we do nothing in case 1: arr[k] >= p } var temp = arr[left]; arr[left] = arr[m]; arr[m] = temp; return m; // 返回存储指数位置,用于快速排序 } function quickSort3(arr, left, right) { if (left < right) { var pivotIdx = partition(arr, left, right); // O(N) // a[left..right] ~> a[left..pivotIdx–1], pivot, a[pivotIdx+1..right] quickSort3(arr, left, pivotIdx-1); // recursively sort left subarray // a[pivotIdx] = pivot is already sorted after partition quickSort3(arr, pivotIdx+1, right); // then sort right subarray } }
该实现方式来自VISUALGO
简单粗暴的使用数组
来自快速排序(Quicksort)的Javascript实现这种方式是选取一个基准值,然后新建两个数组,把大于和小于基准值的分别放在两个数组中,递归排序两个数组,然后合并。
var quickSort = function(arr) { if (arr.length <= 1) { return arr; } var pivotIndex = Math.floor(arr.length / 2); var pivot = arr.splice(pivotIndex, 1)[0]; var left = []; var right = []; for (var i = 0; i < arr.length; i++){ if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return quickSort(left).concat([pivot], quickSort(right)); };
相关文章推荐
- 快速排序的三种实现方式以及非递归版本
- 关于java sort的几种实现方式(单纯排序,按照bean的某一个字段,按照bean的多个字段)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 数组排序几种实现方式
- C++实现几种常用的时间复杂度为O(nlogn)的排序方法:归并排序、快速排序、堆排序、希尔排序
- Nagios 快速实现数据可视化的几种方式
- 元素排序几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- java几种排序简单实现(快速排序,冒泡排序,直接插入排序)
- 几种基本的排序算法(选择排序,冒泡排序,快速排序,归并排序,希尔排序)C语言实现
- Nagios 快速实现数据可视化的几种方式
- 几种常见排序算法之Java实现(插入排序、希尔排序、冒泡排序、快速排序、选择排序、归并排序)
- 排序都有哪几种方法?请列举。用JAVA实现一个快速排序。
- 4、 排序有哪几种方法?请列举。并用JAVA实现一个快速排序.
- 几种排序算法的C++实现——快速排序、堆排序、基数排序
- 快速排序的两种实现方式(Java)
- 快速排序两种实现方式
- 快速排序一种易于理解的方式实现
- 快速排序两种方式实现及优化总结
- 快速排序的三种不同的实现方式。