Leetcode: 0021-0030题速览

Leetcode: 0021-0030题速览

本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解

遵从开源协议为知识共享 版权归属-相同方式共享 4.0 国际 公共许可证

研一在读备战就业,制此集合便于空闲时间浏览,有任何疑惑问题欢迎讨论,共同进步

目录

  • Leetcode: 0021-0030题速览
    • [21. 合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists)
      • 题目描述
      • 解法
        • 方法一:递归
          • Python3
          • Java
          • C++
    • [22. 括号生成](https://leetcode.cn/problems/generate-parentheses)
      • 题目描述
      • 解法
        • 方法一:DFS + 剪枝
          • Python3
          • Java
          • C++
    • [23. 合并 K 个升序链表](https://leetcode.cn/problems/merge-k-sorted-lists)
      • 题目描述
      • 解法
        • 方法一:优先队列(小根堆)
          • Python3
          • Java
          • C++
    • [24. 两两交换链表中的节点](https://leetcode.cn/problems/swap-nodes-in-pairs)
      • 题目描述
      • 解法
        • 方法一:递归
          • Python3
          • Java
          • C++
    • [25. K 个一组翻转链表](https://leetcode.cn/problems/reverse-nodes-in-k-group)
      • 题目描述
      • 解法
        • 方法一:迭代
          • Python3
          • Java
    • [26. 删除有序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array)
      • 题目描述
      • 解法
        • 方法一:一次遍历
          • Python3
          • Java
          • C++
    • [27. 移除元素](https://leetcode.cn/problems/remove-element)
      • 题目描述
      • 解法
        • 方法一:一次遍历
          • Python3
          • Java
          • C++
    • [28. 找出字符串中第一个匹配项的下标](https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string)
      • 题目描述
      • 解法
        • 方法一:遍历
          • Python3
          • Java
          • C++
    • [29. 两数相除](https://leetcode.cn/problems/divide-two-integers)
      • 题目描述
      • 解法
        • 方法一:模拟 + 快速幂
          • Python3
          • Java
          • C++
    • [30. 串联所有单词的子串](https://leetcode.cn/problems/substring-with-concatenation-of-all-words)
      • 题目描述
      • 解法
        • 方法一:哈希表 + 滑动窗口
          • Python3
          • Java
          • C++

21. 合并两个有序链表

题目描述

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

示例 1:

链表合并

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

难度:简单

标签:递归,链表

解法

方法一:递归

我们先判断链表 l 1 l_1 l1 l 2 l_2 l2 是否为空,若其中一个为空,则返回另一个链表。否则,我们比较 l 1 l_1 l1 l 2 l_2 l2 的头节点:

  • l 1 l_1 l1 的头节点的值小于等于 l 2 l_2 l2 的头节点的值,则递归调用函数 m e r g e T w o L i s t s ( l 1 . n e x t , l 2 ) mergeTwoLists(l_1.next, l_2) mergeTwoLists(l1.next,l2),并将 l 1 l_1 l1 的头节点与返回的链表头节点相连,返回 l 1 l_1 l1 的头节点。
  • 否则,递归调用函数 m e r g e T w o L i s t s ( l 1 , l 2 . n e x t ) mergeTwoLists(l_1, l_2.next) mergeTwoLists(l1,l2.next),并将 l 2 l_2 l2 的头节点与返回的链表头节点相连,返回 l 2 l_2 l2 的头节点。

时间复杂度 O ( m + n ) O(m + n) O(m+n),空间复杂度 O ( m + n ) O(m + n) O(m+n)。其中 m m m n n n 分别为两个链表的长度。

Python3
## Definition for singly-linked list.
## class ListNode:
##     def __init__(self, val=0, next=None):
##         self.val = val
##         self.next = next
class Solution:def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:if list1 is None or list2 is None:return list1 or list2if list1.val <= list2.val:list1.next = self.mergeTwoLists(list1.next, list2)return list1else:list2.next = self.mergeTwoLists(list1, list2.next)return list2
Java
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {if (list1 == null) {return list2;}if (list2 == null) {return list1;}if (list1.val <= list2.val) {list1.next = mergeTwoLists(list1.next, list2);return list1;} else {list2.next = mergeTwoLists(list1, list2.next);return list2;}}
}
C++
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if (!list1) return list2;if (!list2) return list1;if (list1->val <= list2->val) {list1->next = mergeTwoLists(list1->next, list2);return list1;} else {list2->next = mergeTwoLists(list1, list2->next);return list2;}}
};

22. 括号生成

题目描述

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

 

示例 1:

输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

示例 2:

输入:n = 1
输出:[“()”]

 

提示:

  • 1 <= n <= 8

难度:中等

标签:字符串,动态规划,回溯

解法

方法一:DFS + 剪枝

题目中 n n n 的范围为 [ 1 , 8 ] [1, 8] [1,8],因此我们直接通过“暴力搜索 + 剪枝”的方式通过本题。

我们设计一个函数 d f s ( l , r , t ) dfs(l, r, t) dfs(l,r,t),其中 l l l r r r 分别表示左括号和右括号的数量,而 t t t 表示当前的括号序列。那么我们可以得到如下的递归结构:

  • 如果 l > n l \gt n l>n 或者 r > n r \gt n r>n 或者 l < r l \lt r l<r,那么当前括号组合 t t t 不合法,直接返回;
  • 如果 l = n l = n l=n r = n r = n r=n,那么当前括号组合 t t t 合法,将其加入答案数组 ans 中,直接返回;
  • 我们可以选择添加一个左括号,递归执行 dfs(l + 1, r, t + "(")
  • 我们也可以选择添加一个右括号,递归执行 dfs(l, r + 1, t + ")")

时间复杂度 O ( 2 n × 2 × n ) O(2^{n\times 2} \times n) O(2n×2×n),空间复杂度 O ( n ) O(n) O(n)

Python3
class Solution:def generateParenthesis(self, n: int) -> List[str]:def dfs(l, r, t):if l > n or r > n or l < r:returnif l == n and r == n:ans.append(t)returndfs(l + 1, r, t + '(')dfs(l, r + 1, t + ')')ans = []dfs(0, 0, '')return ans
Java
class Solution {private List<String> ans = new ArrayList<>();private int n;public List<String> generateParenthesis(int n) {this.n = n;dfs(0, 0, "");return ans;}private void dfs(int l, int r, String t) {if (l > n || r > n || l < r) {return;}if (l == n && r == n) {ans.add(t);return;}dfs(l + 1, r, t + "(");dfs(l, r + 1, t + ")");}
}
C++
class Solution {
public:vector<string> generateParenthesis(int n) {vector<string> ans;function<void(int, int, string)> dfs = [&](int l, int r, string t) {if (l > n || r > n || l < r) return;if (l == n && r == n) {ans.push_back(t);return;}dfs(l + 1, r, t + "(");dfs(l, r + 1, t + ")");};dfs(0, 0, "");return ans;}
};

23. 合并 K 个升序链表

题目描述

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

 

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

 

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

难度:困难

标签:链表,分治,堆(优先队列),归并排序

解法

方法一:优先队列(小根堆)

我们可以创建一个小根堆来 p q pq pq 维护所有链表的头节点,每次从小根堆中取出值最小的节点,添加到结果链表的末尾,然后将该节点的下一个节点加入堆中,重复上述步骤直到堆为空。

时间复杂度 O ( n × log ⁡ k ) O(n \times \log k) O(n×logk),空间复杂度 O ( k ) O(k) O(k)。其中 n n n 是所有链表节点数目的总和,而 k k k 是题目给定的链表数目。

Python3
## Definition for singly-linked list.
## class ListNode:
##     def __init__(self, val=0, next=None):
##         self.val = val
##         self.next = next
class Solution:def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:setattr(ListNode, "__lt__", lambda a, b: a.val < b.val)pq = [head for head in lists if head]heapify(pq)dummy = cur = ListNode()while pq:node = heappop(pq)if node.next:heappush(pq, node.next)cur.next = nodecur = cur.nextreturn dummy.next
Java
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);for (ListNode head : lists) {if (head != null) {pq.offer(head);}}ListNode dummy = new ListNode();ListNode cur = dummy;while (!pq.isEmpty()) {ListNode node = pq.poll();if (node.next != null) {pq.offer(node.next);}cur.next = node;cur = cur.next;}return dummy.next;}
}
C++
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {auto cmp = [](ListNode* a, ListNode* b) { return a->val > b->val; };priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq;for (auto head : lists) {if (head) {pq.push(head);}}ListNode* dummy = new ListNode();ListNode* cur = dummy;while (!pq.empty()) {ListNode* node = pq.top();pq.pop();if (node->next) {pq.push(node->next);}cur->next = node;cur = cur->next;}return dummy->next;}
};

