冒泡排序、选择排序、快速排序、插入排序思路及代码实现……
一 冒泡排序
冒泡排序的思路是,对所有相邻的值进行比较,如果符合条件,则将两者交换位置,最终达到有序化。
示例步骤:
- 比较相邻的元素,如果前一个比后一个大(升序排列),交换位置。
- 对每一对相邻的元素进行重复工作,每步完成后,最大元素出现在末尾。
- 重复上两步,直到数组有序。
冒泡排序的基本思路:
// 一个乱序的数组
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
// 冒泡排序方法
function bubbling (arr) {
const len = arr.length;
// 从头开始遍历数组
for(let i = 0; i < len; i++) {
// 比较相邻元素
for(let j = 0; j < len - 1; j++) {
// 将数组按照升序排列,如果前一个元素大于后一个元素,则交换位置
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
冒泡排序的优化:
// 一个乱序的数组
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
// 冒泡排序方法
function bubbling (arr) {
const len = arr.length;
let flag = true; // 标志位
// 标志位为 true 时执行循环,避免排好序之后继续循环
for(let i = 0; i < len && flag; i++) {
flag = false;
// j < len - 1 - i, 每次循环后,末位已排好的不再继续循环
for(let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
// 如果有交换的情况,说明数组不是有序的,继续循环
flag = true;
}
}
}
return arr;
}
二 选择排序
选择排序的思路是,在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
示例步骤:
- 在乱序数组中查找到最小元素(升序),存放到起始位置。
- 在剩余元素内找到最小元素,存放到已排序元素的后面。
- 重复上述步骤。
选择排序的基本思路:
// 乱序数组
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
// 选择排序方法
function choose (arr) {
// 最小元素的坐标
let minIndex;
const len = arr.length;
for(let i = 0; i < len - 1; i ++){
minIndex = i;
// 遍历查找。如果存在更小元素,则替换最小元素坐标
for(let j = i + 1; j < len; j ++){
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
// 找到了最小坐标,替换到乱序数组头部
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
return arr;
}
选择排序的优化:
/**
* 双指针,同时处理最大值和最小值
*/
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
function choose (arr) {
// 左指针,从左到右开始遍历
let left = 0;
// 右指针,从右到左开始遍历
let right = arr.length - 1;
// 最大值
let max;
// 最小值
let min;
while (left < right) {
max = left;
min = left;
for(let i = left + 1; i < arr.length - left; i++){
// 同时寻找最小值和最大值坐标
if(arr[i] > arr[max]){
max = i;
}
if(arr[i] < arr[min]){
min = i;
}
}
// 如果找到的最大值不是末尾元素,则将最大值交换到末尾
if(max !== right){
swap(arr, max, right);
}
if(min === right){
min = max;
}
// 如果找到的最小值不是头部元素,则将最小值交换到头部
if(min !== left){
swap(arr, min, left);
}
left ++;
right --;
}
return arr;
}
function swap (arr, left, right) {
[arr[left], arr[right]] = [arr[right], arr[left]]
}
三 快速排序
快速排序的思路是,从数列中挑出一个元素,以该元素为基准将数组分为两个部分,把小于基准值的数据集中到数组的左边(升序排列),把大于基准值的数据集中到数组的右边;数组的左边和右边重复上边的步骤,直到数组有序。
快速排序的基本思路:
/**
* 该方法是用空间换取时间,每次细分都会创建左右两个数组,会加重内存的负担,对于配置不高的机器来说是不可取的
*/
// 快速排序函数
function quickSort(arr) {
// 如果数组长度<=1,则说明数组不需要排序
if (arr.length <= 1) {
return arr;
}
// 找一个基准值索引,一般为数组中间的元素
let middleIndex = Math.floor(arr.length / 2);
// 获取基准值
let middleValue = arr[middleIndex];
// 建立一个左数组
let left = [];
// 建立一个右数组
let right = [];
// 遍历数组
for (let i = 0,len = arr.length; i < len; i++){
// 如果i为基准值索引,则跳过操作
if (i === middleIndex) {
continue;
}
// 把小于基准值的数据放在左数组中
if (arr[i] < middleValue) {
left.push(arr[i]);
} else {
// 把大于基准值的数据放在右数组中
right.push(arr[i]);
}
}
// 将做数组、基准值和右数组整合在一块儿
return quickSort(left).concat([middleValue], quickSort(right));
}
// quickSort([8, 0, 4, 6, 1, 2, 7, 3, 5, 9]); // [0,1,2,3,4,5,6,7,8,9] 得到有序数组
快速排序的优化:
/**
* 使用左右两个指针来代替左右数组来对原数组排序
* 时间换取空间
*/
function quick(array, left, right) {
let index;
if (array.length > 1) {
index = sortOnce(array, left, right);
if (left < index - 1) {
// 对基准值左边的元素排序
quick(array, left, index - 1);
}
if (index < right) {
// 对基准值右边的元素排序
quick(array, index, right);
}
}
return array;
}
function sortOnce(arr, left, right) {
const pivot = arr[Math.floor((right + left) / 2)];
let i = left;
let j = right;
while (i <= j) {
while (arr[i] < pivot) {
i++;
}
while (arr[j] > pivot) {
j--;
}
if (i <= j) {
let middle = arr[i]
arr[i] = arr[j]
arr[j] = middle
i++;
j--;
}
}
// 新增加,我们把处理之后的i值return出去
return i
}
// 使用乱序数组验证
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
quick(arr, 0, arr.length - 1) // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] 得到有序数组
四 插入排序
插入排序思路是,在要排序的一组数中,假定前 n-1 个数已经排好序,现在将第 n 个数插到前面的有序数列中,使得这第 n 个数也是排好顺序的。如此反复循环,直到全部排好顺序。
示例步骤:
- 获得一个待排序的值 – 目标值。
- 从后向前遍历此元素之前的元素,如果元素大于目标值,将元素后移一个单位。
- 元素小于或等于目标值,将目标值放在此元素之后。
直接插入排序:
// 待排序数组
const arr = [8, 0, 4, 6, 1, 2, 7, 3, 5, 9];
// 直接插入排序
function insertSort(arr) {
// 传入的数组长度
const len = arr.length;
// 由于数组的第一个元素不需要排序,所以i从1开始
for (let i = 1; i < len; i++) {
// 获取目标值
let result = arr[i];
// 待插入的坐标
let resultIndex;
// 从i向前遍历,如果大于目标值,则后移一位
for (resultIndex = i; resultIndex > 0 && (arr[resultIndex - 1] > result); resultIndex--) {
arr[resultIndex] = arr[resultIndex - 1];
}
// 将目标值放入适当的坐标中
arr[resultIndex] = result;
}
}
折半插入排序:
/**
* 折半插入是在直接插入的过程中使用 二分查找(会在查找方法中详细介绍) 方法搜索坐标
*/
function binaryInsertSort(arr) {
let start; // 二分查找的起始点
let end; // 二分查找的终止点
let resultIndex; // 插入坐标
let result; // 目标值
for (let i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) {
// 获取目标值
result = arr[i];
// 获取起始坐标
start = 0;
// 获取终止坐标
end = i - 1;
// 在此区间之内进行二分查找
while (start <= end) {
// 中间值索引
let mid = Math.floor((start + end) / 2);
// 目标值 > 中间值
if (result > arr[mid]) {
start = mid + 1;
} else {
// 目标值 < 中间值
end = mid - 1;
}
}
// 将start 到 resultIndex之间的元素统一后移一位
for (resultIndex = i; resultIndex > start; resultIndex--) {
arr[resultIndex] = arr[resultIndex - 1];
}
// 将目标值放入插入坐标
arr[resultIndex] = result;
}
}
}
参考:JAVA资讯库