面试官:能说下(or手写)快速排序原理不?
菜鸡:我忘了不好意思 = =
以上场景是致命的
数年经验的开发者,忘掉具体代码也是分分钟的事。
所以写下来,每当面试前,温习一边就完事了。
所用语言:JavaScript
涉及算法:冒泡、
(一)冒泡排序(Bubble Sort)
简述:在数组开头,一对一对地取,一个一个地往后推。取出来的一对进行比较,顺序不对则换位。排序总是后面的比前面的先好。
1 function bubble(elements) { 2 for(let i = 0; i < elements.length - 1; i++) { 3 let quit = true 4 for(let j = 0; j < elements.length - 1 - i; j++) { 5 if(elements[j] < elements[j+1]) { 6 //一对交换 7 let swap = elements[j] 8 elements[j] = elements[j+1] 9 elements[j+1] = swap 10 //发生换位,取消退出 11 quit = false 12 } 13 } 14 //里层循环并没有产生换位,说明整个数集已经好了 15 if(quit) break 16 } 17 18 return elements 19 }
时间复杂度:最好的情况是本来就排好了的,跑第一次最里层循环就可以退出了,O(n)
否则,两层循环,复杂度为O(n2)
(二)选择排序(Selection Sort)
简述:在未排序数集中,选出最小的元素,放在数集开头,然后再重复这个动作。
1 function selection(elements) { 2 for(let i = 0; i < elements.length - 1; i++) { 3 // i 上界减1给j预留位置 4 for(let j = i + 1; j < elements.length; j++) { 5 if(elements[i] > elements[j]) { 6 let temp = elements[i] 7 elements[i] = elements[j] 8 elements[j] = temp 9 } 10 } 11 } 12 13 return elements 14 }
时间复杂度:两层循环,明显的O(n2)
(三)插入排序(Insertion Sort)
简述:在未排序数集中取一个,在前面的已排序集中从后向前逐个比较,插入到应在的位置。重复前面步骤。
1 function insertion(elements) { 2 for(let i = 1; i < elements.length; i++) { 3 //第0个已排序好 4 if(elements[i] < elements[i-1]) { 5 //暂时最大值直接归并到已排序最后一位,省去插入 6 let tmp = elements[i] 7 let j = i - 1 8 elements[i] = elements[j] 9 10 while(j >= 0 && tmp < elements[j]) { 11 elements[j+1] = elements[j] 12 j-- 13 } 14 15 elements[j+1] = tmpi 16 } 17 } 18 19 return elements 20 }
时间复杂度:O(n2)
额外空间:O(1)
(四)快速排序
简述:分而治法。取一个标杆,大于它的站右边,小于它的站左边。得出的两个子集继续取各自的标杆站队。直至子集不可再分。
时间复杂度:最坏的情况是已排好序列,O(n2)
最好、平均情况是 O(nlogn) 2为底
(五)三路快排
简述:对快排的优化:标志位的选择为随机,分出三个子集来应对等于标志位的情况。
1 function quickThree(elements) { 2 __quickThree(elements, 0, elements.length - 1) 3 return elements 4 } 5 6 function __quickThree() { 7 if (start > end) { 8 return 9 } 10 11 let rand = Math.round(Math.random() * (end - start)) 12 //随机选出的标志位放在首位 13 [elements[start], elements[start + rand]] = [elements[start + rand], elements[start]] 14 let flag = elements[start] 15 16 let lt = start 17 let gt = end + 1 18 let i = start + 1 19 while(i < gt) { 20 if (elements[i] < flag) { 21 [elements[i], elements[lt + 1]] = [elements[lt + 1], elements[i]] 22 lt++ 23 i++ 24 } else if (elements[i] > flag) { 25 [elements[i], elements[gt - 1]] = [elements[gt - 1], elements[i]] 26 gt-- 27 //后面换上来的,未经比较,因此 i 不自增,继续滚入while校验 28 } else { 29 i++ 30 } 31 } 32 //标志位放回它应在的地方 33 [elements[start], elements[lt]] = [elements[lt], elements[start]] 34 __quickThree(elements, status, lt - 1) 35 __quickThree(elements, gt, end) 36 }
lt——i之间是小于哨兵的、i——gt之间是等于哨兵的,gt——end是大于哨兵的