24. 两两交换链表中的节点

题目描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

 

示例 1:

链表交换

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

 

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

难度:中等

标签:递归,链表

解法

方法一:递归

我们可以通过递归的方式实现两两交换链表中的节点。

递归的终止条件是链表中没有节点,或者链表中只有一个节点,此时无法进行交换,直接返回该节点。

否则,我们递归交换链表 h e a d . n e x t . n e x t head.next.next head.next.next,记交换后的头节点为 t t t,然后我们记 h e a d head head 的下一个节点为 p p p,然后令 p p p 指向 h e a d head head,而 h e a d head head 指向 t t t,最后返回 p p p

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n),其中 n n n 是链表的长度。

Python3
## Definition for singly-linked list.
## class ListNode:
##     def __init__(self, val=0, next=None):
##         self.val = val
##         self.next = next
class Solution:def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:if head is None or head.next is None:return headt = self.swapPairs(head.next.next)p = head.nextp.next = headhead.next = treturn p
Java
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode swapPairs(ListNode head) {if (head == null || head.next == null) {return head;}ListNode t = swapPairs(head.next.next);ListNode p = head.next;p.next = head;head.next = t;return p;}
}
C++
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {if (!head || !head->next) {return head;}ListNode* t = swapPairs(head->next->next);ListNode* p = head->next;p->next = head;head->next = t;return p;}
};

