各自排序

37 阅读6分钟

算法

稀疏数组和队列

  1. 记录数组一共有几行几列,有多少个不同的值,
  2. 把具有不同值的元素的行列及值记录在一个小规模数组中,减少程序的模型。



排序算法

  • 冒泡排序 ( 时间复杂度 : O(n^2) )
function bubble(arr) {
  let change = false
  for (let i = 0; i < arr.length-1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++){
      let t = 0
      if(arr[j]>=arr[j+1]){
        change = true                    //优化的地方
        t=arr[j]
        arr[j] = arr[j+1]
        arr[j+1] =t
      }
    }
    if(!change){
      break
    }else{
      change = false
    }
  }
  return arr
}

console.log(bubble(arr))



  • 选择排序( 时间复杂度 : O(n^2) ) 介绍:属于内部排序法,是从想要排序的数据中,按指定的规则选出一个元素,再依规定交换位置后达到排序的目的
function selectsort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let minIndex = i
    let min = arr[i]
    for (let j = i+1; j < arr.length; j++) {
      if (arr[j] < min) {
        min = arr[j]
        minIndex = j
      }
    }
    if (minIndex !== i) {            //这里的意思是 如果经过一轮比较后,最小值
      arr[minIndex] = arr[i]         //min = arr[i] ,则不需要交换。
      arr[i] = min
    }
  }
  return arr
}


console.log(selectsort(arr))



  • 插入排序 ( 时间复杂度 : O(n^2) )

介绍:属于内部排序,是对于想要排序的元素以插入的方式找寻该元素适当的位置,以达到排序的目的,

function insertsort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let insertVal = arr[i]
    let insertIndex = i - 1
    while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
      arr[insertIndex + 1] = arr[insertIndex]
      insertIndex--
    }
    arr[insertIndex + 1] = insertVal
  }
  return arr
}
console.log(insertsort(arr))



  • 希尔排序 ( 时间复杂度 : O(n log n ) )

介绍:希尔排序也是插入排序,他是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。(有两种思路,一种在插入的时候采用交换法,一种采用移动法,其中,移动法的速度最快。)

function shellsort(arr) {
  let temp = 0
  let j = 0
  for (let step = arr.length / 2; step > 0; step = Math.floor(step / 2)) {
    for (let i = step; i < arr.length; i++) {
      j = i
      temp = arr[j]
      while (temp < arr[j - step] && j - step >= 0) {
        arr[j] = arr[j - step]
        j = j - step
      }
      arr[j] = temp
    }
  }
  return arr
}

console.log(shellsort(arr))



  • 快速排序 ( 时间复杂度 : O(n log n ) ) 介绍:是对冒泡排序的改叫。基本思想是通过一趟将要排序的数据分割成独立的两个部分,其中一个部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。
function quicksort(arr) {
  if(arr.length <= 1) return arr

  let left = []
  let right = []
  let pivotIndex = Math.floor(arr.length/2)
  let pivot = arr.splice(pivotIndex , 1)[0]    //这里是重点,如果改为
                                               //arr[pivotIndex],则会栈溢出报错
  for(let i = 0 ; i<arr.length ; i++) {
    if(arr[i] < pivot) {
      left.push(arr[i])
    }else{
      right.push(arr[i])
    }
  }

  return [...quicksort(left) , pivot , ...quicksort(right)]
}

console.log(quicksort(arr))



  • 归并排序 ( 时间复杂度 : O(n log n ) )

介绍:该算法采用分治策略。而分治,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,再利用递归求解。

function mergeSort(arr) {

  if (arr.length < 2) return arr

  let mid = Math.floor(arr.length / 2)
  let left = arr.slice(0, mid)
  let right = arr.slice(mid, arr.length)
  return merge(mergeSort(left), mergeSort(right))
}

