LeetCode 演算法題系列(第一週 25道)

叫我詹躲躲發表於2021-10-03

【演算法進度 213/400 (〃'▽'〃)】,繼續加油!

136. 只出現一次的數字

給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。

說明:
你的演算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?

示例 1:

輸入: [2,2,1]
輸出: 1

示例 2:
輸入: [4,1,2,1,2]
輸出: 4

雜湊表

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function (nums) {
    let hash = {}
    for (let i = 0; i < nums.length; i++) {
        hash[nums[i]] ? hash[nums[i]]++ : hash[nums[i]] = 1
    }
    for (let j in hash) {
        if (hash[j] === 1) {
            return j
        }
    }
};

異或

var singleNumber = function (nums) {
    let ans = nums[0]
    for (let i = 1; i < nums.length; i++) {
        ans = ans ^ nums[i]
    }
    return ans
};

278. 第一個錯誤的版本

你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有通過質量檢測。由於每個版本都是基於之前的版本開發的,所以錯誤的版本之後的所有版本都是錯的。

假設你有 n 個版本 [1, 2, ..., n],你想找出導致之後所有版本出錯的第一個錯誤的版本。
你可以通過呼叫 bool isBadVersion(version) 介面來判斷版本號 version 是否在單元測試中出錯。實現一個函式來查詢第一個錯誤的版本。你應該儘量減少對呼叫 API 的次數。

示例 1:
輸入:n = 5, bad = 4
輸出:4
解釋:
呼叫 isBadVersion(3) -> false 
呼叫 isBadVersion(5) -> true 
呼叫 isBadVersion(4) -> true
所以,4 是第一個錯誤的版本。

示例 2:
輸入:n = 1, bad = 1
輸出:1

二分法

var solution = function (isBadVersion) {
    return function (n) {
        let left = 1, right = n
        while (left < right) {
            const mid = Math.floor(left + (right - left) / 2)
            if (isBadVersion(mid)) {
                right = mid
            } else {
                left = mid + 1
            }
        }
        return left
    };
};

704. 二分查詢

給定一個 n 個元素有序的(升序)整型陣列 nums 和一個目標值 target ,寫一個函式搜尋 nums 中的 target,如果目標值存在返回下標,否則返回 -1。

示例 1:

輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中並且下標為 4

示例 2:

輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1

提示:

    你可以假設 nums 中的所有元素是不重複的。
    n 將在 [1, 10000]之間。
    nums 的每個元素都將在 [-9999, 9999]之間。

二分法

var search = function (nums, target) {
    let low = 0, high = nums.length - 1
    while (low <= high) {
        const mid = Math.floor((high - low) / 2) + low
        const num = nums[mid]
        if (num === target) {
            return mid
        } else if (num > target) {
            high = mid - 1
        } else if (num < target) {
            low = mid + 1
        }
    }
    return -1
};

findIndex

var search = function(nums, target) {
    return nums.findIndex(v=>v===target)
};

indexOf

var search = function(nums, target) {
    return nums.indexOf(target)
};

for迴圈

var search = function (nums, target) {
    for (let i = 0; i < nums.length; i++) {
        if(nums[i] === target) return i
    }
    return -1
};

35. 搜尋插入位置

給定一個排序陣列和一個目標值,在陣列中找到目標值,並返回其索引。如果目標值不存在於陣列中,返回它將會被按順序插入的位置。

請必須使用時間複雜度為 O(log n) 的演算法。

示例 1:
輸入: nums = [1,3,5,6], target = 5
輸出: 2

示例 2:
輸入: nums = [1,3,5,6], target = 2
輸出: 1

示例 3:
輸入: nums = [1,3,5,6], target = 7
輸出: 4

示例 4:
輸入: nums = [1,3,5,6], target = 0
輸出: 0

示例 5:
輸入: nums = [1], target = 0
輸出: 0

提示:

    1 <= nums.length <= 104
    -104 <= nums[i] <= 104
    nums 為無重複元素的升序排列陣列
    -104 <= target <= 104

filter

var searchInsert = function (nums, target) {
    return nums.filter(v => v < target).length
};

二分法

var searchInsert = function (nums, target) {
    let len = nums.length
    if (len === 0) return 0
    let left = 0, right = len-1
    while (left < right) {
        // const mid = (left + right) >> 1
        const mid = Math.floor((right - left) / 2 + left)
        if (nums[mid] >= target) {
            right = mid
        } else if(nums[mid] < target) {
            left = mid + 1
        }
    }
    if (nums[right] < target) {
        return right + 1
    }
    return right
};

