【前端】記錄自己在leetcode上的刷題之路


每日一題,加油沖!

1.只出現一次數字

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

解題思路1:利用Set對象的特性,只能存儲任意類型的唯一值,來篩選出重復的數據,再與保存的set對象里面的值進行比較

 1 var singleNumber = function(nums) {
 2     let mySet = new Set();
 3     let repeatList = [];
 4     for(let i = 0; i < nums.length; i++) {
 5         if (mySet.has(nums[i])) {
 6             repeatList.push(nums[i]);
 7         } else {
 8             mySet.add(nums[i])
 9         }
10         
11     }
12     //console.log("mySet",mySet);
13     //console.log("repeatList",repeatList);
14     const singleNum = Array.from(mySet).filter(item=>!repeatList.includes(item));
15     console.log("singleNum",singleNum);
16     return singleNum;
17 };
18 singleNumber([1,2,1,2,4,6,7,7,6]);

解題思路2:利用兩層循環,找到重復的數據,再和原數組比較

 1 /**
 2  * @param {number[]} nums
 3  * @return {number}
 4  */
 5 var singleNumber = function(nums) {
 6     let repeatList = [];
 7     for (let i = 0; i < nums.length; i++) {
 8         const outItem = nums[i];
 9         for(let j = i + 1; j < nums.length; j++){
10             const innerItem = nums[j];
11             if (outItem === innerItem) {
12                 repeatList.push(innerItem);            
         }
15 } 16 } 17 console.log("repeatList", repeatList); 18 const singleNum = nums.filter(item=>!repeatList.includes(item)) 19 console.log("singleNum", singleNum); 20 return singleNum[0]; 21 }; 22 singleNumber([1,2,2,1,3])

2.給定一個數組,將數組中的元素向右移動 k 個位置,其中 k 是非負數。

解題思路1:根據位置k,截取數組,再重新組合

1 
 /**
  * @param {number[]} nums
* @param {number} k
  * @return {void} Do not return anything, modify nums in-place instead.
*/

var rotate = function(nums, k) { 2 const len = nums.length 3 const index = len? len - k : len 4 const arr = nums.splice(index) 5 const dataset = arr.concat(nums) 6 dataset.forEach((item,index)=>{ 7 nums[index] = item 8 }) 9 };

解題思路2:無限輪播思想,復制一份原數組,再與原數組組合形成新數組

1 
 /** 
  * @param {number[]} nums
* @param {number} k
  * @return {void} Do not return anything, modify nums in-place instead.
*/
  var rotate = function(nums, k) {
2     const len = nums.length
3     const copyNums = [...nums,...nums]
4     const index = len ? len - k%len : len
5     const _nums = copyNums.splice(index, len)
6     for(let i = 0; i < _nums.length; i++){
7         nums[i] = _nums[i]
8     }
9 };

 3.兩個數組的交集

  • 輸出結果中每個元素出現的次數,應與元素在兩個數組中出現次數的最小值一致。
  • 我們可以不考慮輸出結果的順序
  • 例:輸入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
       輸出:[4,9]

解題思路:先找出交集元素,然后統計交集元素在每個數組的出現次數,最后輸出結果

 

// 如果有交集,獲取交集元素在當前數組的出現次數
function getTimes(data=[], num){
    let times = 0
    data.forEach(item=>{
        if (item === num) {
            times+=1
        }
    })
    return times
}

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function(nums1, nums2) {
    let dataMap= {}; // 儲存數組交集
    let data = []; // 返回交集
    for(let i = 0; i < nums1.length; i++) {
        const outItem = nums1[i];
        let crossObj = {}
        for(let j = 0; j < nums2.length; j++) {
            const innerItem = nums2[j]
            if (outItem === innerItem) {
                crossObj = {
                    num: outItem,
                    num1Times: getTimes(nums1, outItem), 
                    num2Times: getTimes(nums2, innerItem)
                }
            }
        }
        if (Object.keys(crossObj).length) {
            dataMap[crossObj.num] = crossObj
        }
    }
    for (let key in dataMap) {
        const val = dataMap[key]
        const len = Math.min(val.num1Times, val.num2Times)
        for (let m = 0; m < len; m++) {
            data.push(val.num)
        }
    }
    return data
};
intersect([1,2,2,1], [2,2]) // 返回結果[2,2]

  4.K連續位的翻轉次數

  •  在僅包含 0 和 1 的數組 A 中,一次 K 位翻轉包括選擇一個長度為 K 的(連續)子數組,同時將子數組中的每個 0 更改為 1,而每個 1 更改為 0。

