冒泡排序
算法思想:通过两两比较相邻值,如果反序则交换位置,这样较小的元素会冒泡到前面,较大的元素会沉淀至尾部
实现思路:
(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)