每日一道 LeetCode

199 阅读10分钟

一. 数组

1.【2022.07.24】【1480】一维数组的动态和

🌟 LeetCode链接:链接

🌟 难度: 简单

给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。

请返回 nums 的动态和。

  • 示例 1
输入: nums = [1,2,3,4]
输出: [1,3,6,10]
解释: 动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4]
  • 示例 2
输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1]

🌟代码

var runningSum = function(nums) {
    for (let i = 1; i < nums.length; i++) {
        nums[i] += nums[i-1];
    }
    return nums;
};

2.【2022.07.25】【724】寻找数组的中心下标

🌟 LeetCode链接:链接

🌟 难度: 简单

给你一个整数数组 nums ,请计算数组的 中心下标

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

🌟代码

/**
 * @param {number[]} nums
 * @return {number}
 */
 var pivotIndex = function(nums) {
     const total = nums.reduce((a, b) => a + b, 0);
     let sum = 0;
     for (let i = 0; i < nums.length; i++) {
         if (2 * sum + nums[i] === total) {
             return i;
         }
         sum += nums[i];
     }
     return -1;
 }

3.【2022.08.01】【704】二分查找

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定一个 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

🌟代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0, right = nums.length -1;
    while(left <= right) {
        // 使用 Math.floor((high - low) / 2) + low 的原因:如果high特别大,但是没有越界,low特别大,但也没有越界,但是你把他俩相加就有可能越界,所以用low + (high - low) / 2 的方法来保证不会越界。low不越界,high-low肯定也不会越界。
        const mid = Math.floor((high - low) / 2) + low;
        const num = nums[mid];
        if(num === target) {
            return mid;
        } else if(num < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

4.【2022.08.01】【278】第一个错误的版本

🌟 LeetCode链接:  链接

🌟 难度:   简单

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 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

🌟代码

/**
 * Definition for isBadVersion()
 * 
 * @param {integer} version number
 * @return {boolean} whether the version is bad
 * isBadVersion = function(version) {
 *     ...
 * };
 */

/**
 * @param {function} isBadVersion()
 * @return {function}
 */
var solution = function(isBadVersion) {
    /**
     * @param {integer} n Total versions
     * @return {integer} The first bad version
     */
     return function(n) {
         let left = 1, right = n;
         while(left < right) {
             const mid = Math.floor((right - left) / 2) + left;
             if(isBadVersion(n)) {
                 right = mid;
             } else {
                 left = mid - 1;
             }
         }
         return left;
     }
}

5.【2022.08.02】【26】删除有序数组中的重复项

🌟 LeetCode链接:  链接

🌟 难度:   简单

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
  • 示例 2
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

🌟代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    const n = nums.length;
    // 定义两个指针fast和slow分别为快指针和慢指针
    // 快指针表示遍历数组到达的下标位置
    // 慢指针表示下一个不同元素要填入的下标位置
    // 初始时两个指针都指向下标1
    let slow = 1, fast = 1;
    while(fast < n) {
        if(nums[fast] !== nums[fast - 1]) {
            nums[slow] = nums[fast];
            ++slow;
        }
        ++fast;
    }
    return slow;
}

6.【2022.08.02】【27】移除元素

🌟 LeetCode链接:  链接

🌟 难度:   简单

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

  • 示例 1
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
  • 示例 2
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

🌟代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeElement = function(nums, val) {
    const n = nums.length;
    let left = 0;
    for(let right = 0; right < n; right++) {
        if(nums[right] !== val) {
            nums[left] = nums[right];
            left++;
        }
    }
    return left;
}

7.【2022.08.03】【88】合并两个有序数组

🌟 LeetCode链接:  链接

🌟 难度:   简单

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

  • 示例 1
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3][2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
  • 示例 2
输入: nums1 = [1], m = 1, nums2 = [], n = 0
输出: [1]
解释: 需要合并 [1][] 。
合并结果是 [1]
  • 示例 3
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [][1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

🌟代码

/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function(nums1, m, nums2, n) {
    nums1.splice(m, nums1.lenth - 3, ...nums2);
    nums1.sort((a, b) => a - b);
}

8.【2022.08.03】【35】搜索插入位置

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 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

🌟代码

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

9.【2022.08.15】【66】加一

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字

你可以假设除了整数 0 之外,这个整数不会以零开头。

  • 示例 1
输入: digits = [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
  • 示例 2
输入: digits = [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
  • 示例 3
输入: digits = [0]
输出: [1]

🌟代码

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    const n = digits.length;
    for (let i = n - 1; i >= 0; --i) {
        if (digits[i] !== 9) {
            ++digits[i];
            for (let j = i + 1; j < n; ++j) {
                digits[j] = 0;
            }
            return digits;
        }
    }

    // digits 中所有的元素均为 9
    const ans = new Array(n + 1).fill(0);
    ans[0] = 1;
    return ans;
};

10.【2022.08.16】【136】只出现一次的数字

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

  • 示例 1
输入: [2,2,1]
输出: 1
  • 示例 2
输入: [4,1,2,1,2]
输出: 4

🌟代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let ans = 0;
    for(const num of nums) {
        ans ^= num;
    }
    return ans;
};

11.【2022.08.17】【169】多数元素

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

  • 示例 1
输入: nums = [3,2,3]
输出: 3
  • 示例 2
输入: nums = [2,2,1,1,1,2,2]
输出: 2

🌟代码

/**
 * @param {number[]} nums
 * @return {number}
 */
// 思路:排序数组,如果有一个数字出现的频率大于n/2,则在数组中nums.length/2的位置就是这个数
var majorityElement = function(nums) {
    // 排序
    nums.sort((a, b) => a - b);
    return nums[Math.floor(nums.length / 2)];
};

二. 递归

fn(0);
function fn(nub){
    if(nub < 5){
        fn(nub+1);
    }
    console.log(nub);
}
// 5 4 3 2 1 0

1.【2022.07.26】【21】合并两个有序链表

🌟 LeetCode链接: 链接

🌟 难度:  简单

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 image.png

  • 示例 1
输入: l1 = [1,2,4], l2 = [1,3,4]
输出: [1,1,2,3,4,4]
  • 示例 2
输入: l1 = [], l2 = []
输出: []
  • 示例 3
输入: l1 = [], l2 = [0]
输出: [0]

🌟代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    if (list1 === null) {
        return list2;
    } else if (list2 === null) {
        return list1;
    } else if(list1.val < list2.val) {
        list1.next = mergeTwoLists(list1.next, list2);
        return list1;
    } else {
        list2.next = mergeTwoLists(list1, list2.next);
        return list2;
    }
}; 

2.【2022.07.27】【206】反转链表

🌟 LeetCode链接: 链接

🌟 难度:  简单

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

  • 示例 1

image.png

输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
  • 示例 2

image.png

输入: head = [1,2]
输出: [2,1]
  • 示例 3
输入: head = []
输出: []

🌟代码

/** * Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
// 方法一 迭代
var reverseList = function(head) {
    let prev = null; let curr = head;
    while(curr) {
        const next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
};

// 方法二 递归
var reverseList = function(head) {
    if (head == null || head.next == null) {
        return head;
    }
    const newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
};

三. 字符串

1.【2022.07.28】【205】同构字符串

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定两个字符串 s 和 t ,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

  • 示例 1
输入: s = "egg", t = "add"
输出: true
  • 示例 2
输入: s = "foo", t = "bar"
输出: false
  • 示例 3
输入: s = "paper", t = "title"
输出: true
/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isIsomorphic = function(s, t) {
    const s2t = {};
    const t2s = {};
    const len = s.length;
    for(let i = 0; i < len; i++) {
        const x = s[i], y = t[i];
        if((s2t[x] && s2t[x] !== y) || (t2s[y] && t2s[y] !== x)) {
            return false;
        }
        s2t[x] = y;
        t2s[y] = x;
    }
    return true;
}

2.【2022.07.29】【392】判断子序列

🌟 LeetCode链接:  链接

🌟 难度:   简单

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

  • 示例 1
输入: s = "abc", t = "ahbgdc"
输出: true
/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isSubsequence = function(s, t) {

};