返回所需的 K 位翻轉的最小次數,以便數組沒有值為 0 的元素。如果不可能,返回 -1。

解題思路:遍歷數組A,遇到為0的開始翻轉K個長度

 1 /**
 2  * @param {number[]} A
 3  * @param {number} K
 4  * @return {number}
 5  */
 6 var minKBitFlips = function(A, K) {
 7     const len = A.length
 8     let count = 0;
 9     for(let i = 0; i <len; i++) {
10         // 開始翻轉K個長度
11         if (A[i] === 0 && len - i >= K) {
12             count++
13             for(let j = i; j < i + K; j++){
14                 A[j] = A[j]^1 // 異或運算符 reverseNum(A[j])
15             }
16 
17         }
18     }
19     console.log(A)
20     if (A.includes(0)) {
21         count = -1
22     }
23     return count
24 };
25 
26 function reverseNum(num){
27     return typeof num === 'number'  
28         ? num === 1 ? 0 : 1 
29         : num
30 }

 

 5.托普利茨矩陣(20210222)

 

  • 給你一個 m x n 的矩陣 matrix 。如果這個矩陣是托普利茨矩陣,返回 true ;否則,返回 false 。
  • 如果矩陣上每一條由左上到右下的對角線上的元素都相同,那么這個矩陣是 托普利茨矩陣 。

 

解題思路:各自與比較左上方數值比較,如果不相等則不是托普利茨矩陣

 1 /**
 2  * @param {number[][]} matrix
 3  * @return {boolean}
 4  */
 5 var isToeplitzMatrix = function(matrix) {
 6     const len = matrix.length
 7     const nLen = matrix[0].length
 8     // 各自跟左上角的數值比較
 9     for(let i = 1; i<len; i++) {
10         for(j = 1; j<nLen; j++) {
11             if(matrix[i][j]!== matrix[i-1][j-1]){
12                 return false
13             }
14         }
15     }
16     return true
17 };
18 
19 isToeplitzMatrix([[1,2,3,4],[5,1,2,3],[9,5,1,2]]) // true

 6.愛生氣的書店老板

  • 今天,書店老板有一家店打算試營業 customers.length 分鍾。每分鍾都有一些顧客(customers[i])會進入書店,所有這些顧客都會在那一分鍾結束后離開。在某些時候,書店老板會生氣。 如果書店老板在第 i 分鍾生氣,那么 grumpy[i] = 1,否則 grumpy[i] = 0。 當書店老板生氣時,那一分鍾的顧客就會不滿意,不生氣則他們是滿意的。書店老板知道一個秘密技巧,能抑制自己的情緒,可以讓自己連續 X 分鍾不生氣,但卻只能使用一次。請你返回這一天營業下來,最多有多少客戶能夠感到滿意的數量。
  • 示例:

輸入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
輸出:16
解釋:
書店老板在最后 3 分鍾保持冷靜。
感到滿意的最大客戶數量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.