25. K 个一组翻转链表

题目描述

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

 

示例 1:

链表翻转

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

链表翻转2

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

 

提示:
  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

 

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

    难度:困难

    标签:递归,链表

    解法

    方法一:迭代

    时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1),其中 n n n 是链表的长度。

    Python3
    ## Definition for singly-linked list.
    ## class ListNode:
    ##     def __init__(self, val=0, next=None):
    ##         self.val = val
    ##         self.next = next
    class Solution:def reverseKGroup(self, head: ListNode, k: int) -> ListNode:def reverseList(head):pre, p = None, headwhile p:q = p.nextp.next = prepre = pp = qreturn predummy = ListNode(next=head)pre = cur = dummywhile cur.next:for _ in range(k):cur = cur.nextif cur is None:return dummy.nextt = cur.nextcur.next = Nonestart = pre.nextpre.next = reverseList(start)start.next = tpre = startcur = prereturn dummy.next
    
    Java
    /*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
    class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dummy = new ListNode(0, head);ListNode pre = dummy, cur = dummy;while (cur.next != null) {for (int i = 0; i < k && cur != null; ++i) {cur = cur.next;}if (cur == null) {return dummy.next;}ListNode t = cur.next;cur.next = null;ListNode start = pre.next;pre.next = reverseList(start);start.next = t;pre = start;cur = pre;}return dummy.next;}private ListNode reverseList(ListNode head) {ListNode pre = null, p = head;while (p != null) {ListNode q = p.next;p.next = pre;pre = p;p = q;}return pre;}
    }
    

    26. 删除有序数组中的重复项

    题目描述

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

    考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

    • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
    • 返回 k 。

    判题标准:

    系统会用下面的代码来测试你的题解:

    int[] nums = […]; // 输入数组
    int[] expectedNums = […]; // 长度正确的期望答案

    int k = removeDuplicates(nums); // 调用

    assert k == expectedNums.length;
    for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
    }

    如果所有断言都通过,那么您的题解将被 通过

     

    示例 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 。不需要考虑数组中超出新长度后面的元素。

     

    提示:

    • 1 <= nums.length <= 3 * 104
    • -104 <= nums[i] <= 104
    • nums 已按 非严格递增 排列

    难度:简单

    标签:数组,双指针

    解法

    方法一:一次遍历

    我们用一个变量 k k k 记录当前已经处理好的数组的长度,初始时 k = 0 k=0 k=0,表示空数组。

    然后我们从左到右遍历数组,对于遍历到的每个元素 x x x,如果 k = 0 k=0 k=0 或者 x ≠ n u m s [ k − 1 ] x \neq nums[k-1] x=nums[k1],我们就将 x x x 放到 n u m s [ k ] nums[k] nums[k] 的位置,然后 k k k 自增 1 1 1。否则, x x x n u m s [ k − 1 ] nums[k-1] nums[k1] 相同,我们直接跳过这个元素。继续遍历,直到遍历完整个数组。

    这样,当遍历结束时, n u m s nums nums 中前 k k k 个元素就是我们要求的答案,且 k k k 就是答案的长度。

    时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。其中 n n n 为数组的长度。

    补充:

    原问题要求最多相同的数字最多出现 1 1 1 次,我们可以扩展至相同的数字最多保留 k k k 个。

    • 由于相同的数字最多保留 k k k 个,那么原数组的前 k k k 个元素我们可以直接保留;
    • 对于后面的数字,能够保留的前提是:当前数字 x x x 与前面已保留的数字的倒数第 k k k 个元素比较,不同则保留,相同则跳过。

    相似题目:

    • 80. 删除有序数组中的重复项 II
    Python3
    class Solution:def removeDuplicates(self, nums: List[int]) -> int:k = 0for x in nums:if k == 0 or x != nums[k - 1]:nums[k] = xk += 1return k
    
    Java
    class Solution {public int removeDuplicates(int[] nums) {int k = 0;for (int x : nums) {if (k == 0 || x != nums[k - 1]) {nums[k++] = x;}}return k;}
    }
    
    C++
    class Solution {
    public:int removeDuplicates(vector<int>& nums) {int k = 0;for (int x : nums) {if (k == 0 || x != nums[k - 1]) {nums[k++] = x;}}return k;}
    };
    

    27. 移除元素

    题目描述

    给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

    假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

    • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
    • 返回 k

    用户评测:

    评测机将使用以下代码测试您的解决方案:

    int[] nums = […]; // 输入数组
    int val = …; // 要移除的值
    int[] expectedNums = […]; // 长度正确的预期答案。
    // 它以不等于 val 的值排序。

    int k = removeElement(nums, val); // 调用你的实现

    assert k == expectedNums.length;
    sort(nums, 0, k); // 排序 nums 的前 k 个元素
    for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
    }

    如果所有的断言都通过,你的解决方案将会 通过

     

    示例 1:

    输入:nums = [3,2,2,3], val = 3
    输出:2, nums = [2,2,,]
    解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
    你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

    示例 2:

    输入:nums = [0,1,2,2,3,0,4,2], val = 2
    输出:5, nums = [0,1,4,0,3,,,_]
    解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
    注意这五个元素可以任意顺序返回。
    你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

     

    提示:

    • 0 <= nums.length <= 100
    • 0 <= nums[i] <= 50
    • 0 <= val <= 100

    难度:简单

    标签:数组,双指针

    解法

    方法一:一次遍历

    我们用变量 k k k 记录当前不等于 v a l val val 的元素个数。

    遍历数组 n u m s nums nums,如果当前元素 x x x 不等于 v a l val val,则将 x x x 赋值给 n u m s [ k ] nums[k] nums[k],并将 k k k 自增 1 1 1

    最后返回 k k k 即可。

    时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。其中 n n n 为数组 n u m s nums nums 的长度。

    Python3
    class Solution:def removeElement(self, nums: List[int], val: int) -> int:k = 0for x in nums:if x != val:nums[k] = xk += 1return k
    
    Java
    class Solution {public int removeElement(int[] nums, int val) {int k = 0;for (int x : nums) {if (x != val) {nums[k++] = x;}}return k;}
    }
    
    C++
    class Solution {
    public:int removeElement(vector<int>& nums, int val) {int k = 0;for (int x : nums) {if (x != val) {nums[k++] = x;}}return k;}
    };
    

    28. 找出字符串中第一个匹配项的下标

    题目描述

    给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1

     

    示例 1:

    输入:haystack = “sadbutsad”, needle = “sad”
    输出:0
    解释:“sad” 在下标 0 和 6 处匹配。
    第一个匹配项的下标是 0 ,所以返回 0 。

    示例 2:

    输入:haystack = “leetcode”, needle = “leeto”
    输出:-1
    解释:“leeto” 没有在 “leetcode” 中出现,所以返回 -1 。

     

    提示:

    • 1 <= haystack.length, needle.length <= 104
    • haystackneedle 仅由小写英文字符组成

    难度:简单

    标签:双指针,字符串,字符串匹配

    解法

    方法一:遍历

    以字符串 haystack 的每一个字符为起点与字符串 needle 进行比较,若发现能够匹配的索引,直接返回即可。

    假设字符串 haystack 长度为 n n n,字符串 needle 长度为 m m m,则时间复杂度为 O ( ( n − m ) × m ) O((n-m) \times m) O((nm)×m),空间复杂度 O ( 1 ) O(1) O(1)

    Python3
    class Solution:def strStr(self, haystack: str, needle: str) -> int:n, m = len(haystack), len(needle)for i in range(n - m + 1):if haystack[i : i + m] == needle:return ireturn -1
    
    Java
    class Solution {public int strStr(String haystack, String needle) {if ("".equals(needle)) {return 0;}int len1 = haystack.length();int len2 = needle.length();int p = 0;int q = 0;while (p < len1) {if (haystack.charAt(p) == needle.charAt(q)) {if (len2 == 1) {return p;}++p;++q;} else {p -= q - 1;q = 0;}if (q == len2) {return p - q;}}return -1;}
    }
    
    C++
    class Solution {
    private:vector<int> Next(string str) {vector<int> n(str.length());n[0] = -1;int i = 0, pre = -1;int len = str.length();while (i < len) {while (pre >= 0 && str[i] != str[pre])pre = n[pre];++i, ++pre;if (i >= len)break;if (str[i] == str[pre])n[i] = n[pre];elsen[i] = pre;}return n;}public:int strStr(string haystack, string needle) {if (0 == needle.length())return 0;vector<int> n(Next(needle));int len = haystack.length() - needle.length() + 1;for (int i = 0; i < len; ++i) {int j = 0, k = i;while (j < needle.length() && k < haystack.length()) {if (haystack[k] != needle[j]) {if (n[j] >= 0) {j = n[j];continue;} elsebreak;}++k, ++j;}if (j >= needle.length())return k - j;}return -1;}
    };
    

    29. 两数相除

    题目描述

    给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

    整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8-2.7335 将被截断至 -2

    返回被除数 dividend 除以除数 divisor 得到的

    注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231,  231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231

     

    示例 1:

    输入: dividend = 10, divisor = 3
    输出: 3
    解释: 10/3 = 3.33333… ,向零截断后得到 3 。

    示例 2:

    输入: dividend = 7, divisor = -3
    输出: -2
    解释: 7/-3 = -2.33333… ,向零截断后得到 -2 。

     

    提示:

    • -231 <= dividend, divisor <= 231 - 1
    • divisor != 0

    难度:中等

    标签:位运算,数学

    解法

    方法一:模拟 + 快速幂

    除法本质上就是减法,题目要求我们计算出两个数相除之后的取整结果,其实就是计算被除数是多少个除数加上一个小于除数的数构成的。但是一次循环只能做一次减法,效率太低会导致超时,可借助快速幂的思想进行优化。

    需要注意的是,由于题目明确要求最大只能使用 32 位有符号整数,所以需要将除数和被除数同时转换为负数进行计算。因为转换正数可能会导致溢出,如当被除数为 INT32_MIN 时,转换为正数时会大于 INT32_MAX

    假设被除数为 a a a,除数为 b b b,则时间复杂度为 O ( log ⁡ a × log ⁡ b ) O(\log a \times \log b) O(loga×logb),空间复杂度 O ( 1 ) O(1) O(1)

    Python3
    class Solution:def divide(self, a: int, b: int) -> int:if b == 1:return aif a == -(2**31) and b == -1:return 2**31 - 1sign = (a > 0 and b > 0) or (a < 0 and b < 0)a = -a if a > 0 else ab = -b if b > 0 else bans = 0while a <= b:x = bcnt = 1while x >= (-(2**30)) and a <= (x << 1):x <<= 1cnt <<= 1a -= xans += cntreturn ans if sign else -ans
    
    Java
    class Solution {public int divide(int a, int b) {if (b == 1) {return a;}if (a == Integer.MIN_VALUE && b == -1) {return Integer.MAX_VALUE;}boolean sign = (a > 0 && b > 0) || (a < 0 && b < 0);a = a > 0 ? -a : a;b = b > 0 ? -b : b;int ans = 0;while (a <= b) {int x = b;int cnt = 1;while (x >= (Integer.MIN_VALUE >> 1) && a <= (x << 1)) {x <<= 1;cnt <<= 1;}ans += cnt;a -= x;}return sign ? ans : -ans;}
    }
    
    C++
    class Solution {
    public:int divide(int a, int b) {if (b == 1) {return a;}if (a == INT_MIN && b == -1) {return INT_MAX;}bool sign = (a > 0 && b > 0) || (a < 0 && b < 0);a = a > 0 ? -a : a;b = b > 0 ? -b : b;int ans = 0;while (a <= b) {int x = b;int cnt = 1;while (x >= (INT_MIN >> 1) && a <= (x << 1)) {x <<= 1;cnt <<= 1;}ans += cnt;a -= x;}return sign ? ans : -ans;}
    };
    

    30. 串联所有单词的子串

    题目描述

    给定一个字符串 s 和一个字符串数组 words words 中所有字符串 长度相同

     s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

    • 例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd""cdabef", "cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

    返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

     

    示例 1:

    输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
    输出:[0,9]
    解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
    子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
    子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
    输出顺序无关紧要。返回 [9,0] 也是可以的。

    示例 2:

    输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”]
    输出:[]
    解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
    s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
    所以我们返回一个空数组。

    示例 3:

    输入:s = “barfoofoobarthefoobarman”, words = [“bar”,“foo”,“the”]
    输出:[6,9,12]
    解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
    子串 “foobarthe” 开始位置是 6。它是 words 中以 [“foo”,“bar”,“the”] 顺序排列的连接。
    子串 “barthefoo” 开始位置是 9。它是 words 中以 [“bar”,“the”,“foo”] 顺序排列的连接。
    子串 “thefoobar” 开始位置是 12。它是 words 中以 [“the”,“foo”,“bar”] 顺序排列的连接。

     

    提示:

    • 1 <= s.length <= 104
    • 1 <= words.length <= 5000
    • 1 <= words[i].length <= 30
    • words[i] 和 s 由小写英文字母组成

    难度:困难

    标签:哈希表,字符串,滑动窗口

    解法

    方法一:哈希表 + 滑动窗口

    我们用哈希表 c n t cnt cnt 统计 w o r d s words words 中每个单词出现的次数,用哈希表 c n t 1 cnt1 cnt1 统计当前滑动窗口中每个单词出现的次数。我们记字符串 s s s 的长度为 m m m,字符串数组 w o r d s words words 中单词的数量为 n n n,每个单词的长度为 k k k

    我们可以枚举滑动窗口的起点 i i i,其中 0 < i < k 0 \lt i \lt k 0<i<k。对于每个起点,我们维护一个滑动窗口,左边界为 l l l,右边界为 r r r,滑动窗口中的单词个数为 t t t,另外用一个哈希表 c n t 1 cnt1 cnt1 统计滑动窗口中每个单词出现的次数。

    每一次,我们提取字符串 s [ r : r + k ] s[r:r+k] s[r:r+k],如果 s [ r : r + k ] s[r:r+k] s[r:r+k] 不在哈希表 c n t cnt cnt 中,说明当前滑动窗口中的单词不合法,我们将左边界 l l l 更新为 r r r,同时将哈希表 c n t 1 cnt1 cnt1 清空,单词个数 t t t 重置为 0。如果 s [ r : r + k ] s[r:r+k] s[r:r+k] 在哈希表 c n t cnt cnt 中,说明当前滑动窗口中的单词合法,我们将单词个数 t t t 加 1,将哈希表 c n t 1 cnt1 cnt1 s [ r : r + k ] s[r:r+k] s[r:r+k] 的次数加 1。如果 c n t 1 [ s [ r : r + k ] ] cnt1[s[r:r+k]] cnt1[s[r:r+k]] 大于 c n t [ s [ r : r + k ] ] cnt[s[r:r+k]] cnt[s[r:r+k]],说明当前滑动窗口中 s [ r : r + k ] s[r:r+k] s[r:r+k] 出现的次数过多,我们需要将左边界 l l l 右移,直到 c n t 1 [ s [ r : r + k ] ] = c n t [ s [ r : r + k ] ] cnt1[s[r:r+k]] = cnt[s[r:r+k]] cnt1[s[r:r+k]]=cnt[s[r:r+k]]。如果 t = n t = n t=n,说明当前滑动窗口中的单词正好合法,我们将左边界 l l l 加入答案数组。

    时间复杂度 O ( m × k ) O(m \times k) O(m×k),空间复杂度 O ( n × k ) O(n \times k) O(n×k)。其中 m m m n n n 分别是字符串 s s s 和字符串数组 w o r d s words words 的长度,而 k k k 是字符串数组 w o r d s words words 中单词的长度。

    Python3
    class Solution:def findSubstring(self, s: str, words: List[str]) -> List[int]:cnt = Counter(words)m, n = len(s), len(words)k = len(words[0])ans = []for i in range(k):cnt1 = Counter()l = r = it = 0while r + k <= m:w = s[r : r + k]r += kif w not in cnt:l = rcnt1.clear()t = 0continuecnt1[w] += 1t += 1while cnt1[w] > cnt[w]:remove = s[l : l + k]l += kcnt1[remove] -= 1t -= 1if t == n:ans.append(l)return ans
    
    Java
    class Solution {public List<Integer> findSubstring(String s, String[] words) {Map<String, Integer> cnt = new HashMap<>();for (String w : words) {cnt.merge(w, 1, Integer::sum);}int m = s.length(), n = words.length;int k = words[0].length();List<Integer> ans = new ArrayList<>();for (int i = 0; i < k; ++i) {Map<String, Integer> cnt1 = new HashMap<>();int l = i, r = i;int t = 0;while (r + k <= m) {String w = s.substring(r, r + k);r += k;if (!cnt.containsKey(w)) {cnt1.clear();l = r;t = 0;continue;}cnt1.merge(w, 1, Integer::sum);++t;while (cnt1.get(w) > cnt.get(w)) {String remove = s.substring(l, l + k);l += k;cnt1.merge(remove, -1, Integer::sum);--t;}if (t == n) {ans.add(l);}}}return ans;}
    }
    
    C++
    class Solution {
    public:vector<int> findSubstring(string s, vector<string>& words) {

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/442649.html

    如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    云手机哪款好用?2024年云手机推荐对比指南

    随着云手机市场的快速扩展&#xff0c;消费者在选择云手机时面临着众多选择。为了帮助大家找到最适合自己的云手机&#xff0c;小编特意整理了一份当前市场上几款备受关注的云手机品牌对比&#xff0c;大家一起往下看吧。 1. Ogphone云手机 Ogphone云手机是近年来海外业务版块迅…

    PCIe配置篇(2)——如何进行配置操作(二)

    一、配置机制 我们之前提到过&#xff0c;配置空间存在于PCIe设备上&#xff0c;而处理器通常无法直接执行配置读写请求&#xff0c;因为它只能生成内存和I/O请求。这意味着RC&#xff08;Root Complex&#xff09;需要将某些访问请求转换为配置请求&#xff0c;以支持配置空间…

    SpringBoot3响应式编程全套-Spring Webflux

    目录 传送门前言一、组件对比二、WebFlux1、引入2、Reactor Core3、DispatcherHandler3.1、请求处理流程 4、注解开发4.1、目标方法传参4.2、返回值写法 5、文件上传6、错误处理7、RequestContext8、自定义Flux配置9、Filter 传送门 SpringMVC的源码解析&#xff08;精品&…

    python操作.docx、.pptx文件

    python操作.docx、.pptx文件 .docx文件和.pptx文件是Microsoft Office套件中两种常见的文件格式&#xff0c;分别对应Word文档和PowerPoint演示文稿。WPS Office完美支持Microsoft Office文件格式。 使用 Python 操作 .docx 和 .pptx 文件是一项非常实用的技能&#xff0c;尤…

    ElasticSearch 备考 -- Snapshot Restore

    一、题目 备份集群下的索引 task&#xff0c;存储快照名称为 snapshot_1 二、思考 这个涉及的是集群的备份&#xff0c;主要是通过创建快照&#xff0c;涉及到以下2步骤 Setp1&#xff1a;注册一个备份 snapshot repository Setp2&#xff1a;创建 snapshot 可以通过两种方…

    目标检测 DN-DETR(2022)

    文章目录 前言gt labels 和gt boxes加噪query的构造attention maskIS&#xff08;InStability&#xff09;指标 前言 gt labels 和gt boxes加噪 query的构造 attention mask IS&#xff08;InStability&#xff09;指标

    工行企业网银U盾展期后有两个证书问题的解决方法

    工行企业网银U盾证书快到期后&#xff0c;可以自助展期&#xff0c;流程可以根据企业网银提示页面操作。操作后&#xff0c;可能存在两个新旧两个证书并存的情况&#xff0c;致使网银转账等操作失败&#xff0c;如图&#xff1a; 其原因是新证书生成后&#xff0c;旧证书没有删…

    TCP_SOCKET编程实现

    文章目录 与UDP_SOCKET的区别第一代Tcp_ServerTcp_Client第二代Tcp_Server第三代Tcp_server多线程版本Tcp_Server线程池版的Tcp_Server使用inet_ntop来解决线程安全问题 业务逻辑编写总结补充说明&&业务代码完成ping的真实作用Translate编写Transform业务代码 整体总结…

    胤娲科技:机械臂「叛逃」记——自由游走,再悄然合体

    夜深人静&#xff0c;你正沉浸在梦乡的前奏&#xff0c;突然意识到房间的灯还亮着。此刻的你&#xff0c;是否幻想过有一只无形的手&#xff0c;轻盈地飘过&#xff0c;帮你熄灭那盏碍眼的灯&#xff1f; 又或者&#xff0c;你正窝在沙发上&#xff0c;享受电视剧的紧张刺激&am…

    C语言 | Leetcode C语言题解之第466题统计重复个数

    题目&#xff1a; 题解&#xff1a; #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <string.h> #include <math.h> #include <limits.h>#define MMAX(a, b) ((a) > (b)? (a) : (b)) #define MMIN(a,…

    打卡第六天 P10287 [GESP样题 七级] 最长不下降子序列

    今天是我打卡第六天&#xff0c;做个普及/提高−题吧(#^.^#) 原题链接&#xff1a;[GESP样题 七级] 最长不下降子序列 - 洛谷 题目描述 输入格式 输出格式 输出一行一个整数表示答案。 输入输出样例 输入 #1 5 4 2 10 6 3 1 5 2 2 3 3 1 1 4 输出 #1 3 输入 #2 6 11 …

    微服务——分布式事务

    目录 分布式事务 1.1分布式事务的特性 1.2分布式事务应用背景 ​编辑 1.3.认识Seata 1.4部署TC服务 1.4.1.准备数据库表 1.4.2.准备配置文件 1.4.3.Docker部署 1.5.微服务集成Seata 1.5.1.引入依赖 1.5.2.改造配置 1.5.3.添加数据库表 ​编辑1.6.XA模式 1.6.1.两…

    JavaScript函数基础(通俗易懂篇)

    10.函数 10.1 函数的基础知识 为什么会有函数&#xff1f; 在写代码的时候&#xff0c;有一些常用的代码需要书写很多次&#xff0c;如果直接复制粘贴的话&#xff0c;会造成大量的代码冗余&#xff1b; 函数可以封装一段重复的javascript代码&#xff0c;它只需要声明一次&a…

    精品WordPress主题/响应式个人博客主题Kratos

    Kratos 是一款专注于用户阅读体验的响应式 WordPress 主题&#xff0c;整体布局简洁大方&#xff0c;针对资源加载进行了优化。 Kratos主题基于Bootstrap和Font Awesome的WordPress一个干净&#xff0c;简单且响应迅速的博客主题&#xff0c;Vtrois创建和维护&#xff0c; 主…

    LeetCode 刷题基础 -- 模板原型Ⅰ

    模板原型 - 基础篇 学习网站一、进制转换二、二分查找① 查找指定元素② 查找第一个大于等于 x 值的序列下标③ 查找第一个大于 x 值的序列下标④ 单峰序列 三、双指针① 两数之和② 序列合并③ 集合求交④ 集合求并 四、其他高效技巧与算法① 区间和② 01 对③ 左小数 五、数学…

    BLE MESH学习1-基于沁恒CH582学习

    BLE MESH学习1-基于沁恒CH582学习 一、BLE mesh说明 mesh组网可以实现相比点对点模式更远的距离、更灵活的网络形式、更可靠的连接和更多的设备加入。BLE mesh在IoT中的传感器和控制具有重要意义。我的目的也是IoT领域&#xff0c;实现自己的传感器读取、开关控制等类似米家智…

    知识改变命运 数据结构【java对象的比较】

    0&#xff1a;前言 在基本数据类型中&#xff0c;我们可以直接使用号比较是否相等&#xff0c;还记的学堆哪里时候&#xff0c;插入一个数据&#xff0c;就会与其他数据进行比较&#xff0c;当时我们传入的是Integer类型&#xff0c;在Integer类里面已经实现了compare。 如果…

    西门子S7-1200博途软件项目的下载

    S7-1200的CPU本体上集成了PROFINET通信口&#xff0c;通过这个通信口可以实现CPU与编程设备的通信。 此外&#xff0c;S7-1200 可以通过连接CM1243-5扩展模块&#xff0c;然后电脑通过PC ADAPTER USB A2电缆、或者电脑上的CP卡&#xff08;例如CP5612&#xff09;通过PROFIBUS…

    手写mybatis之SQL执行器的定义和实现

    前言 所有系统的设计和实现&#xff0c;核心都在于如何解耦&#xff0c;如果解耦不清晰最后直接导致的就是再继续迭代功能时&#xff0c;会让整个系统的实现越来越臃肿&#xff0c;稳定性越来越差。而关于解耦的实践在各类框架的源码中都有非常不错的设计实现&#xff0c;所以阅…

    陪伴系统,会成为女性向游戏的下一个争夺点吗?

    乙游提供给女性玩家的只有恋爱感吗&#xff1f; 一般来说&#xff0c;对于乙女游戏的概括常常以为玩家提供“恋爱陪伴感”为主&#xff0c;恋爱很好理解&#xff0c;通过与多位男主角的剧情互动来模拟在真实恋爱中的情感交互&#xff0c;当下乙游都将重点放在了营造恋爱感上。…