在前面的一些面试中遇到过一道简单的算法题,就是找出第n大的数。具体的题目找不到了,但是跟这个差不多。(414. 第三大的数 - 力扣(LeetCode))就是第三大改成第n大。
我当时看到这个就很兴奋了,这不很简单吗?夸夸夸就写完了,不就是直接给数组用sort排个序然后直接用下标n给他取出来就行了吗。当我写完这个之后,他就开始发难了,能不能不用自带的方法排序? 完了,这不是问到我的软肋了吗,挠头发挠了半天没挠出来,害。所以还是要好好整理下吧,我又翻出来我的数据结构这本书。
几种排序方法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 希尔排序
- 堆排序
先慢慢一个一个来吧!
冒泡排序
冒泡排序的原理就是根据冒泡一样,我还记的老师说的,大的沉下去,小的浮上来。意思就是一次拿两个相邻的元素比较,如果前者大于后者就进行交换,然后再往后走,一直交换下去,直到不再需要交换为止。
举个例子:
一组数 [5, 1, 2, 6, 2, 8, 3]
第一轮 [1, 2, 5, 2, 6, 3, 8]
(1,5交换后,5,2也要交换,5,6不需要交换,6,2交换,6,8不需要交换,8,3交换)
第二轮 [1, 2, 2, 5, 3, 6, 8]
(同理)
第三轮 [1, 2, 2, 3, 5, 6, 8]
(同理)
第四轮 [1, 2, 2, 5, 3, 6, 8]
(不需要交换,结束!)
具体的代码实现如下:
function bubbleSort(arr) {
let n = arr.length;
let swapped;
for (let i = 0; i < n - 1; i++) {
swapped = false; // 初始化标志位为 false
for (let j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true; // 发生了交换,设置标志位为 true
}
}
// 如果没有发生交换,说明数组已经有序,可以提前结束
if (!swapped) {
break;
}
}
return arr;
}
外圈循环代表着排序的轮次,因为我们排序的时候两两对比,只需要进行n-1轮排序就够了。第二层循环是交换元素的位置(如果位置不对的话)在这里设置一个是否交换标志,如果整个一轮都没有进行交换说明顺序已经排好,就不需要进行下一轮了,可以提前结束。
它的复杂度最好的情况下就是O(n),最坏的情况那就是n的平方咯。
选择排序
选择排序就是每次在没有排序的部分选择最小的(最大的)值放在首位(尾部)。
还是拿刚才的例子举例:
一组数 [5, 1, 2, 6, 2, 8, 3]
第一轮 [1, 5, 2, 6, 2, 8, 3]
(找到最小的值1,让他与首位5进行交换)
第二轮 [1, 2, 5, 6, 2, 8, 3]
(同理,2和5交换位置)
第三轮 [1, 2, 2, 6, 5, 8, 3]
(同理,2和5交换位置)
第四轮 [1, 2, 2, 3, 5, 8, 6]
(3和6交换位置)
第五轮 [1, 2, 2, 3, 5, 8, 6]
(5不需要交换位置)
第六轮 [1, 2, 2, 3, 5, 6, 8]
(6和8交换位置)
具体的代码实现如下:
function selectionSort(arr) {
let n = arr.length;
for (let i = 0; i < n - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex !== i) {
// 交换元素
let temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
return arr;
}
每一轮都会找到未排序的最小值,如果最小值的索引不是未排序的第一个的话就需要进行交换。 选择排序我认为还是差点意思,但是还是比较简单的,它的时间复杂的位On的平方。
未完待续
今天就先写这两个排序吧,明天再看看写剩下的几个排序。