977. 有序陣列的平方

給你一個按 非遞減順序 排序的整數陣列 nums,返回 每個數字的平方 組成的新陣列,要求也按 非遞減順序 排序。

示例 1:
輸入:nums = [-4,-1,0,3,10]
輸出:[0,1,9,16,100]
解釋:平方後,陣列變為 [16,1,0,9,100]
排序後,陣列變為 [0,1,9,16,100]

示例 2:
輸入:nums = [-7,-3,2,3,11]
輸出:[4,9,9,49,121]

提示:
    1 <= nums.length <= 104
    -104 <= nums[i] <= 104
    nums 已按 非遞減順序 排序
    
進階:

    請你設計時間複雜度為 O(n) 的演算法解決本問題

雙指標

var sortedSquares = function (nums) {
    let i = 0, j = nums.length - 1, res = [], k = nums.length - 1
    while (i <= j) {
        if (nums[i] * nums[i] > nums[j] * nums[j]) {
            res[k--] = nums[i] * nums[i++]
        } else {
            res[k--] = nums[j] * nums[j--]
        }
    }
    return res
};

reduce+sort

var sortedSquares = function (nums) {
    return nums.reduce((acc, prev) => acc.concat(prev * prev), []).sort((a,b)=>a-b)
};

896. 單調數列

如果陣列是單調遞增或單調遞減的,那麼它是單調的。如果對於所有 i <= j,A[i] <= A[j],那麼陣列 A 是單調遞增的。 如果對於所有 i <= j,A[i]> = A[j],那麼陣列 A 是單調遞減的。當給定的陣列 A 是單調陣列時返回 true,否則返回 false。

示例 1:
輸入:[1,2,2,3]
輸出:true

示例 2:
輸入:[6,5,4,4]
輸出:true

示例 3:
輸入:[1,3,2]
輸出:false

示例 4:
輸入:[1,2,4,5]
輸出:true

示例 5:
輸入:[1,1,1]
輸出:true

提示:

    1 <= A.length <= 50000
    -100000 <= A[i] <= 100000
var isMonotonic = function (nums) {
    let inc =true,dec = true
    for(let i=0;i<nums.length;i++){
        if(nums[i+1]-nums[i]>0){
            dec = false
        }
        if(nums[i]-nums[i+1]>0){
            inc = false
        }
    }
    return dec || inc
};

941. 有效的山脈陣列

給定一個整數陣列 arr,如果它是有效的山脈陣列就返回 true,否則返回 false。

讓我們回顧一下,如果 A 滿足下述條件,那麼它是一個山脈陣列:

    arr.length >= 3
    在 0 < i < arr.length - 1 條件下,存在 i 使得:
        arr[0] < arr[1] < ... arr[i-1] < arr[i]
        arr[i] > arr[i+1] > ... > arr[arr.length - 1]
示例 1:
輸入:arr = [2,1]
輸出:false

示例 2:
輸入:arr = [3,5,5]
輸出:false

示例 3:
輸入:arr = [0,3,2,1]
輸出:true

提示:

    1 <= arr.length <= 104
    0 <= arr[i] <= 104

雙指標

const validMountainArray = (A) => {
    const n = A.length;
    let i = 0;
    let j = n - 1;

    while (i + 1 < n && A[i] < A[i + 1]) {
        i++;
    }
    while (j - 1 >= 0 && A[j - 1] > A[j]) {
        j--;
    }
    if (i != 0 && i == j && j != n - 1) {
        return true;
    }
    return false;
};

167. 兩數之和 II - 輸入有序陣列

給定一個已按照 非遞減順序排列 的整數陣列 numbers ,請你從陣列中找出兩個數滿足相加之和等於目標數 target 。

函式應該以長度為 2 的整數陣列的形式返回這兩個數的下標值。numbers 的下標 從 1 開始計數 ,所以答案陣列應當滿足 1 <= answer[0] < answer[1] <= numbers.length 。

你可以假設每個輸入 只對應唯一的答案 ,而且你 不可以 重複使用相同的元素。

示例 1:
輸入:numbers = [2,7,11,15], target = 9
輸出:[1,2]
解釋:2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。

