详解冒泡排序和快速排序

冒泡排序

算法思想:通过两两比较相邻值,如果反序则交换位置,这样较小的元素会冒泡到前面,较大的元素会沉淀至尾部

 实现思路

(1)设置一个哨兵,默认值为数组长度-1;

(2)通过i++循环两两比较相邻值,反序则交换位置,这样较小的元素会冒泡到前面,较大的元素会沉淀至尾部;一次循环过后,最大的元素会沉淀到尾部,哨兵位置-1

(3)继续i++循环直到哨兵位置为0

function badBubbleSort(arr) {
  let flag = arr.length - 1
  while (flag > 0) {
    for (let i = 0; i < flag; i++) {
      if (arr[i] > arr[i + 1]) {
        let tmp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = tmp
      }
    }
    flag--
  }
  return arr
}

 上述方案存在一个明显的缺点,一轮循环后flag才减一。不管数组是不是有序,时间复杂度为 O(n^2)

优化:

进入一轮i++循环时,flag先置为0;当存在反序交换时,flag记录交换的位置;

若数组为有序数组,一轮循环过后,没有发生反序交换,flag为0。可以结束循环。此时时间复杂度为 O(n)

function bubbleSort(arr) {
  let flag = arr.length - 1
  while (flag > 0) {
    let bound = flag
    flag = 0
    for (let i = 0; i < bound; i++) {
      if (arr[i] > arr[i + 1]) {
        let tmp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = tmp
        flag = i
      }
    }
  }
  return arr
}

总结

冒泡排序(稳定,属于交换排序):两两比较相邻数值,如果反序则交换位置。
时间复杂度

平均情况: O(n^2)

最好情况: O(n)

最坏情况:   O(n^2)

空间复杂度  O(1)

 

快速排序

算法思想:改良自冒泡排序,冒泡排序是相邻元素两两比较,快速排序增大了比较的范围,这样子进行交换位置,跳跃位置变大,能更快地让元素有序。

实现思路:选定一个轴值,让左侧元素小于轴值,右侧元素大于轴值;再依次对左侧元素和右侧元素进行类似操作直到全部元素有序

轴值的选定方法

 (1)设置左哨兵i=0;右哨兵j=arr.length-1

 (2)先进行右侧扫描,若右哨兵位置的值大于左哨兵位置的值,则右哨兵-1;直到出现小于情况,交换两个哨兵位置的值

 (3)再进行左侧扫描,若左哨兵位置的值小于右哨兵位置的值,则左哨兵+1;直到出现大于情况,交换两个哨兵位置的值

 (4)重复(2)(3)步骤直到两个哨兵相遇,此时获得轴值就是两个哨兵相遇位置的值

 (5)返回轴值,即两个哨兵相遇的位置

 快速排序的实现方法:(递归方式)

 (1)先选定轴值

 (2)对轴值左侧位置进行快排

 (3)对轴值右侧位置进行快排

function quickSort(arr) { // 快速排序
  return recursiveQuickSort(arr, 0, arr.length - 1)
}
function recursiveQuickSort(arr, first, end) { // 递归排序
  if(first < end) {
    let pivot = getPivot(arr, first, end) // 获得轴值
    recursiveQuickSort(arr, first, pivot - 1) // 对轴值左侧元素进行类似操作
    recursiveQuickSort(arr, pivot + 1, end) // 对轴值右侧元素进行类似操作
  }
  return arr
}
function getPivot(arr, first, end) { // 选定轴值
  let i = first // 左哨兵
  let j = end // 右哨兵
  while(i < j) { // 直到两个哨兵相遇才能退出循环
    while(i < j && arr[j] >= arr[i]) j-- // 先进行右侧扫描
    if(i < j) { // 交换位置
      let tmp = arr[i]
      arr[i] = arr[j]
      arr[j] = tmp
      i++ // 交换了位置,证明原本左哨兵位置的值更大,现在把值换小了,左哨兵可以移动了(左哨兵:我变小了,可以移动了)
    }
    while(i < j && arr[i] <= arr[j]) i++ // 再进行左侧扫描
    if(i < j) { // 交换位置
      let tmp = arr[i]
      arr[i] = arr[j]
      arr[j] = tmp
      j-- // 交换了位置,证明原本右哨兵位置的值更小,现在把值换大了,右哨兵可以移动了(右哨兵:我变大了,可以移动了)
    }
  }
  return i // 获得轴值---两个哨兵相遇的位置就是轴值
}

总结

快速排序(不稳定,属于交换排序):首先选一个轴值(pivot,即比较的基准),将待排序记录分成独立的两部分,左侧记录的关键码均小于等于轴值,右侧记录的关键码均大于等于关键码。然后分别对这两部分重复上述过程,知道整个序列有序。快排是对冒泡的改良,属于交换排序。
 

时间复杂度
平均情况:O(nlog2n)
最好情况:O(nlog2n)
最坏情况:O(n2)


空间复杂度 O(log2n)~O(n)
 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值