选择排序&快速排序(JS)

  • 选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面,或者将最大值放在最后面。
但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择,每一趟从前往后查找出无序区最小值,将最小值交换至无序区最前面的位置。
平均时间复杂度:O(N^2)
最佳时间复杂度:O(N^2)
最差时间复杂度:O(N^2)
空间复杂度:O(1)
排序方式:In-place
稳定性:不稳定
/**
 * 基础选择排序 - 方法1
 * @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
 * @returns sort-arr
 */
console.time('selectSort1');
var arr1 = [3, 1, 5, 4, 7, 6, 0, 2];
var selectSort1 = arr => {
  let len = arr.length;
  if (len <= 1) return;
  let minIndex = 0; // 默认最小值为第一位0
  for (let i = 0; i < len - 1; i++) {
    minIndex = i
    for(let j = i+1;j<len;j++){
      arr[j] < arr[minIndex] ? minIndex = j : null
    }
    [arr[i],arr[minIndex]] = [arr[minIndex],arr[i]]
  }
  console.log('排序后数组', arr);
  return arr;
};
selectSort1(arr1);
console.timeEnd('selectSort1');
  • 快速排序
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

快速排序的基本思想就是分治法
平均时间复杂度:O(NlogN)
最佳时间复杂度:O(NlogN)
最差时间复杂度:O(N^2)
空间复杂度:根据实现方式的不同而不同
/**
 * 基础快速排序 - 方法1
 * 缺点:① 多开辟了left,right空间
 *       ② 使用push及concat方法
 * @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
 * @returns sort-arr
 */
console.time('quickSort1');
// var arr1 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr1 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];

var quickSort1 = arr => {
  // 排异
  let len = arr.length;
  if (len <= 1) return arr;

  // 算法核心
  let target = arr[0];
  let left = [],
    right = [];
  for (let i = 1; i < len; i++) {
    if (arr[i] < target) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  // 终值
  // return quickSort1(left).concat([target], quickSort1(right)); // es5 数组concat
  return [...quickSort1(left),target,...quickSort1(right)] // es6 展开运算符
};
console.log(quickSort1(arr1));
console.timeEnd('quickSort1');
/**
 * 优化快速排序 - 方法2
 * 优点:① 增加前后两个指针l,r 每次默认选择当前分区数组的第一个为目标值target
 *       ②  通过一轮while大循环 将小于target的值排到左分区 将大于target的值排到右分区 
 *      ③ 此时再分别递归左右分区数组
 *      ④ 针对大数组数据时 效果很好 相较于上面的算法 我们以较好的状态来进行分区 而不是每次对比来左右分区
 * @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
 * @returns sort-arr
 */
console.time('quickSortWrapper');
// var arr2 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr2 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];

var quickSortWrapper = arr => {
  // 排异
  let len = arr.length;
  if (len <= 1) return arr;

  // 算法核心
  let arrSort = quickSort2(arr,0,len-1)

  // 终值
  return arrSort
};
var quickSort2 = (arr,start,end) => {
  if(end - start < 1) return 
  let target = arr[start] // 首次进来默认选择第0个作为目标值
  let l = start
  let r = end  // 这里另外声明两个变量来替代start end  因为后面在进行左右分区递归遍历时需要原数组的start,end
  while(l < r){
    while(l < r && arr[r] >= target){
      r--
    }
    arr[l] = arr[r]
    while(l < r && arr[l] <= target){
      l++
    }
    arr[r] = arr[l]
  }
  arr[l] = target
  // 此时l索引位置 左边已经是小于target的值 右边已经是大于target的值 且分区索引位置就是l位置
  quickSort2(arr,start,l-1) // 左分区 递归遍历
  quickSort2(arr,l+1,end) // 右分区 递归遍历
  return arr
}
console.log(quickSortWrapper(arr2));
console.timeEnd('quickSortWrapper');
/**
 * 优化快速排序 - 方法3
 * 优点:① 三路快排 
 *       ② 适合大数组数据且含重复数据比较多的情况 使用
 *      ③ 感觉跟第一种 就是多了针对center收集了重复数据 避免重复遍历比较
 * @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
 * @returns sort-arr
 */
console.time('quickSort3');
// var arr3 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr3 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];

var quickSort3 = arr => {
  // 排异
  let len = arr.length;
  if (len <= 1) return arr;

  // 算法核心
  let left = [],right=[],center=[];
  let target = arr[0],i=0;
  while(i<len){
    if(arr[i] < target){
      left.push(arr[i])
    }else if(arr[i] === target){
      center.push(arr[i])
    }else{
      right.push(arr[i])
    }
    i++
  }

  // 终值
  // return quickSort3(left).concat(...center, quickSort3(right)); // es5 数组concat
  return [...quickSort3(left),...center,...quickSort3(right)] // es6 展开运算符
};
console.log(quickSort3(arr3));
console.timeEnd('quickSort3');

参考链接:
选择排序
快速排序
快速排序
快速排序时间空间复杂度推算

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值