【筆記】JavaScript版數據結構與算法——基礎算法之“排序類”(164. 最大間距)


最大間距

1.題目

164. 最大間距 - 力扣(LeetCode)

給定一個無序的數組,找出數組在排序之後,相鄰元素之間最大的差值。
如果數組元素個數小於 2,則返回 0。

示例 1

輸入: [3,6,9,1]
輸出: 3
解釋: 排序後的數組是 [1,3,6,9], 其中相鄰元素 (3,6) 和 (6,9) 之間都存在最大差值 3。

示例 2

輸入: [10]
輸出: 0
解釋: 數組元素個數小於 2,因此返回 0。

說明:

  • 你可以假設數組中所有元素都是非負整數,且數值在 32 位有符號整數範圍內。
  • 請嘗試在線性時間複雜度和空間複雜度的條件下解決此問題。

題目模板

/**
 * @param {number[]} nums
 * @return {number}
 */
var maximumGap = function(nums) {

};

2.思路分析

傳統方法:先排序,再遍歷一遍,遍歷過程中找到最大差值
升級後:在排序過程中找到最大差值

3.所用到的方法

見題解

4.題解及優化

傳統方法:

let maximumGap = (nums) => {
  let maxGap = 0
  if (nums.length < 2) return maxGap
  // 排序
  for (let i = nums.length - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      [nums[j], nums[j + 1]] = nums[j] > nums[j + 1] ? [nums[j + 1], nums[j]] : [nums[j], nums[j + 1]]
    }
  }
  nums.reduce((pre, cur) => {
    maxGap = cur - pre > maxGap ? cur - pre : maxGap
    return cur
  })
  return maxGap
}

在這裏插入圖片描述


在排序過程中緩存最大間距:

  let maxGap = 0
  if (nums.length < 2) return maxGap
  if (nums.length === 2) return nums[1] - nums[0] > maxGap ? nums[1] - nums[0] : maxGap
  // 排序
  for (let i = nums.length - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      [nums[j], nums[j + 1]] = nums[j] > nums[j + 1] ? [nums[j + 1], nums[j]] : [nums[j], nums[j + 1]]
    }
    if (i < nums.length - 1) maxGap = nums[i + 1] - nums[i] > maxGap ? nums[i + 1] - nums[i] : maxGap
  }
  return maxGap

在這裏插入圖片描述
結果並不如意


使用基數排序

let maximumGap = (nums) => {
  let maxGap = 0
  if (nums.length < 2) return maxGap

  let mod = 10 // 進制/基數,常見還有以radix爲變量名
  let dev = 1 // 用來取進制位數據
  let maxDigit = Math.max(...nums).toString().length // 最大位數
  let counter = []
  for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
    for (let j = 0; j < nums.length; j++) {
      // 提取相應進制位的數據(個位/十位/百位...),相當於桶排序的桶的編號
      let bucket = Math.floor((nums[j] % mod) / dev)
      // 初始化統計數組
      if (counter[bucket] == null) {
        counter[bucket] = []
      }
      // 對應入組(這步相當於排序),利用LSD(次位優先)進行基數排序
      counter[bucket].push(nums[j])
    }
    let pos = 0
    for (let j = 0; j < counter.length; j++) {
      let value = null
      if (counter[j] != null) {
        while ((value = counter[j].shift()) != null) {
          nums[pos++] = value
        }
      }
    }
    // console.log('nums', nums) // 這裏可以得到按個位、十位...排序結果
  }
  nums.reduce((pre, cur) => {
    maxGap = cur - pre > maxGap ? cur - pre : maxGap
    return cur
  })
  return maxGap
}

在這裏插入圖片描述
還不錯


結合桶排序和鴿籠原理:

let maximumGap = (nums) => {
  if (nums.length < 2) return 0
  let min = Math.min(...nums) // 最小值
  let max = Math.max(...nums) // 最大值
  if (max - min === 0) return 0
  let gap = Math.ceil((max - min) / (nums.length - 1)) // 均差(兩兩差值的平均值向下取整)
  // 桶(區間)下限、桶(區間)上限(填充的目的是跳過空桶(區間))
  let bucketsMin = new Array(nums.length - 1).fill(max) // Number.MAX_SAFE_INTEGER
  let bucketsMax = new Array(nums.length - 1).fill(min)
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] === min || nums[i] === max) continue // 桶中不容納最大最小兩個值
    let idx = Math.floor((nums[i] - min) / gap) // 定位正確的桶
    // 核心步驟:統計過程中只留下桶(區間)上下限,去掉其他無關中間元素(最大間距一定是大於均差的,一定產生在邊緣值之差之間)
    bucketsMin[idx] = Math.min(nums[i], bucketsMin[idx]) // 統計桶(區間)下限
    bucketsMax[idx] = Math.max(nums[i], bucketsMax[idx]) // 統計桶(區間)上限
  }
  let maxGap = 0 // 存儲最大間隔
  let pre = min
  for (let i = 0; i < nums.length - 1; i++) {
    if (bucketsMax[i] === min) continue // 跳過空桶(區間)
    // 核心步驟:統計邊緣值之差的最大值(還有一步在循環外)
    maxGap = Math.max(maxGap, bucketsMin[i] - pre) // 本區間下限 與 上一個區間(空區間略過)上限 差值 的 最大值(第一個區間下限與最小值差值也在比較之中)
    pre = bucketsMax[i]
  }
  maxGap = Math.max(maxGap, max - pre) // 最後非空一個區間下限與最大值差值也參與比較
  return maxGap
}

在這裏插入圖片描述

鴿籠原理也稱抽屜原理:n 個物品放入 m 個容器中,如果 n > m 那麼一定有一個容器裝有至少兩個物品。同理n個桶,放m個物體,如果 n > m 那麼一定有一個容器是空桶。

假設對於數組中的任意一個元素都有一個桶,那麼每個元素恰好佔據一個桶。現在增加桶的個數,必然會有至少一個桶是空桶,那麼這個空桶附近的兩個元素之差就是我們想要的結果

代碼中將兩個最值放到最兩邊的桶內,剩下 數組元素個數-1 個桶,在元素入桶過程中不免會有,多個元素入一個桶的情況發生,因此桶內只放屬於這個桶的最大值和最小值(區間上下限),然後尋找空桶的過程就是:

  • 本區間下限 與 上一個區間(空區間略過)上限 差值 的 最大值
  • 第一個區間下限與最小值差值也在比較之中
  • 最後非空一個區間下限與最大值差值也參與比較

可以這樣理解:最大間隔一定是大於平均間隔的((maxmin)/(n1)(max-min)/(n-1)),而平均間隔就是桶的容量。

在這裏插入圖片描述
在這裏插入圖片描述
圖片來自:LeetCode-164.MaximumGap(最大間距)
———————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章