算法入门【下】

237 阅读2分钟

关于排序

  1. 总共讲解七种排序
  • 选择排序
  • 快速排序
  • 归并排序
  • 计数排序
  • 冒泡排序
  • 插入排序
  1. 所有的递归都可以写成循环。所以每种排序都有递归写法和循环写法。

选择排序

参考博客:算法入门 【上】

快速排序

递归思路——以x为基准,比他小的排到他前面,比他大的排到他后面。

这样x位置就永远定下来了!因为他前面的全是比他小的,后面全是比他大的。那前面那些数字自己在排序,后面的也自己在排序。x就永远不动了!

那左边和右边的数组也像这样选个基准排序。

代码(阮一峰版本)

快速排序

quickSort = arr => {                                     //接收一个数组
  let if (arr.length <= 1) { return arr; }             //如果这个数组小于等于1,就直接返回这个数组
  let pivotIndex = Math.floor(arr.length / 2);         // 把数组中最中间(或中间偏左)的那个元素作为基准
  let pivot = arr.splice(pivotIndex, 1)[0];            //把这个基准的人单独拎出来(在基准的下标处删除一个,得到一个数组)
  let left = [];                                       // 生成一个left的数组
  let right = [];                                      //生成一个right数组
  for (let i = 0; i < arr.length; i++){                //遍历删掉基准后的arry,比基准元素小的就放到left,比基准元素大的就放到right
    if (arr[i] < pivot) { left.push(arr[i]) 
    } else { right.push(arr[i]) }
  }
  return quickSort(left).concat(                  
[pivot], quickSort(right) )                         
}                                                        //最后返回一个数组,这个数组:对left快排得到的数组+基准元素+对right快排得到的数组

归并排序(merge)

  • 把两个排好序的数组合并merge成一个新的排好序的数组。
  • 把一个数组分为左右两边数组
  • 所以把一个数组一直分左右两个数组,再分左右两个数组,直到变成一个元素组成的数组。好啦,就可以开始合并了。把一个元素的数组和另一个一个元素的数组合并,在和其他合并合并合并完事
  • 图解

代码

let mergeSort = arr =>{           接受一个数组
  if(arr.length===1){return arr}       如果你的长度是1 ,默认排好序(精髓)
  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) => {          merge接收两个数组,a和b
  if(a.length === 0) return b    如果a空了,返回B
  if(b.length === 0) return a    如果b空了,返回a
  return a[0] > b[0] ?           如果都没有空,就找到两个数组中,谁的第一个最小,如果b 0的第一个最小
     [b[0]].concat(merge(a, b.slice(1))) :     那我就用b0连接上a和b的其他部分的merge      
     [a[0]].concat(merge(a.slice(1), b))
}

计数排序

思路:

  • 用一个哈希表做记录
  • 发现数字N就记N:,如果再次发现N就加1
  • 最后把哈希表的ke全部打出来,假设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++) {    //遍历0到最大数,看哈希表里面有没有这个key
    if (j in hashTable) {
      for (let i = 0; i < hashTable[j]; i++) {
        result.push(j);
      }
    }
  }
  return result;
};

计数排序的特点

  • 使用了额外的hashTable
  • 只便利数组一遍(不过还要遍历一次hashTable)
  • 这叫做 [ 用空间换时间 ]

时间复杂度对比

  • 选择排序O(n^2)
  • 快速排序O(n log2 n)
  • 归并排序O(n log2 n)
  • 计数排序O(n + max) 时间快但同时内存也多了

冒泡排序

visualgo.net/zh/sorting

插入排序

visualgo.net/zh/sorting 点击INS

希尔排序

sorting.at/ 自己选择Shell Sort

基数排序

visualgo.net/zh/sorting 点击RAD

算法学习总结

战略上藐视敌人,战术上重视敌人

特点

  • 思路都很简单
  • 细节都很多
  • 多画表,多画图,多log
  • 如果实在不想陷入JS的细节,可以用伪代码