js 实现快速排序
// 快速排序 // 快速排序基本思路, 通过遍历,找到一个基准值,遍历一趟后,将所有比基准值小的放在基准值左边,比基准值大的放在基准值右边,这时候基准值就是一个排好序的数,再将基准值两边的数组分别调用此方法递归,最终生成有序数列 // 1.左右指针交换法 // 左右指针对向前进,当遇到左侧指针所指的数小于等于基准值,或右侧指针指向的数大于基准值,则循环会停止,交换左右指针对应的的数,否则指针继续向前,最后两个指针会指向同一个位置,将基准值与重合位置交换 // 单趟排序的思路是:取区间中最左或最右边的元素为key,定义两个变量,这里假设是p和q,q从区间的最右边向左走,找到比key小的元素就停下。p从最左边向右走,找到比key大的元素就停下。然后交换p和q所指向的元素。 function quickSort1(arr) { let tmpArr = [...arr]; //复制数组 return quick1(tmpArr, 0, tmpArr.length - 1); } // 默认左指针从 0 开始,右指针从尾部开始 function quick1(arr, i, j) { // 当左右指针索引值相减<=0,说明数组只有一个值了 if (j - i <= 0) return; let left = i; // 定义左指针 let right = j; // 定义右指针 let base = left; // 以左边第一个为基准值的索引值 let center = arr[base]; // 定义基准值 // 循环条件,就是左指针小于右指针,当等于时,即停止 while (left < right) { // 如果右侧指针指向的数小于基准值,则停止循环,否则继续向左移动一位 while (left < right && center < arr[right]) { // 右侧指针继续向左移动一位 right--; } // 如果左侧指针指向的数大于基准值,则停止循环,否则继续向右移动一位 // 注意,左右指针遍历条件中至少有一个写=,否则会陷入死循环 while (left < right && center >= arr[left]) { left++; } // 当上面两个循环停止,此时交换满足左指针指向的元素大于基准值,右指针指向的元素小于基准值,则将左右元素交换 let temp = arr[right]; arr[right] = arr[left]; arr[left] = temp; } // 整体遍历结束后,说明 left=right,这时候左右指针相遇,则相遇的位置即为基准值应该被排好序的位置,这时候将基准值与相遇位置的元素进行交换 let temp = arr[base]; arr[base] = arr[left]; arr[left] = temp; // 递归,这时候相遇位置的索引等于 left=right //分别递归左右两侧 // left=right 位置已经是排好序的了 quick1(arr, i, left - 1); quick1(arr, left + 1, j); return arr; } console.log("快速排序-左右指针交换法"); console.log(quickSort1([6, 3, 7, 8, 2, 4, 0, 1, 6, 5])); // 2.挖坑法,每次把坑位与指针位置交换 // 取最左或最右边的元素为key,假设把这个位置“挖空”,让最右边的q向左走,直到遇到比key小的数,将其放到key的位置,自己的位置变“空”。直到pq相遇,那么这个位置就是最终的坑位,再将key填入这个坑位,就完成了一趟排序。 function quickSort2(arr) { let tmpArr = [...arr]; //复制数组 return quick2(tmpArr, 0, tmpArr.length - 1); } function quick2(arr, i, j) { if (j - i <= 0) return; //说明数组只有一个值了 let left = i; let right = j; let base = left; //以左边第一个为基准值 let center = arr[base]; while (left < right) { // 如果右侧指针指向的数小于基准值,则停止循环,否则继续向左移动一位 while (left < right && center < arr[right]) { right--; } if (left < right) { // 右指针找到了比基准值小的值,与基准值交换位置 arr[base] = arr[right]; arr[right] = center; base = right; // 左指针继续向前 left++; } // 如果左侧指针指向的数大于基准值,则停止循环,否则继续向右移动一位 while (left < right && center > arr[left]) { left++; } if (left < right) { // 左指针找到了比基准值大的值,与基准值交换位置 arr[base] = arr[left]; arr[left] = center; base = left; // 右指针继续向左 right--; } } quick2(arr, i, base - 1); //分别处理左右两侧 quick2(arr, base + 1, j); return arr; } console.log("快速排序-挖坑法"); console.log(quickSort2([6, 3, 7, 8, 2, 4, 0, 1, 6, 5])); // 3.额外定义两个数组实现,最简单,空间复杂度最高 function quickSort3(arr) { if (arr.length <= 1) return arr; const num = arr[0]; let left = [], right = []; for (let i = 1; i < arr.length; i++) { if (arr[i] <= num) { left.push(arr[i]); } else { right.push(arr[i]); } } return quickSort3(left).concat([num], quickSort3(right)); } console.log("快速排序-创建数组法"); console.log(quickSort3([6, 3, 7, 8, 2, 4, 0, 1, 6, 5])); console.log(quickSort3([1, 2, 3, 4, 5, 6, 7, 8, 9, 9])); // 4.快慢指针法 // 取最左边的数为key,定义两个快慢指针,都从key的下一位往右走,fast每走一步判断一下它指向的元素是否小于key,若小于则交换fast和slow位置的元素,并且让slow向前走,直到fast走到底,结束循环。最后让slow和key位置的值交换。再返回key的位置。
参考链接:
https://www.imooc.com/article/73247
https://blog.csdn.net/xinxxxxxxxxx/article/details/123032933