解題思路:滑動窗口解法。先求不生氣時間內的顧客數,然后滑動X個,找出各挽留了多少顧客數,找出挽留顧客數的最大值,再相加即可得最大滿意顧客數

 1 /**
 2  * @param {number[]} customers
 3  * @param {number[]} grumpy
 4  * @param {number} X
 5  * @return {number}
 6  */
 7 var maxSatisfied = function(customers, grumpy, X) {
 8     // 思路:先求不生氣時間內的顧客數,然后滑動X個,找出各挽留了多少顧客數,找出挽留顧客數的最大值,再相加即可得最大滿意顧客數
 9     // 1.不生氣時顧客數
10     let cusNum = 0
11     for(let i = 0; i < grumpy.length; i++){
12         if (grumpy[i] === 0) {
13             cusNum+= customers[i]
14         }
15     }
16     // console.log("cusNum", cusNum)
17 
18     // 2.挽留的顧客數集合
19     const reserveList = []
20     for(let j = 0; j < grumpy.length; j++){
21         if (j + X <= grumpy.length) {
22             let num = 0
23             for(let k = 0; k < X; k++) {
24                 if (grumpy[j+k] === 1) {
25                     num += customers[j+k]
26                 }
27             }
28             reserveList.push(num)
29         }
30         
31     }
32     // console.log("reserveList", reserveList)
33     // 找出挽留顧客數的最大值
34     const reserve = Math.max(...reserveList)
35 
36     return cusNum + reserve
37 };

7.區域和檢索 - 數組不可變(難度指數:簡單

  • 給定一個整數數組  nums,求出數組從索引 i 到 ji ≤ j)范圍內元素的總和,包含 i兩點。