示例 2:
輸入:numbers = [2,3,4], target = 6
輸出:[1,3]

示例 3:
輸入:numbers = [-1,0], target = -1
輸出:[1,2]

提示:

    2 <= numbers.length <= 3 * 104
    -1000 <= numbers[i] <= 1000
    numbers 按 非遞減順序 排列
    -1000 <= target <= 1000
    僅存在一個有效答案

雙指標

var twoSum = function (numbers, target) {
    let i = 0, j = numbers.length-1;
    while (i <= j) {
        if (numbers[i] + numbers[j] > target) {
            j--
        } else if (numbers[i] + numbers[j] === target) {
            return [++i, ++j]
        } else {
            i++
        }
    }
};

1984. 學生分數的最小差值

給你一個 下標從 0 開始 的整數陣列 nums ,其中 nums[i] 表示第 i 名學生的分數。另給你一個整數 k 。

從陣列中選出任意 k 名學生的分數,使這 k 個分數間 最高分 和 最低分 的 差值 達到 最小化 。
返回可能的 最小差值 。

示例 1:

輸入:nums = [90], k = 1
輸出:0
解釋:選出 1 名學生的分數,僅有 1 種方法:
- [90] 最高分和最低分之間的差值是 90 - 90 = 0
可能的最小差值是 0

示例 2:
輸入:nums = [9,4,1,7], k = 2
輸出:2
解釋:選出 2 名學生的分數,有 6 種方法:
- [9,4,1,7] 最高分和最低分之間的差值是 9 - 4 = 5
- [9,4,1,7] 最高分和最低分之間的差值是 9 - 1 = 8
- [9,4,1,7] 最高分和最低分之間的差值是 9 - 7 = 2
- [9,4,1,7] 最高分和最低分之間的差值是 4 - 1 = 3
- [9,4,1,7] 最高分和最低分之間的差值是 7 - 4 = 3
- [9,4,1,7] 最高分和最低分之間的差值是 7 - 1 = 6
可能的最小差值是 2

提示:
    1 <= k <= nums.length <= 1000
    0 <= nums[i] <= 1
var minimumDifference = function (nums, k) {
    nums = nums.sort((a, b) => a - b)
    let ret = Infinity
    for (let i = 0; i + k - 1 < nums.length; i++) {
        if (nums[i + k - 1] - nums[i] < ret) {
            ret = nums[i + k - 1] - nums[i];
        }
    }
    return ret
};

1436. 旅行終點站

給你一份旅遊線路圖,該線路圖中的旅行線路用陣列 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示該線路將會從 cityAi 直接前往 cityBi 。請你找出這次旅行的終點站,即沒有任何可以通往其他城市的線路的城市。

題目資料保證線路圖會形成一條不存在迴圈的線路,因此恰有一個旅行終點站。

示例 1:
輸入:paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]]
輸出:"Sao Paulo" 
解釋:從 "London" 出發,最後抵達終點站 "Sao Paulo" 。本次旅行的路線是 "London" -> "New York" -> "Lima" -> "Sao Paulo" 。

示例 2:
輸入:paths = [["B","C"],["D","B"],["C","A"]]
輸出:"A"
解釋:所有可能的線路是:
"D" -> "B" -> "C" -> "A". 
"B" -> "C" -> "A". 
"C" -> "A". 
"A". 
顯然,旅行終點站是 "A" 。

示例 3:
輸入:paths = [["A","Z"]]
輸出:"Z"

提示:
    1 <= paths.length <= 100
    paths[i].length == 2
    1 <= cityAi.length, cityBi.length <= 10
    cityAi != cityBi
    所有字串均由大小寫英文字母和空格字元組成。

Set

var destCity = function(paths) {
    let ans = new Set()
    for(let i of paths){
        ans.add(i[0])
    }
    for(let j  of paths){
        if(!ans.has(j[1])){
            return j[1]
        }
    }
    return ''
};

876. 連結串列的中間結點

給定一個頭結點為 head 的非空單連結串列,返回連結串列的中間結點。

如果有兩個中間結點,則返回第二個中間結點。

示例 1:
輸入:[1,2,3,4,5]
輸出:此列表中的結點 3 (序列化形式:[3,4,5])
返回的結點值為 3 。 (測評系統對該結點序列化表述是 [3,4,5])。
注意,我們返回了一個 ListNode 型別的物件 ans,這樣:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:
輸入:[1,2,3,4,5,6]
輸出:此列表中的結點 4 (序列化形式:[4,5,6])
由於該列表有兩個中間結點,值分別為 3 和 4,我們返回第二個結點。

