记录 1 道算法题
排序数组
要求:将数组升序排列。
排序老生常谈的问题了,主要有 4 种方法。
- 冒泡
冒泡的性能不好,加上一般会使用 api,所以一般不使用,但是是一种刚接触代码的时候都会碰到的解法。
function sortArray(nums) {
for(let i = 0; i < nums.length; i++) {
// 每次循环后都会确定数组的最后一个位置的数
for(let j = 0; j < nums.lenth - i - 1; j++) {
// 相邻的两个数进行比较
if(nums[j] > nums[j + 1]) {
const t = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = t
}
}
}
return nums
}
- 快排
快排是每个人都会的一种排序算法。作为一种分治算法,但不算稳定,他是基于一个基准值划分左右,当随机的基准值划分的左边或右边只有一个的时候,递归的层数也挺深的,基本等于数组长度。
function sortArray(nums) {
fastSort(nums, 0, nums.length - 1)
return nums
}
function fastSort(arr, start, end) {
if (start >= end) return
// 这里拿了第一个为基准值,采用随机会更好
let p = arr[start]
let s = start + 1
for(let i = start + 1; i <= end; i++) {
if (arr[i] < p) {
swap(arr, i, s)
s++
}
}
// 将基准值交换到中间
swap(arr, start, s - 1)
// 由于不做减枝,而是排序整个数组,所以直接递归
// 基准值的位置是对的,所以只需要对左右两边递归排序
fastSort(arr, start, s - 2)
fastSort(arr, s, end)
}
- 堆
堆是只关心堆顶是数字在堆中是什么关系,其他位置的数字存放时符合一个树状结构,但是乱序的。一般是大顶堆和小顶堆,大顶堆就是堆顶数字是堆内最大的。小顶堆则相反。
只要将数组里的数都放进堆中,然后再一直取堆顶的数就可以了。由于我们需要升序排列,所以我们使用小顶堆,就可以使用 push,添加到数组里。
堆的实现可以看这篇文章
function sortArray(nums) {
const heap = new Heap((a, b) => a - b)
while(nums.length) {
heap.push(nums.pop())
}
while(heap.size()) {
nums.push(heap.pop())
}
return nums
}
- 归并
归并可以将有序数组进行合并,常见有由上至下的递归写法和由下至上的循环写法。本质上是通过将数组划分成更小的数组,然后实现升序,比如 n 个长度为 1 的数组,他们自己是有序的。他们两两一组组成了 Math.sqrt(n) 个长度为 2 的数组,他们仍然是可以实现有序。依次组合,最后就实现了有序排列。
和排序链表思路类似。不同的地方在于递归做法中,数组可以直接取到中间,进行砍半。不用斩断链表。