选择、快速、归并、计数排序

一、求任意长度数组的最小数

1.1求两个数中最小的数

let minOf2 = ([a,b]) => a < b ? a : b

//调用:
minOf2.call(null,[1,2]) //call的第一个参数是this,设置为null
复制代码

JS有现成的API:Math.min

Math.min(1,2) //1
Math.min.call(null,1,2)
Math.min.apply(null,[1,2])//apply和call调用的区别,前者后半部分是一个数组
复制代码
  • Math看来像是和Object一样的构造函数,但其实只是一个普通对象
  • 这是唯一一个特例:首字母大写的是构造函数

1.2求三个数中最小的数

let minOf3 = ([a,b,c]) => {
    return minOf2([minOf2([a,b]),c])
}

//推理求四个数中的最小的数字
let minOf4 = ([a,b,c,d]) => {
    return minOf2([a,minOf3([b,c,d])])
}
//任意长度数组求最小值,都可以通过minOf2来实现
复制代码

1.3求任意长度数组的最小数

let min = (numbers) => {
    if(numbers.length > 2 ){
      return min([numbers[0],min(numbers.slice(1))] )
    }
   else{
       return Math.min.apply(null,numbers)
   }
}
//上述就是递归
复制代码

递归的特点:

  • 函数不停的调用自己,每次调用的参数略有不同

  • 当满足某个简单条件时,则会实现一个简单的调用

  • 最终算出结果

如何理解递归:

  • 代入法理解递归
  • 调用栈理解递归

二、选择排序

2.1递归的思路

2.1.1长度为2的数组排序

let sort2 =([a,b]) => 
a < b ? [a,b] :[b,a]
复制代码

2.1.2长度为3的数组排序

let minIndex = (numbers) => numbers.indexOf(min(numbers));

let sort3 = (numbers) => {
   let index = minIndex(numbers) //返回最小值的下标
   let min = numbers[index] //numbers里的最小值
   numbers.splice(index, 1)//从number里面删掉最小的数
   return [min].concat(sort2(numbers))//把最小的数放在第一位,然后连接后面的数字
}
复制代码

2.1.3长度为4的数组排序

let sort4 = (numbers) => {
  let index = minIndex(numbers)
  let min = numbers[index]
  numbers.splice(index, 1)
  return [min].concat(sort3(numbers))
}
复制代码

2.1.4求任意长度数组的长度

let min = (numbers) => {
    if(numbers.length > 2 ){
        return min([numbers[0],min(numbers.slice(1))] )
    }else{
       return Math.min.apply(null,numbers)
   }
};

let minIndex = (numbers) => numbers.indexOf(min(numbers));

let sort = (numbers) => {
     if(numbers.length > 2){
        let index = minIndex(numbers) //返回最小值的下标
        let min = numbers[index]//numbers里的最小值
        numbers.splice(index, 1)//从number里面删掉最小的数
        return [min].concat(sort(numbers))//把最小的数放在第一位,然后连接后面的数字
     }else{
        return numbers[0]<numbers[1] ? numbers :numbers.reverse()
     }
}
//用代入法理解递归 sort([12,5,8,7,9])
复制代码

2.2循环的思路

2.2.1用循环实现sort

let sort = (numbers) => {
  for(let i=0; i< numbers.length -1; i++){
    console.log(`----`) //这个log很精髓
    console.log(`i: ${i}`)
    let index = minIndex(numbers.slice(i))+ i
    //如果上次循环找到第一个最小数字,那么之后找数字的时候,可以忽略第一个
    //+i,如果不加i,numbers.slice(i)返回的新数组下标总是从0算起
    console.log(`index: ${index}`)
    console.log(`min: ${numbers[index]}`)
    if(index!==i){
      swap(numbers, index, i)
      console.log(`swap ${index}: ${i}`)
      console.log(numbers)
    }
}
  return numbers
}

//交换数组中两个数字的位置
let swap = (array, i, j) => {
  let temp = array[i]
  array[i] = array[j]
  array[j] = temp
}

//循环实现最小数字的下标
let minIndex = (numbers) => {
  let index = 0 //假设最小数字下标是0
  for(let i=1; i<numbers.length; i++){
    if(numbers[i] < numbers[index]){
      index = i
    }
  }
  return index
}
复制代码

总结:

  • 所有的递归都能改成循环
  • 但循环有很多细节:
    • 边界条件
    • 长度为0和1的数组处理
  • debug的时候要看控制台,学会打log,打log的时候注意标记

三、快速排序

递归的思路

let quickSort = arr => {
  if (arr.length <= 1) { return arr; }
  let pivotIndex = Math.floor(arr.length / 2); //选择一个基准
  let pivot = arr.splice(pivotIndex, 1)[0];
  let left = [];
  let right = [];
  for (let i = 0; i < arr.length; i++){
    if (arr[i] < pivot) { 
        left.push(arr[i])
    } else { right.push(arr[i]) }
  }
  return quickSort(left).concat([pivot], quickSort(right) )
}
复制代码

四、归并排序

递归的思路

let mergeSort = arr =>{
  if(arr.length === 1){return arr}
  let left = arr.slice(0, Math.floor(arr.length/2))//数组的左半部分
  let right = arr.slice(Math.floor(arr.length/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))
}
复制代码

五、计数排序

思路:

  • 用一个哈希表做记录
  • 发现数字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++){//如果j出现的次数不止一次,就要遍历
        result.push(j)
      }
    }
  }
  return result
}
复制代码
  • 使用额外的hashTable,只便利一次数组和一次hashTable,用空间换时间

六、四种排序的时间复杂度

15.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享