提示:
    給定連結串列的結點數介於 1 和 100 之間。
var middleNode = function(head) {
    let slow = head,fast = head;
    while(fast!==null && fast.next!==null){
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow
};

374. 猜數字大小

374. 猜數字大小

猜數字遊戲的規則如下:

每輪遊戲,我都會從 1 到 n 隨機選擇一個數字。 請你猜選出的是哪個數字。
如果你猜錯了,我會告訴你,你猜測的數字比我選出的數字是大了還是小了。

你可以通過呼叫一個預先定義好的介面 int guess(int num) 來獲取猜測結果,返回值一共有 3 種可能的情況(-1,1 或 0):

-1:我選出的數字比你猜的數字小 pick < num
1:我選出的數字比你猜的數字大 pick > num
0:我選出的數字和你猜的數字一樣。恭喜!你猜對了!pick == num

返回我選出的數字。

示例 1:
輸入:n = 10, pick = 6
輸出:6

示例 2:
輸入:n = 1, pick = 1
輸出:1

示例 3:
輸入:n = 2, pick = 1
輸出:1

示例 4:
輸入:n = 2, pick = 2
輸出:2

提示:
    1 <= n <= 231 - 1
    1 <= pick <= n

二分法

var guessNumber = function (n) {
    let left = 1, right = n
    while (left < right) {
        const mid = Math.floor((right - left) / 2 + left)
        if (guess(mid) <= 0) {
            right = mid
        } else {
            left = mid + 1
        }
    }
    return left
};

405. 數字轉換為十六進位制數

405. 數字轉換為十六進位制數

給定一個整數,編寫一個演算法將這個數轉換為十六進位制數。對於負整數,我們通常使用 補碼運算 方法。

注意:

十六進位制中所有字母(a-f)都必須是小寫。
十六進位制字串中不能包含多餘的前導零。如果要轉化的數為0,那麼以單個字元'0'來表示;對於其他情況,十六進位制字串中的第一個字元將不會是0字元。 
給定的數確保在32位有符號整數範圍內。
不能使用任何由庫提供的將數字直接轉換或格式化為十六進位制的方法。
示例 1:
輸入:
26

輸出:
"1a"

示例 2:
輸入:
-1

輸出:
"ffffffff"
var toHex = function (num) {
    const hex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f']
    if (num === 0) {
        return '0'
    }
    let ans = "";
    if (num < 0) {
        num = Math.pow(2, 32) - Math.abs(num);
    }
    while (num) {
        ans += hex[num % 16];
        num = Math.floor(num / 16);
    }
    return ans.split("").reverse().join("");
};

643. 子陣列最大平均數 I

給你一個由 n 個元素組成的整數陣列 nums 和一個整數 k 。請你找出平均數最大且 長度為 k 的連續子陣列,並輸出該最大平均數。

任何誤差小於 10-5 的答案都將被視為正確答案。

示例 1:
輸入:nums = [1,12,-5,-6,50,3], k = 4
輸出:12.75
解釋:最大平均數 (12-5-6+50)/4 = 51/4 = 12.75

示例 2:

輸入:nums = [5], k = 1
輸出:5.00000

提示:

    n == nums.length
    1 <= k <= n <= 105
    -104 <= nums[i] <= 104
var findMaxAverage = function (nums, k) {
    let max = ans = [...nums].slice(0, k).reduce((acc, prev) => acc += prev);
    for (let i = 1; i <= nums.length - k; i++) {
        ans = ans - nums[i - 1] + nums[i + k - 1]
        max = Math.max(ans, max)
    }
    return max / k
};

283. 移動零

給定一個陣列 nums,編寫一個函式將所有 0 移動到陣列的末尾,同時保持非零元素的相對順序。

示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]

說明:
    必須在原陣列上操作,不能拷貝額外的陣列。
    儘量減少操作次數。
var moveZeroes = function (nums) {
    let i = 0, j = 0;
    while (i < nums.length) {
        if (nums[i] != 0) {
            nums[j++] = nums[i]
        }
        i++
    }
    for (let a = j; a < nums.length; a++) {
        nums[a] = 0
    }
    return nums
};

相關文章