1.排序

93 阅读2分钟
// 冒排
function sort(arr,l=0,r=arr.length-1){
  for(let i=l; i<r; i++){
    for(let j=r; j>i; j--){
      if(arr[j]<arr[j-1]) swap(arr,j,j-1)
    }
  }
}
// 选排
function sort(arr,l=0,r=arr.length-1){
  for(let i=min=l; min = i,i<r; i++){
    for(let j=r; j>i; j--){
      if(arr[j]<arr[min]) j=min
    }
    swap(arr,i,min)
  }
}
// 插排
function sort(arr,l=0,r=arr.length-1){
  for(let i=l; i<r; i++){
    for(let j=i+1;j>l;j--){
      if(arr[j]<arr[j-1]) swap(arr,j,j-1)
      else break
    }
  }
}
// 归排
function sort(arr,l=0,r=arr.length-1,m=(l+r)>>1){
  if(l>=r) return 
  sort(arr,l,m)
  sort(arr,m+1,r)
  merge(arr,l,m,r)
}
function merge(arr,l,m,r){
  let helpArr = []
  let p1=l
  let p2=m+1
  while(p1<=m&&p2<=r) helpArr.push(arr[p1]<arr[p2]?arr[p1++]:arr[p2++])
  while(p1<=m) helpArr.push(arr[p1++])
  while(p2<=r) helpArr.push(arr[p2++])
  while(l<=r) arr[l++] = helpArr.shift()
}

// 小和问题,左边有小值就累加
// 其实可以逆向思考:右边有大值,就累加所有大值个数*自身值=>在归排合并过程中进行累加
function sort(arr,l=0,r=arr.length-1,m = (l+r)>>1){
  if(l>=r) return 0 // 边界返回0
  return sort(arr,l,m) + sort(arr,m+1,r)+merge(arr,l,m,r)
}
function merge(arr,l,m,r){
  let count = 0 // 初始累加值
  let helpArr = []
  let p1 = l
  let p2 = m+1
  while(p1<=m&&p2<=r){
    if(arr[p1]<arr[p2]){
      count+= (r-p2+1)*arr[p1] // r-p2+1即获取右边大值个数
      helpArr.push(arr[p1++])
    }
    else {
      helpArr.push(arr[p2++])
    }
  }
  while(p1<=m) helpArr.push(arr[p1++])
  while(p2<=r) helpArr.push(arr[p2++])
  while(l<=r) arr[l++] = helpArr.shift()

  return count
}
// 荷兰国旗问题:[<n,==n,>n]
function partition(arr, n, l=0, r=arr.length-1) {
  let min = l-1
  let max = r+1
  while (l < max) {
    if (arr[l] < n) swap(arr, l++, ++min)
    else if (arr[i] > n) swap(arr, l, --max)
    else l++
  }
}

// 快排
function sort(arr, l=0, r=arr.length-1) {
  if(l>=r) return
  let [min, max] = partition(arr, l, r)
  sort(arr, l, min)
  sort(arr,max,r)
}
function partition(arr,l,r) { // 荷兰国旗
  let min = l - 1
  let max = r + 1
  let p = arr[(r+l)>>1] // 取中间项
  while (l < max) {
    if (arr[l] < p) swap(arr, ++min, l++)
    else if (arr[l] > p) swap(arr, --max, l)
    else l++
  }
  // 对于重复项多的数组,适合使用快排
  return [min, max] // 小区下边界,大区上边界
}
// 堆排
// 左孩子 i*2+1
// 右孩子 i*2+2
// 父节点 (i-1)>>1

function heapSort(arr,l=0,r=arr.length-1) {
  if(l>=r) return 
  // for(let i =(r-1)>>1;i>=l;i--) heapify(arr,i,r)
  for(let i=l;i<=r;i++) heapInsert(arr,i)
  swap(arr,l,r--)
  while(r>l){
    heapify(arr,l,r)
    swap(arr,l,r--)
  }
}
function heapify(arr,i,hs) { // 向下对比,生成大根堆
  while(i*2+1<=hs){
    let max = i*2+2<=hs&&arr[i*2+2]>arr[i*2+1] ? i*2+2 : i*2+1 // 最大的孩子索引
    if(arr[i]<arr[max]){
      swap(arr,i,max)
      i = max
    }
    else break
  }
}
function heapInsert(arr,i){ // 向上对比,生成大根堆
  while(i>0&&arr[i]>arr[(i-1)>>1]){ // arr[(i-1)>>1] 父节点
    swap(arr,i,(i-1)>>1)
    i = (i-1)>>1
  }
}
// 基数排序
function sort(arr,l=0,r=arr.length-1) {
  if (arr.length < 2) return 
  let bit = getMaxBit(arr, l, r)
  let radix = 10
  let help = []
  let count =[]
  let i = d = 0
  for (d = 1; d <= bit; d++){
    count = new Array(radix).fill(0)
    for (i = l; i <= r; i++) count[getDigit(arr[i], d)]++ // 统计频次
    for (i = 1; i < radix; i++) count[i] += count[i - 1] // 前累加频次
    // 一定要倒序,因为上一轮循环中把前一进位上值最大的放在了最后,
    // 倒序能确保在本轮循环中当前进位相同时,将前一进位最大的放最后
    for (i = r; i >= l; i--) help[--count[getDigit(arr[i], d)]] = arr[i] 
    for (i = l; i <= r; i++) arr[i] = help.shift() // 刷新数组
  }
}
function getMaxBit(arr,l,r) { // 获取最大进位
  let maxNum = arr[l++]
  while (l <= r) maxNum = Math.max(arr[l++], maxNum)
  return String(maxNum).length
}
function getDigit(n,d) { // 获取进位上的数字
  return Math.floor(n / Math.pow(10, d - 1)) % 10
}
// Master公式
// T(N) = a*T(N/b) + O(N^d)
// log(b,a) > d ==> T(N) = N^log(b,a)
// log(b,a) < d ==> T(N) = N^d
// log(b,a) = d ==> T(N) = N^d*logN // 常见情况

//二分查找
function binarySearch(arr,n,l=0,r=arr.length-1){
  let m = 0
  while(l<=r){
    m = (r+l)>>1
    if(n<arr[m]) r = m-1
    else if(n>arr[m]) l = m+1
    else return m
  }
  return -1
}
// 时间复杂度可以根据Master公式算出
// T(N) = a*T(N/b) + O(N^d) = 1*T(N/2) + O(N^0)
// log(b,a) = log(2,1) = 0 
// d = 0 
// log(b,a) = d ==>T(N) = N^d*logN = logN

// ^: 异或,无进位相加

// 在一个数组里面有一个数出现了奇数次,其他的数都出现的次数为偶数次,现要找到这个数
// a^a=0
// a^b^c=a^c^b
// a^0=a
function getSingleNum(arr){
  let res = 0
  for(let i =0;i<arr.length;i++) res^=arr[i]
  return res
}
// 在一个数组里面有两个数出现了奇数次,其他的数都出现的次数为偶数次,现要找到这两个数
function getSingleNums(arr){
  let temp = 0 // a^b
  arr.forEach(num=>temp^=num)
  let temp2 = temp&(~temp+1) // temp最右1
  let res1 = 0
  let res2 = 0
  arr.forEach(num=>{
    if(num&temp2==1) res1^=num
    else res2^=num
  })
  return [res1,res2]
}