zoukankan      html  css  js  c++  java
  • 【前端】记录自己在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的连续正数序列

  • 相关阅读:
    linux安装maven
    Jenkins 改成中文语言显示
    appium怎么按下系统按键?如按下返回键、home键等等
    Jenkins+TestNG+gitlab+maven持续集成
    问题一:使用AndroidDriver而非原来的AppiumDriver的原因
    appium教程
    问题二:appium 搞定权限弹框的一个小办法
    问题三:Appium 的 UIAutomator2 模式下使用 sendKeys 出现错误
    TestNG执行顺序控制
    idea Mac 快捷键
  • 原文地址:https://www.cnblogs.com/scallop/p/14239985.html
Copyright © 2011-2022 走看看