算法与数据结构之四种排序

84 阅读2分钟

算法入门:请写一个 min 函数,要求 min(numbers) 能返回数组 numbers 中的最小数字

let min = (numbers) => {
  if (numbers.length > 2) {
    return min([numbers[0], min(numbers.splice(1))])
  } else {
    return Math.min.apply(null, numbers)
  }
}
console.log('取[16, 12, 13, 7, 25]最小值:')
console.log(min([16, 12, 13, 7, 25]))

1. 选择排序

思路:每次选择最小/大的,选完就排完。

  • 递归写法
// 请写出一个 sort 函数,要求 sort(numbers) 能返回一个把 numbers 从小到大排列的数组
// 获取最小值的index
let minIndex = (numbers) => {
  let index = 0
  for (let i=1; i<numbers.length;i++){
    if (numbers[i] < numbers[index]) index = i
  }
  return index
}

let sort = (numbers) => {
  if (numbers.length > 2) {
    let index = minIndex(numbers)
    let min = numbers[index]
    numbers.splice(index, 1)
    return [min].concat(sort(numbers))
  } else {
    return numbers[0] < numbers[1] ? numbers : numbers.reverse()
  }
}
console.log('排序[17,39,24,15,11]:')
console.log(sort([17,39,24,15,11]))
  • 循环写法
// 获取最小值的index
let minIndex = (numbers) => {
  let index = 0
  for (let i=1; i<numbers.length;i++){
    if (numbers[i] < numbers[index]) index = i
  }
  return index
}

// 替换数组两个下标对应数据的位置
let swap = (array, i, j) => {
  let temp = array[i]
  array[i] = array[j]
  array[j] = temp
}

let sort2 = numbers => {
  for (let i = 0; i < numbers.length - 1; i++) {
    let index = minIndex(numbers.slice(i)) + i
    if (index !== i) {
      swap(numbers, index, i)
    }
  }
  return numbers
}
console.log('排序[4,3,6,8,2]:')
console.log(sort2([4,3,6,8,2]))

2. 快速排序

思路:重复做操作「以某数为基准,小的去前面,大的去后面」

let quickSort = numbers => {
  if (numbers.length > 1) {
    // 基准的index
    let pivotIndex = Math.floor(numbers.length / 2)
    // 基准的值
    let pivot = numbers.splice(pivotIndex, 1)[0]
    let left = []
    let right = []
    for (let i = 0; i < numbers.length; i++) {
      if (numbers[i] < pivot) {
        left.push(numbers[i])
      }else{
        right.push(numbers[i])
      }
    }
    return quickSort(left).concat([pivot],quickSort(right))
  } else {
    return numbers
  }
}
console.log("排序[8,3,9,4,2]")
console.log(quickSort([8,3,9,4,2]))

3. 归并排序

思路:左边一半排好序,右边一半排好序,然后把左右两边合并起来。

let mergeSort = numbers => {
  let k = numbers.length
  if (k === 1) {
    return numbers
  }
  let left = numbers.slice(0, Math.floor(k / 2))
  let right = numbers.slice(Math.floor(k / 2))
  return merge(mergeSort(left), mergeSort(right))
}
let merge = (a,b) => {
  if (a.length === 0) return b
  if (b.length === 0) return a
  return a[0] > b[0] ? [b[0]].concat(merge(a,b.slice(1))) : [a[0]].concat(merge(a.slice(1),b))
}
console.log("排序[18,3,9,4,12]")
console.log(mergeSort([18,3,9,4,12]))

4. 计数排序

思路:用一个哈希表做记录,发现数字 N 就记 N:1,如果再次发现 N 就加 1,最后把哈希表的 key 全部打出来,假设 N:m,那么 N 需要打印 m 次。

let countSort = arr => {
  let hashTable = {}, max = 0, result = []
  // 遍历数组
  for (let i = 0; i < arr.length; i++) {
    if (!(arr[i] in hashTable)) {
      hashTable[arr[i]] = 1
    } else {
      hashTable[arr[i]] += 1
    }
    if (arr[i] > max) max = arr[i]
  }
  // 遍历哈希表
  for (let j = 0; j <= max; j++) {
    if (j in hashTable) {
      for (let i = 0; i < hashTable[j]; i++) {
        result.push(j)
      }
    }
  }
  return result
}
console.log("排序[18,3,9,4,9,12]")
console.log(countSort([18,3,9,4,9,12]))

5. 时间复杂度对比

  • 选择排序 O(n^2)
  • 快速排序 O(n log2 n)
  • 归并排序 O(n log2 n)
  • 计数排序 O(n + max) 「用空间换时间」

6. 其他排序