function merge(left, right) {
  let result = []

  while (left.length > 0 && right.length > 0) {
    if (left[0] < right[0]) {
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }

  while (left.length) {
    result.push(left.shift())
  }
  while (right.length) {
    result.push(right.shift())
  }

  return result
}

let arr = [1, 4, 7, 1, 5, 3, 0, 15, 47, 20]

console.log(mergeSort(arr))



  • 桶排序 ( 时间复杂度 : O(n log n ) )

介绍:将数组分到有限数量的桶里。每个桶再个别排序(可能在使用其他的排序算法),最后依次把各个桶中的记录列出来便得到了有序序列

function binsort(arr, binsize) {
  let min = Math.min(...arr)
  let max = Math.max(...arr)
  let defaultbinsize = 5

  binsize = binsize || defaultbinsize
  let bincount = Math.floor((max - min) / binsize) + 1  
  let bin = new Array(bincount)
  for (let i = 0; i < bin.length; i++) {
    bin[i] = []
  }
  for (let j = 0; j < arr.length; j++) {
    bin[Math.floor((arr[j] - min) / binsize)].push(arr[j])    //这里是最关键的
  }

  arr.length = 0
  for (let i = 0; i < bin.length; i++) {
    bin[i].sort((a, b) => a - b)
    for (let j = 0; j < bin[i].length; j++) {
      arr.push(bin[i][j])
    }
  }

  return arr
}

console.log(binsort(arr))



查找算法

  • 二分查找

介绍:二分法查找,也称折半查找,是一种在有序数组中查找特定元素的搜索算法。查找过程可以分为以下步骤:

  1. 首先,从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步。
  2. 如果目标元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作。
  3. 如果某一步数组为空,则表示找不到目标元素。
  function Tsearch(arr, val) {
    if (arr.length == 0) return -1
    let left = 0
    let right = arr.length - 1
    let res = []

    while (left <= right) {
      let min = parseInt((right + left) / 2)
      if (val > arr[min]) {
        left = min + 1
      } else if (val < arr[min]) {
        right = min - 1
      } else {
        let temp = min - 1
        while (true) {
          if (temp < 0 || arr[temp] != val) {
            break
          } else {
            res.push(temp)
            temp--
          }
        }
        res.push(min)
        temp = min + 1
        while (true) {
          if (temp > right || arr[temp] !== val) {
            break
          } else {
            res.push(temp)
            temp++
          }
        }
        return res
      }
    }
    return -1
  }
  console.log(Tsearch(arr, 400))



  • 斐波那契查找 介绍:用斐波那契数列对数组进行分割,也叫做黄金比例分割。
  // 先求出一个斐波那契数列
  
  function fbi(len) {
    if (len < 0) return -1
    let res = []
    res[0] = 1
    let p = 0
    let q = 0
    let sum = 1
    for (let i = 1; i < len; i++) {
      p = q
      q = sum
      sum = sum + p
      res[i] = sum
    }
    return res
  }


// 用斐波那契数列进行分割

  function fabonaciiSearch(arr, key) {
    let low = 0
    let hight = arr.length - 1
    let min = 0
    let k = 0
    let f = fbi(20)
    while (hight > f[k] - 1) {
      k++
    }
    let temp = arr
    temp.length = f[k]
    temp.fill(arr[arr.length - 1], arr.length)

    while (low <= hight) {
      min = low + f[k - 1] - 1
      if (key > arr[min]) {
        low = min + 1
        k -= 2
      } else if (key < arr[min]) {
        hight = min - 1
        k -= 1
      } else {
        if (min < hight) {
          return min
        } else {
          return hight
        }
      }
    }

    return -1
  }

  console.log(fabonaciiSearch(arr, 600))



广度优先遍历(BFS)

介绍:从二叉树的第一层(根结点)开始,自上至下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问。

数据结构:队列。

父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点即可

  let tree = {
    value: 0,
    chilLeft: {
      value: 1,
      chilLeft: {
        value: 3
      },
      chilRight: {
        value: 4
      }
    },
    chilRight: {
      value: 2,
      chilLeft: {
        value: 5
      },
      chilRight: {
        value: 6
      }
    }
  }

  function wid(tree) {
    let list = []
    let queue = [tree]

    while(queue.length !== 0) {
      let target = queue.shift()
      list.push(target.value)
      if(target.chilLeft) {
        queue.push(target.chilLeft)
      } 
      if(target.chilRight) {
        queue.push(target.chilRight)
      } 
    }

    return list
  }
  
  //输出结果是 0123456
  



深度优先遍历(DFS)

介绍:从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。

数据结构:栈

父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点即可

  let tree = {
    value: 0,
    chilLeft: {
      value: 1,
      chilLeft: {
        value: 3
      },
      chilRight: {
        value: 4
      }
    },
    chilRight: {
      value: 2,
      chilLeft: {
        value: 5
      },
      chilRight: {
        value: 6
      }
    }
  }


  function dep(tree) {
    let list = []
    let stack = [tree]

    while (stack.length !== 0) {
      let target = stack.pop()
      list.push(target.value)
      if (target.chilRight) {
        stack.push(target.chilRight)
      }
      if (target.chilLeft) {
        stack.push(target.chilLeft)
      }
    }

    return list
  }
  //输出结果为 0134256