解題思路:先找出前綴和preSum,對於數組任意索引之間的總和sumRange,有sumRange = sums[j+1] - sums[i]

 1 /**
 2  * @param {number[]} nums
 3  */
 4 var NumArray = function(nums) {
 5     // 前綴和
 6     const len = nums.length
 7     this.preSum = new Array(len + 1).fill(0)
 8     for (let i = 0; i < len; i++) {
 9         this.preSum[i+1] = this.preSum[i] + nums[i]
10     }
11     console.log("preSum", this.preSum)
12 };
13 
14 /** 
15  * @param {number} i 
16  * @param {number} j
17  * @return {number}
18  */
19 NumArray.prototype.sumRange = function(i, j) {
20     return this.preSum[j+1] - this.preSum[i]
21 };
22 
23 // 輸入:["NumArray","sumRange","sumRange","sumRange"]  [[[-2,0,3,-5,2,-1]],[0,2],[2,5],[0,5]]
24 // 輸出:[null,1,-1,-3]
25 /**
26  * Your NumArray object will be instantiated and called as such:
27  * var obj = new NumArray(nums)
28  * var param_1 = obj.sumRange(i,j)
29  */

 8.接雨水(難度指數:困難

  • 給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水。

 

 解題思路1:先找出每個位置i能接多少水,每個位置能接多少水取決於左右兩側最大值中的小的一個。 時間復雜度 O(n^2)  空間復雜度(1)

 1 /**
 2  * @param {number[]} height
 3  * @return {number}
 4  */
 5 var trap = function(height) {
 6     if (height.length === 0) {
 7         return 0
 8     }
 9     // 思路:先找出每個位置i能接多少水,每個位置能接多少水取決於左右兩側最大值中的小的一個。
10     const len = height.length
11     let res = 0
12     for (let i = 1; i < len - 1; i++) {
13         let leftMax = 0;
14         let rightMax = 0;
15         // 找出左邊最高的柱子
16         for (let j = i; j >=0; j--) {
17             leftMax = Math.max(leftMax, height[j])
18         }
19         // 找出右邊最高的柱子
20         for(let j = i; j < len; j++){
21             rightMax = Math.max(rightMax, height[j])
22         }
23 
24         res += Math.min(leftMax, rightMax) - height[i]
25     }
26     return res
27 };

解題思路2:先找出左右最高柱子,再算能接多少水,降低復雜度,時間復雜度O(n),空間復雜度O(n)

 1 /**
 2  * @param {number[]} height
 3  * @return {number}
 4  */
 5 var trap = function(height) {
 6     if (height.length === 0) {
 7         return 0
 8     }
 9     // 思路2:先找出左右最高柱子,再算能接多少水。
10     // 優化性能
11     const len = height.length
12     let res = 0
13 
14     const leftMax = new Array(len)
15     const rightMax = new Array(len)
16 
17     leftMax[0] = height[0]
18     rightMax[len-1] = height[len-1]
19 
20     //計算leftMax從左到右
21     for (let i = 1; i < len; i++) {
22         leftMax[i] = Math.max(height[i], leftMax[i-1])
23     }
24 
25     // 計算rightMax從右到左
26     for(let j = len - 2; j >= 0; j--) {
27         rightMax[j] = Math.max(height[j], rightMax[j + 1])
28     }
29 
30     for (let i = 1; i < len - 1; i++) {
31         res += Math.min(leftMax[i], rightMax[i]) - height[i]
32     }
33     return res
34 };

解題思路3:雙指針法

 

 1 /**
 2  * @param {number[]} height
 3  * @return {number}
 4  */
 5 var trap = function(height) {
 6     if (height.length === 0) {
 7         return 0;
 8     }
 9     // 雙指針法
10     const len = height.length;
11     let res = 0;
12 
13     let left = 0;
14     let right = len - 1;
15 
16     let leftMax = height[0];
17     let rightMax = height[len - 1];
18 
19     while(left <= right){
20         leftMax = Math.max(leftMax, height[left]);
21         rightMax = Math.max(rightMax, height[right]);
22 
23         // 木桶原理,能裝多少水取決於矮的一邊
24         if (leftMax < rightMax) {
25             // 只看左邊的
26             res += leftMax - height[left];
27             left++;
28         } else {
29             // 只看右邊
30             res += rightMax - height[right];
31             right--;
32         }
33     }
34     return res;
35 };

 9.下一個排列(難度指數:中等)

  • 實現獲取 下一個排列 的函數,算法需要將給定數字序列重新排列成字典序中下一個更大的排列。
  • 如果不存在下一個更大的排列,則將數字重新排列成最小的排列(即升序排列)。
  • 必須 原地 修改,只允許使用額外常數空間。

(ps:剛拿到這個題,題目也看不懂,沒有思路,看了一個大佬解釋,總算弄明白了)

解題思路:從后往前,找出開始升序的相鄰兩個數的位置i和j,然后找出位置j后面,最小的大數(不僅僅是大於i位置的數,要保證盡可能小),然后互換位置,最后進行升序排列。

附上大佬的解析:

 

 

 圖解:

 

 

 

 1 /**  2  * @param {number[]} nums  3  * @return {void} Do not return anything, modify nums in-place instead.  4  */
 5 var nextPermutation = function(nums) {  6     // 學習大佬的思路,寫一個算法
 7     // 題意:123456 => 123465 123546 ... 654321
 8     // 例如:把數組[1,2,3,8,5,7,6,4 ] 看成整數12385764,找出下一個比他大的最小整數 
 9     // 先從后向前,找出相鄰的是升序的元素 A[i] < A[j],然后從j開始,向后找第一個比A[i]大的數A[K],再然后A[i] 和 A[k] 位置互換,最后再從j開始,升序排列,得到下一個排列。
10     const len = nums.length; 11     let indexJ = 0, indexI = 0; 12     // 從后向前 找出是升序的兩個相鄰位置indexI和indexJ
13     for(let i = len; i >0; i--){ 14         if (nums[i-1] < nums[i]) { 15             indexJ = i 16             indexI = i - 1; 17             break; 18  } 19  } 20     // 從起始位置 indexJ 開始,找出 盡可能小的「大數」nums[j],與前面的「小數」nums[i]交換。比如 123465,下一個排列應該把 5 和 4 交換而不是把 6 和 4 交換
21     // 把i后面的數提出來排序,找出最小的大數bigNum
22     const data = [] 23     for(let j = indexJ; j < len; j++) { 24  data.push(nums[j]) 25  } 26     // 升序排列,第一個大於nums[i]的就是最小的大數
27     data.sort((a,b)=>a-b); 28     const bigNum = data.find(item=>nums[indexI] < item); 29     for(let j = indexJ; j < len; j++) { 30         if (nums[j] === bigNum) { 31  changeVal(nums, indexI, j) 32             break; 33  } 34  } 35     // j位置后面開始升序排列
36     for(let m = indexJ; m < len; m++) { 37         for (let k = m + 1; k < len; k++) { 38             if (nums[k] < nums[m]) { 39  changeVal(nums, k, m) 40  } 41  } 42  } 43 }; 44 
45 function changeVal(nums = [], i=0, j=0) { 46     const temp = nums[j]; 47     nums[j] = nums[i]; 48     nums[i] = temp; 49 }

 10.最大子序和

  • 給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
  • 示例:
    輸入:nums = [-2,1,-3,4,-1,2,1,-5,4]
    輸出:6
    解釋:連續子數組 [4,-1,2,1] 的和最大,為 6 。

解題思路:遍歷,最大的和res初始值為nums[0],之前的和sum為0,計算每個位置i,當前的值nums[i]與之前的和sum + nums[i]比較,再比較res和sum,得出當前最大和,以此內推得到最終結果res,就是最大子序和。

 1 /**
 2  * @param {number[]} nums
 3  * @return {number}
 4  */
 5 var maxSubArray = function(nums) {
 6     const len = nums.length;
 7     // 動態規划
 8     let sum = 0; //
 9     let res = nums[0]; // 結果
10     for(let i=0;i<len;i++){
11         sum = Math.max(nums[i] + sum, nums[i])
12         res = Math.max(res, sum)
13     }
14     return res
15 };

 11.回文鏈表

  • 請判斷一個鏈表是否為回文鏈表。
  • 示例 1:

    輸入: 1->2
    輸出: false

    示例 2:

    輸入: 1->2->2->1
    輸出: true

解題思路:先把鏈表的值存入數組中,再通過遍歷數組,比較兩端的值是否相等來判斷。

 1 /**
 2  * Definition for singly-linked list.
 3  * function ListNode(val, next) {
 4  *     this.val = (val===undefined ? 0 : val)
 5  *     this.next = (next===undefined ? null : next)
 6  * }
 7  */
 8 /**
 9  * @param {ListNode} head
10  * @return {boolean}
11  */
12 var isPalindrome = function(head) {
13     // 先把鏈表的值存入數組中,再通過遍歷數組,比較兩端的值是否相等來判斷
14     const data = []
15     while(head !== null){
16         data.push(head.val)
17         head = head.next
18     }
19     console.log(data)
20     const len = data.length;
21     for(let i = 0, j = len - 1; i < j; i++, j--) {
22         // 比較兩端的值
23         if(data[i] !== data[j]) {
24             return false
25         }
26     }
27     return true
28 };

 12.和為s的連續正數序列

  • 輸入一個正整數 target ,輸出所有和為 target 的連續正整數序列(至少含有兩個數)。

序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。

  • 示例 1:

    輸入:target = 9

    輸出:[[2,3,4],[4,5]]

  • 示例 2:

    輸入:target = 15

    輸出:[[1,2,3,4,5],[4,5,6],[7,8]]

解題思路:找中間值作為外層循環次數,再累加比較,大於則跳過,等於則從當前位置開始累加,得到一個累加的數組,再將此數組放入一個結果中。

 1 /**
 2  * @param {number} target
 3  * @return {number[][]}
 4  */
 5 var findContinuousSequence = function(target) {
 6     const result = [];
 7     // 思路:1.求出中間值:target/2 向上取整(Math.ceil()),再往后和肯定大於目標值了,所以只取中間值以下的
 8     const middle = Math.ceil(target / 2);
 9     // 從1開始累加
10     for (let i = 1; i < middle; i++) {
11         // 控制累加的次數
12         for (let j = 2;; j++) {
13             // 求出累加多次的和(求和公式)
14             const total = (i + (i + j - 1)) * (j / 2);
15             if (total > target) {
16                 break;
17             } else if (total === target) {
18                 result.push(createArr(i, j))
19                 break;
20             }
21         }
22     }
23     return result;
24 };
25 
26 function createArr(n, len) {
27     let arr = new Array(len).fill(null), temp = [];
28     arr[0] = n;
29     arr = arr.map((item, index) => {
30         if (item === null) {
31             item = temp[index - 1] + 1; // 在上一項基礎上加1
32         }
33         // 當前項的值暫存,作為下一項的基礎
34         temp.push(item)
35         return item;
36     });
37     return arr;
38 }

 

持續更新中...

 

和為s的連續正數序列


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM