JS版数据结构之 排序(冒泡、选择、插入、希尔、归并、快速)

冒泡排序

冒泡排序是升序排列
核心思想:让数组的当前项和后一项进行比较,如果当前项比后一项大,就交换位置。
时间复杂度:O(n^2)
空间复杂度:O(1)

const arr = [12, 8, 24, 16, 1, 34, 23, 12, 54]

const bubble = (arr) => {
    
    
  // 控制比较的轮数
  for (let i = 0; i < arr.length - 1; i++) {
    
    
    for (let j = 0; j < arr.length - 1 - i; j++) {
    
    
      if (arr[j] > arr[j + 1]) {
    
    
        // 解构赋值
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr
}
console.log(bubble(arr))

选择排序

核心思想:选择第一个索引的位置,和后面的元素依次比较,如果后面的元素小于第一个元素,记录索引,此轮结束时交换位置,经过一轮比较后可以选出此轮的最小值。
时间复杂度:O(n^2)
空间复杂度:O(1)

const selectSore = (arr) => {
    
    
  let min
  for (let i = 0; i < arr.length - 1; i++) {
    
    
    min = i
    for (let j = i + 1; j < arr.length; j++) {
    
    
      if (arr[min] > arr[j]) {
    
    
        min = j
      }
    }
    [arr[i], arr[min]] = [arr[min], arr[i]]
  }
  return arr
}

插入排序

插入排序是简单排序(冒泡、选择、插入),插入排序是效率最高的。

核心思想

  1. 从第一个元素开始,这个元素可以认为已经被排序(循环的下表从1开始,0默认已被排序)
  2. 取出下一个元素,在已经排序的元素中,从后向前遍历
  3. 如果这个元素大于新的元素,就把这个元素移到下一个位置
  4. 重复上一个步骤,直到已经排序的元素小于或等于新元素的位置
  5. 将新元素插入到这个位置以后,重复上面的步骤

时间复杂度:O(n^2)
空间复杂度:O(1)

const insertSort = (arr) => {
    
    
  // 准备一个新数组,用来存放我们找到的牌
  const handle = []
  handle.push(arr[0])

  // 从第二张牌开始,依次抓牌
  for (let i = 1; i < arr.length; i++) {
    
    
    let A = arr[i]
    for (let j = handle.length - 1; j >= 0; j--) {
    
    
      let B = handle[j]
      if (A > B) {
    
    
        handle.splice(j + 1, 0, A)
        break
      }
      // 把抓取的树放到最前面,因为比它大的都插入了
      if(j === 0){
    
    
        handle.unshift(A)
      }
    }
  }
  return handle
}


const insertSortTwo = (arr) => {
    
    
  let temp 
  for (let i = 0; i < arr.length; i++) {
    
    
    // 记录选出的元素,放在temp中
    let j = i
    temp = arr[i]

    // 内层循环,当前值和之前的值进行比较,发现有比当前值小的值就重新赋值
    while (j > 0 && arr[j - 1] > temp) {
    
    
      arr[j] = arr[j - 1]
      j--
    }
    // 选出j的位置放到temp中
    arr[j] = temp
  }
  return arr
}

希尔排序

希尔排序本质是一种插入排序,但是他对数组进行了等间隔的分组处理,在每一组中做插入排序。
核心思想:按一定的间隔对数组进行分组,在每一个分组中进行插入排序,把较小的值放到靠前的位置;随后逐次缩小间隔,继续进行插入排序,直到间隔为1再做插入排序后结束。
时间复杂度:O(nlogn)


const shellSort = (arr) => {
    
    
  let gap = Math.floor(arr.length - 1)

  // 间隔不断变小
  while (gap > 0) {
    
    
    // 插入排序
    for (let i = 0; i < arr.length; i++) {
    
    
      // 记录选出的元素,放在temp中
      let j = i
      temp = arr[i]

      // 内层循环,当前值和之前的值进行比较,发现有比当前值小的值就重新赋值
      while (j > 0 && arr[j - 1] > temp) {
    
    
        arr[j] = arr[j - 1]
        j--
      }
      // 选出j的位置放到temp中
      arr[j] = temp
    }
    // 重新计算间隔
    gap = Math.floor(gap / 2)
  }
  return arr
}

归并排序

拆分和归并
核心思想:把数组分成前后两部分,然后分别进行排序,再将两部分合并在一起。

// 合并
const merge = (left, right) => {
    
    
  let temp = []
  while (left.length > 0 && right.length > 0) {
    
    
    if (left[0] <= right[0]) {
    
    
      temp.push(left.shift())
    }
    else {
    
    
      temp.push(right.shift())
    }
  }
  return temp.concat(left, right)
}
// 拆分
const mergeSort = (arr) => {
    
    
  if (arr.length === 1) return arr
  let mid = Math.floor(arr.length / 2)
  let left = arr.slice(0, mid)
  let right = arr.slice(mid)
  return merge(mergeSort(left), mergeSort(right))
}

console.log(mergeSort(arr))

快速排序

分治法:将原本的问题拆分成规模更小但是与之相似的子问题,递归解决子问题,然后将子问题进行合并。
核心思想

  1. 选择一个元素作为基准(privot),所有比基准小的放在元素放在基准左侧,所有比基准大的元素放在基准右边,分区操作结束后,基准所处的位置就是最后排序后的位置。
  2. 对基准左边和右边的两个子集,不断地重读第一步和第二步操作,直到最后只剩下最后一个元素为止。
const arr = [12, 8, 24, 16, 1, 34, 23, 12, 54]

const quickSort = (arr) => {
    
    
  if (arr.length <= 1) return arr

  // 中间索引
  const privotIndex = Math.floor(arr.length / 2)
  // 基准元素
  const privot = arr.splice(privotIndex, 1)[0]

  const left = []
  const right = []

  arr.map(item => {
    
    
    if (item < privot) {
    
    
      left.push(item)
    }
    else {
    
    
      right.push(item)
    }
  })

  return quickSort(left).concat(privot, quickSort(right))
}

console.log(quickSort(arr))

猜你喜欢

转载自blog.csdn.net/ladream/article/details/119935339