本题解Go语言部分基于 LeetCode-Go
其他部分基于本人实践学习
个人题解GitHub连接:LeetCode-Go-Python-Java-C
Go-Python-Java-C-LeetCode高分解法-第一周合集
Go-Python-Java-C-LeetCode高分解法-第二周合集
Go-Python-Java-C-LeetCode高分解法-第三周合集
本文部分内容来自网上搜集与个人实践。如果任何信息存在错误,欢迎读者批评指正。本文仅用于学习交流,不用作任何商业用途。
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
(
和 )
成对的匹配上的。Go 版本解题思路:
这个解题思路使用了深度优先搜索(DFS)的方法来生成有效的括号组合。主要的步骤如下:
定义 generateParenthesis
函数,接收括号对数目 n
作为参数,返回有效的括号组合列表。
如果 n
为 0,直接返回一个空的字符串列表,因为没有括号需要生成。
初始化一个空的结果列表 res
,用于存储有效的括号组合。
调用辅助函数 findGenerateParenthesis
来递归生成括号组合。传入当前剩余左右括号数目、当前生成的字符串和结果列表的指针。
在 findGenerateParenthesis
函数中,当左右括号剩余数目都为 0 时,将当前生成的字符串添加到结果列表中。
如果剩余的左括号数目大于 0,可以在当前字符串后添加一个左括号,然后递归生成。
如果剩余的右括号数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目(以保证有效性),可以在当前字符串后添加一个右括号,然后递归生成。
返回最终的有效括号组合列表 res
。
Python 版本解题思路:
这个解题思路同样使用了深度优先搜索(DFS)的方法来生成有效的括号组合。主要的步骤如下:
定义一个 Solution
类,其中有一个 generateParenthesis
方法,接收括号对数目 n
作为参数,返回有效的括号组合列表。
初始化一个空的结果列表 res
,用于存储有效的括号组合。
调用递归函数 generate
来生成括号组合。传入当前剩余左右括号数目、当前生成的字符串和结果列表。
在 generate
函数中,有两个基准情况:
如果剩余的右括号数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目(以保证有效性),可以在当前字符串后添加一个右括号,然后递归生成。
最后,generateParenthesis
方法返回最终的有效括号组合列表 res
。
Java 版本解题思路:
这个解题思路与前两个版本类似,同样使用深度优先搜索(DFS)的方法来生成有效的括号组合。主要的步骤如下:
定义一个 Solution
类,其中有一个 generateParenthesis
方法,接收括号对数目 n
作为参数,返回有效的括号组合列表。
初始化一个空的结果列表 res
,用于存储有效的括号组合。
调用递归函数 generate
来生成括号组合。传入当前剩余左右括号数目、当前生成的字符串和结果列表。
在 generate
函数中,同样有两个基准情况:
如果剩余的右括号数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目(以保证有效性),可以在当前字符串后添加一个右括号,然后递归生成。
最后,generateParenthesis
方法返回最终的有效括号组合列表 res
。
C++ 版本解题思路:
这个解题思路与其他版本相似,同样使用深度优先搜索(DFS)的方法来生成有效的括号组合。主要的步骤如下:
定义一个 Solution
类,其中有一个 generateParenthesis
方法,接收括号对数目 n
作为参数,返回有效的括号组合列表。
初始化一个空的结果列表 res
,用于存储有效的括号组合。
调用递归函数 generate
来生成括号组合。传入当前剩余左右括号数目、当前生成的字符串和结果列表。
在 generate
函数中,同样有两个基准情况:
如果剩余的右括号数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目(以保证有效性),可以在当前字符串后添加一个右括号,然后递归生成。
最后,generateParenthesis
方法返回最终的有效括号组合列表 res
。
// 定义一个函数 generateParenthesis,输入参数 n 表示括号对数目,返回有效的括号组合列表。
func generateParenthesis(n int) []string {
// 如果括号对数目为 0,直接返回一个空的字符串列表。
if n == 0 {
return []string{}
}
// 初始化一个结果列表用于存储有效的括号组合。
res := []string{}
// 调用辅助函数 findGenerateParenthesis 生成括号组合,传入当前左右括号剩余数目、当前生成的字符串和结果列表的指针。
findGenerateParenthesis(n, n, "", &res)
// 返回最终的有效括号组合列表。
return res
}
// 定义辅助函数 findGenerateParenthesis,用于递归生成有效的括号组合。
func findGenerateParenthesis(lindex, rindex int, str string, res *[]string) {
// 当左右括号剩余数目都为 0 时,说明已经生成了一个有效的括号组合,将其添加到结果列表中。
if lindex == 0 && rindex == 0 {
*res = append(*res, str)
return
}
// 如果左括号剩余数目大于 0,可以在当前字符串后添加一个左括号,继续递归生成。
if lindex > 0 {
findGenerateParenthesis(lindex-1, rindex, str+"(", res)
}
// 如果右括号剩余数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目,
// 则可以在当前字符串后添加一个右括号,继续递归生成。
if rindex > 0 && lindex < rindex {
findGenerateParenthesis(lindex, rindex-1, str+")", res)
}
}
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = [] # 存储最终的有效括号组合列表
self.generate(n, n, "", res) # 调用递归函数生成括号组合
return res
# 递归生成括号组合的辅助函数
def generate(self, left: int, right: int, current: str, res: List[str]):
# 当左右括号剩余数目都为 0 时,说明已经生成了一个有效的括号组合,将其添加到结果列表中。
if left == 0 and right == 0:
res.append(current)
return
# 如果左括号剩余数目大于 0,可以在当前字符串后添加一个左括号,继续递归生成。
if left > 0:
self.generate(left - 1, right, current + "(", res)
# 如果右括号剩余数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目,
# 则可以在当前字符串后添加一个右括号,继续递归生成。
if right > 0 and left < right:
self.generate(left, right - 1, current + ")", res)
import java.util.ArrayList;
import java.util.List;
class Solution {
public List generateParenthesis(int n) {
List res = new ArrayList<>(); // 存储最终的有效括号组合列表
generate(n, n, "", res); // 调用递归函数生成括号组合
return res;
}
// 递归生成括号组合的辅助函数
private void generate(int left, int right, String current, List res) {
// 当左右括号剩余数目都为 0 时,说明已经生成了一个有效的括号组合,将其添加到结果列表中。
if (left == 0 && right == 0) {
res.add(current);
return;
}
// 如果左括号剩余数目大于 0,可以在当前字符串后添加一个左括号,继续递归生成。
if (left > 0) {
generate(left - 1, right, current + "(", res);
}
// 如果右括号剩余数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目,
// 则可以在当前字符串后添加一个右括号,继续递归生成。
if (right > 0 && left < right) {
generate(left, right - 1, current + ")", res);
}
}
public static void main(String[] args) {
Solution solution = new Solution();
int n = 3; // 设置括号对数目
List result = solution.generateParenthesis(n); // 调用生成函数
for (String str : result) {
System.out.println(str); // 输出生成的括号组合
}
}
}
#include
#include
using namespace std;
class Solution {
public:
vector generateParenthesis(int n) {
vector res; // 存储最终的有效括号组合列表
generate(n, n, "", res); // 调用递归函数生成括号组合
return res;
}
// 递归生成括号组合的辅助函数
void generate(int left, int right, string current, vector& res) {
// 当左右括号剩余数目都为 0 时,说明已经生成了一个有效的括号组合,将其添加到结果列表中。
if (left == 0 && right == 0) {
res.push_back(current);
return;
}
// 如果左括号剩余数目大于 0,可以在当前字符串后添加一个左括号,继续递归生成。
if (left > 0) {
generate(left - 1, right, current + "(", res);
}
// 如果右括号剩余数目大于 0,并且剩余的右括号数目严格大于剩余的左括号数目,
// 则可以在当前字符串后添加一个右括号,继续递归生成。
if (right > 0 && left < right) {
generate(left, right - 1, current + ")", res);
}
}
};
每个版本代码所需要的基础知识。
Go 版本基础知识:
函数声明与调用: 了解如何声明和调用函数,Go 使用 func
关键字来定义函数。
条件语句: 了解 if
条件语句的用法,以及如何使用条件判断。
切片(Slices): 切片是 Go 中灵活的数据结构,类似于动态数组。了解如何声明、初始化和操作切片。
递归: 了解递归的概念,以及如何在函数中调用自身。
指针与引用: 了解 Go 中的指针和引用,因为函数中需要通过引用传递结果列表。
Python 版本基础知识:
函数定义与调用: 理解如何定义和调用函数,Python 使用 def
关键字来声明函数。
条件语句: 理解 if
条件语句的语法,以及如何使用条件判断。
列表(Lists): 列表是 Python 中常用的数据结构,类似于动态数组。了解如何创建、修改和操作列表。
递归: 理解递归的概念,以及如何在函数中调用自身。
Java 版本基础知识:
函数声明与调用: 了解如何声明和调用函数,Java 使用方法(methods)来定义函数。
条件语句: 了解 if
条件语句的语法,以及如何进行条件判断。
列表(Lists): 在 Java 中,可以使用 ArrayList
类作为动态数组。了解如何创建、修改和操作 ArrayList。
递归: 了解递归的概念,以及如何在方法中调用自身。
类与对象: Java 是面向对象的编程语言,理解类和对象的概念,以及如何定义和使用它们。
C++ 版本基础知识:
函数声明与调用: 了解如何声明和调用函数,C++ 使用函数(functions)来定义函数。
条件语句: 理解 if
条件语句的语法,以及如何进行条件判断。
向量(Vectors): 在 C++ 中,可以使用 vector
类作为动态数组。了解如何创建、修改和操作向量。
递归: 理解递归的概念,以及如何在函数中调用自身。
类与对象: C++ 也是面向对象的编程语言,理解类和对象的概念,以及如何定义和使用它们。
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example :
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
合并 K 个有序链表
Go 版本解法:
这个解法使用了分治的思想来合并 k 个有序链表。首先,判断输入的链表数组的长度:
nil
。对于长度大于 1 的情况,将链表数组分成两半,然后递归地合并左半部分和右半部分,最后将两部分合并。合并的过程是通过一个辅助函数来实现的,比较两个链表的当前节点值,选择较小的节点连接到合并后的链表。
Python 版本解法:
这个解法使用了优先队列(堆)来合并 k 个有序链表。首先,将每个链表的头节点及其值和索引放入优先队列中。然后,循环从优先队列中弹出最小值的节点,将其连接到合并链表的末尾。如果弹出的节点还有下一个节点,将下一个节点入队。
这样,通过不断弹出最小节点,不断将下一个节点入队,就能够逐步构建合并后的链表。
Java 版本解法:
这个解法也是使用了优先队列(堆)来合并 k 个有序链表。与 Python 版本类似,首先将每个链表的头节点及其值和索引放入优先队列中。然后,循环从优先队列中弹出最小值的节点,将其连接到合并链表的末尾。如果弹出的节点还有下一个节点,将下一个节点入队。
这个过程会不断弹出最小节点,不断将下一个节点入队,从而构建合并后的链表。
C++ 版本解法:
这个解法使用了分治的思想来合并 k 个有序链表。首先,定义了一个辅助函数 merge
,用来合并两个有序链表。然后,使用递归的分治方法,将 k 个链表分成两半,递归地合并左半部分和右半部分,最后将两部分合并。
在合并的过程中,辅助函数 merge
会比较两个链表的当前节点值,选择较小的节点连接到合并后的链表。
以上就是各个版本的解题思路,它们都是通过合并有序链表来实现的,但采用了不同的数据结构和算法来达到相同的目标。## 代码
/**
* 定义单链表节点结构
* type ListNode struct {
* Val int // 当前节点的值
* Next *ListNode // 指向下一个节点的指针
* }
*/
func mergeKLists(lists []*ListNode) *ListNode {
// 获取输入链表数组的长度
len := len(lists)
// 根据不同情况进行合并操作
switch len {
case 0:
return nil // 如果没有链表,返回 nil
case 1:
return lists[0] // 如果只有一个链表,直接返回这个链表
default:
mid := len / 2 // 将链表数组分成两半
left := mergeKLists(lists[:mid]) // 递归地合并左半部分
right := mergeKLists(lists[mid:]) // 递归地合并右半部分
return merge(left, right) // 合并左右两部分链表
}
}
func merge(list1 *ListNode, list2 *ListNode) *ListNode {
dummy := &ListNode{} // 创建一个哑节点作为合并后的链表的起始节点
cur := dummy // 创建一个当前节点指针,初始指向哑节点
// 比较两个链表的节点值,逐个选择较小的节点连接到合并后的链表
for list1 != nil && list2 != nil {
if list1.Val < list2.Val {
cur.Next = list1
list1 = list1.Next
} else {
cur.Next = list2
list2 = list2.Next
}
cur = cur.Next
}
// 将剩余的 list1 链接到合并后的链表
for list1 != nil {
cur.Next = list1
list1 = list1.Next
cur = cur.Next
}
// 将剩余的 list2 链接到合并后的链表
for list2 != nil {
cur.Next = list2
list2 = list2.Next
cur = cur.Next
}
return dummy.Next // 返回合并后链表的起始节点
}
import heapq
# 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]:
heap = [] # 使用一个最小堆来存储当前每个链表的头节点
for i, node in enumerate(lists):
if node:
heapq.heappush(heap, (node.val, i, node)) # 将链表头节点的值、链表索引和链表头节点元组入堆
dummy = ListNode() # 创建哑节点作为合并后链表的起始节点
cur = dummy # 创建一个当前节点指针,初始指向哑节点
while heap:
val, idx, node = heapq.heappop(heap) # 弹出堆顶元素,即最小值的节点
cur.next = node # 将最小节点接到合并链表的末尾
cur = cur.next
if node.next:
heapq.heappush(heap, (node.next.val, idx, node.next)) # 将下一个节点入堆,保持堆的有序性
return dummy.next # 返回合并后链表的起始节点
import java.util.*;
/**
* 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 minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);
// 将所有链表的头节点加入堆
for (ListNode node : lists) {
if (node != null) {
minHeap.offer(node);
}
}
ListNode dummy = new ListNode(); // 创建哑节点作为合并后链表的起始节点
ListNode cur = dummy; // 创建一个当前节点指针,初始指向哑节点
while (!minHeap.isEmpty()) {
ListNode minNode = minHeap.poll(); // 弹出堆顶元素,即最小值的节点
cur.next = minNode; // 将最小节点接到合并链表的末尾
cur = cur.next;
if (minNode.next != null) {
minHeap.offer(minNode.next); // 将下一个节点加入堆,保持堆的有序性
}
}
return dummy.next; // 返回合并后链表的起始节点
}
}
/**
* 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* merge(ListNode* list1, ListNode* list2) {
if (!list1) return list2;
if (!list2) return list1;
if (list1->val <= list2->val) {
list1->next = merge(list1->next, list2);
return list1;
} else {
list2->next = merge(list1, list2->next);
return list2;
}
}
// 使用分治法逐步合并 K 个链表
ListNode* quick(vector& lists, int l, int r) {
if (l > r) return nullptr;
if (l == r) return lists[l];
int mid = (l + r) >> 1;
return merge(quick(lists, l, mid), quick(lists, mid + 1, r));
}
// 主函数,合并 K 个有序链表
ListNode* mergeKLists(vector& lists) {
return quick(lists, 0, lists.size() - 1);
}
};
每个版本的详细基础知识。
Go 版本解法:
链表数据结构: 你需要理解 ListNode
结构的定义,它是一个表示链表节点的结构体,包括一个整数值 Val
和指向下一个节点的指针 Next
。
递归: 这个解法使用了递归来实现分治合并。你需要了解递归的工作原理,即一个函数调用自身以解决更小规模的问题。
切片(Slices): Go 中的切片是动态数组,用于存储多个相同类型的元素。解法中用切片进行了链表的分割和合并。
Python 版本解法:
优先队列(堆): heapq
是 Python 中的堆操作库,它提供了优先队列的实现。你需要了解如何使用堆来维护最小元素,以及堆的基本操作。
链表数据结构: 你需要理解 ListNode
结构的定义,类似于 Go 版本的链表数据结构。
类和对象: Python 是面向对象的语言,解法中使用了类来定义 Solution
类,以及链表节点的构造。
循环: 解法中使用了循环来遍历链表、优先队列等。
Java 版本解法:
优先队列(堆): Java 中的优先队列也是通过堆实现的,你需要了解如何使用 PriorityQueue
类来创建和操作优先队列。
链表数据结构: 你需要理解 ListNode
结构的定义,与其他版本类似。
类和对象: Java 也是面向对象的语言,你需要理解类的定义和使用。
循环: 解法中使用了循环来遍历链表、优先队列等。
C++ 版本解法:
优先队列(堆): C++ 中的 priority_queue
类用于实现优先队列,你需要了解如何使用它来维护最小元素。
链表数据结构: 你需要理解 ListNode
结构的定义,与其他版本类似。
递归: 解法中使用了递归来实现分治合并,你需要理解递归的概念和用法。
类和对象: C++ 是面向对象的语言,解法中使用了类来定义 Solution
类和链表节点。
Given a linked list, swap every two adjacent nodes and return its head.
You may not modify the values in the list’s nodes, only nodes itself may be changed.
Example:
Given 1->2->3->4, you should return the list as 2->1->4->3.
两两相邻的元素,翻转链表
Go版本:
Python版本:
Java版本:
C++版本:
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// 定义一个函数,用于交换链表中相邻节点的位置
func swapPairs(head *ListNode) *ListNode {
// 创建一个虚拟节点,指向原链表的头节点
dummy := &ListNode{Next: head}
// 循环遍历链表,直到没有足够的相邻节点可以交换
for pt := dummy; pt != nil && pt.Next != nil && pt.Next.Next != nil; {
// 使用多重赋值的方式交换相邻节点的位置
pt, pt.Next, pt.Next.Next, pt.Next.Next.Next = pt.Next, pt.Next.Next, pt.Next.Next.Next, pt.Next
}
// 返回交换相邻节点后的链表头节点的指针
return dummy.Next
}
# 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]:
# 创建一个虚拟节点,指向原链表的头节点
dummy = ListNode(0)
dummy.next = head
# 使用一个指针pt来遍历链表
pt = dummy
# 循环遍历链表,直到没有足够的相邻节点可以交换
while pt.next and pt.next.next:
# 获取需要交换的相邻节点
node1 = pt.next
node2 = pt.next.next
# 进行节点交换
pt.next = node2
node1.next = node2.next
node2.next = node1
# 更新指针pt的位置,继续下一轮交换
pt = node1
# 返回交换相邻节点后的链表头节点
return dummy.next
/**
* 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) {
// 创建一个虚拟节点,指向原链表的头节点
ListNode dummy = new ListNode(0);
dummy.next = head;
// 使用一个指针pt来遍历链表
ListNode pt = dummy;
// 循环遍历链表,直到没有足够的相邻节点可以交换
while (pt.next != null && pt.next.next != null) {
// 获取需要交换的相邻节点
ListNode node1 = pt.next;
ListNode node2 = pt.next.next;
// 进行节点交换
pt.next = node2;
node1.next = node2.next;
node2.next = node1;
// 更新指针pt的位置,继续下一轮交换
pt = node1;
}
// 返回交换相邻节点后的链表头节点
return dummy.next;
}
}
/**
* 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) return head;
// 创建一个虚拟节点,指向原链表的头节点
ListNode* virNode = new ListNode(-1);
virNode->next = head;
// 使用三个指针p1、p2和p3来进行节点交换
ListNode* p1 = head;
ListNode* p2 = p1->next;
ListNode* p3;
// 创建一个指针ago,用于记录上一组交换节点的末尾节点
ListNode* ago = virNode;
// 循环遍历链表,直到没有足够的相邻节点可以交换
while (p2) {
// 获取p2的下一个节点
p3 = p2->next;
// 将p2的next指针置为空,然后将p2的next指向p1,将p1的next指向p3,将ago的next指向p2
p2->next = nullptr;
p2->next = p1;
p1->next = p3;
ago->next = p2;
// 更新指针ago为p1,指针p1为p3,指针p2为p1的下一个节点
ago = p1;
p1 = p3;
if (!p1) {
break;
} else {
p2 = p1->next;
}
}
// 返回交换相邻节点后的链表头节点
return virNode->next;
}
};
Go 版本:
C++ 版本:
Java 版本:
Python 版本:
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a
multiple of k then left-out nodes in the end should remain as it is.
Example:
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5
Note:
按照每 K 个元素翻转的方式翻转链表。如果不满足 K 个元素的就不翻转。
这一题是 problem 24 的加强版,problem 24 是两两相邻的元素,翻转链表。而 problem 25 要求的是 k 个相邻的元素,翻转链表,problem
相当于是 k = 2 的特殊情况。
Go 版本解题思路:
创建一个 reverseKGroup
函数,该函数接收一个头节点和一个整数 k,用于翻转每 k 个节点。
在 reverseKGroup
函数中,使用一个循环来判断是否剩余 k 个节点。
在循环中,调用 reverse
函数来翻转当前的 k 个节点。
将当前节点组的头节点连接到下一个节点组的头节点,然后递归处理下一个节点组。
最终返回整个链表翻转后的头节点。
reverse
函数用于翻转从 first
到 last
之间的节点,它使用指针操作来实现翻转。
Python 版本解题思路:
创建一个 reverseKGroup
方法,该方法接收一个头节点和一个整数 k,用于翻转每 k 个节点。
在 reverseKGroup
方法中,使用递归来处理每个节点组。
在递归中,调用 reverse
方法来翻转当前的 k 个节点。
将当前节点组的头节点连接到下一个节点组的头节点,然后递归处理下一个节点组。
最终返回整个链表翻转后的头节点。
reverse
方法用于翻转从 first
到 last
之间的节点,它使用指针操作来实现翻转。
Java 版本解题思路:
创建一个 reverseKGroup
方法,该方法接收一个头节点和一个整数 k,用于翻转每 k 个节点。
在 reverseKGroup
方法中,使用递归来处理每个节点组。
在递归中,调用 reverse
方法来翻转当前的 k 个节点。
将当前节点组的头节点连接到下一个节点组的头节点,然后递归处理下一个节点组。
最终返回整个链表翻转后的头节点。
reverse
方法用于翻转从 first
到 last
之间的节点,它使用指针操作来实现翻转。
C++ 版本解题思路:
创建一个 reverseKGroup
方法,该方法接收一个头节点和一个整数 k,用于翻转每 k 个节点。
在 reverseKGroup
方法中,使用循环来处理每个节点组。
在循环中,调用 reverse
方法来翻转当前的 k 个节点。
将当前节点组的头节点连接到下一个节点组的头节点,然后继续循环处理下一个节点组。
最终返回整个链表翻转后的头节点。
reverse
方法用于翻转从 target
节点开始的 k 个节点,它使用指针操作来实现翻转。
/**
* 单链表节点的定义。
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// reverseKGroup 函数接收一个头节点和一个整数 k,将链表中每 k 个节点进行翻转操作。
func reverseKGroup(head *ListNode, k int) *ListNode {
node := head // 保存头节点的引用,用于后面返回
for i := 0; i < k; i++ {
if node == nil {
return head // 如果剩余节点不足 k 个,不进行翻转,直接返回头节点
}
node = node.Next // 移动节点指针到下一个节点
}
newHead := reverse(head, node) // 调用 reverse 函数翻转当前的 k 个节点
head.Next = reverseKGroup(node, k) // 递归处理剩余节点,并将返回的头节点连接到当前节点组的末尾
return newHead // 返回翻转后的头节点
}
// reverse 函数用于将从 first 到 last 之间的节点进行翻转,返回翻转后的头节点。
func reverse(first *ListNode, last *ListNode) *ListNode {
prev := last // prev 指向翻转后的链表末尾,初始为 last
for first != last {
tmp := first.Next // 保存下一个节点的引用
first.Next = prev // 将当前节点的 Next 指向 prev,实现翻转
prev = first // 更新 prev 为当前节点,以便下一轮循环使用
first = tmp // 更新 first 为下一个节点,以便下一轮循环使用
}
return prev // 返回翻转后的头节点
}
# 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: Optional[ListNode], k: int) -> Optional[ListNode]:
node = head # 保存头节点的引用,用于后面返回
for i in range(k):
if not node:
return head # 如果剩余节点不足 k 个,不进行翻转,直接返回头节点
node = node.next # 移动节点指针到下一个节点
new_head = self.reverse(head, node) # 调用 reverse 函数翻转当前的 k 个节点
head.next = self.reverseKGroup(node, k) # 递归处理剩余节点,并将返回的头节点连接到当前节点组的末尾
return new_head # 返回翻转后的头节点
# reverse 函数用于将从 first 到 last 之间的节点进行翻转,返回翻转后的头节点。
def reverse(self, first: Optional[ListNode], last: Optional[ListNode]) -> Optional[ListNode]:
prev = last # prev 指向翻转后的链表末尾,初始为 last
while first != last:
tmp = first.next # 保存下一个节点的引用
first.next = prev # 将当前节点的 next 指向 prev,实现翻转
prev = first # 更新 prev 为当前节点,以便下一轮循环使用
first = tmp # 更新 first 为下一个节点,以便下一轮循环使用
return prev # 返回翻转后的头节点
/**
* 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 node = head; // 保存头节点的引用,用于后面返回
for (int i = 0; i < k; i++) {
if (node == null) {
return head; // 如果剩余节点不足 k 个,不进行翻转,直接返回头节点
}
node = node.next; // 移动节点指针到下一个节点
}
ListNode newHead = reverse(head, node); // 调用 reverse 函数翻转当前的 k 个节点
head.next = reverseKGroup(node, k); // 递归处理剩余节点,并将返回的头节点连接到当前节点组的末尾
return newHead; // 返回翻转后的头节点
}
// reverse 函数用于将从 first 到 last 之间的节点进行翻转,返回翻转后的头节点。
private ListNode reverse(ListNode first, ListNode last) {
ListNode prev = last; // prev 指向翻转后的链表末尾,初始为 last
while (first != last) {
ListNode tmp = first.next; // 保存下一个节点的引用
first.next = prev; // 将当前节点的 next 指向 prev,实现翻转
prev = first; // 更新 prev 为当前节点,以便下一轮循环使用
first = tmp; // 更新 first 为下一个节点,以便下一轮循环使用
}
return prev; // 返回翻转后的头节点
}
}
/**
* 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:
// 主函数,翻转每 k 个节点的组
ListNode* reverseKGroup(ListNode* head, int k) {
auto cur = head; // 当前组的头节点
ListNode* prev_tail = nullptr; // 上一组的尾节点
ListNode *cur_head = nullptr; // 当前组的头节点
ListNode *cur_tail = nullptr; // 当前组的尾节点
ListNode *next_cur = nullptr; // 下一个组的头节点
ListNode *ans = nullptr; // 最终翻转后的链表的头节点
// 循环处理每个组
do {
// 调用 reverse 函数翻转当前的 k 个节点
next_cur = reverse(cur, cur_head, cur_tail, k);
if(!ans) {
ans = cur_head; // 如果 ans 为空,将当前组的头节点作为答案的头节点
}
if(prev_tail) {
prev_tail->next = cur_head; // 将上一组的尾节点与当前组的头节点相连
}
prev_tail = cur_tail; // 更新 prev_tail 为当前组的尾节点
cur = next_cur; // 将 cur 更新为下一个组的头节点
} while(next_cur);
cur_tail->next = next_cur; // 将最后一个组的尾节点与下一个组的头节点相连
return ans; // 返回整个翻转后的链表头节点
}
// 辅助函数,翻转从 target 节点开始的 k 个节点
ListNode* reverse(ListNode *target, ListNode *(&ret_head), ListNode *(&ret_tail), int k) {
if(!target->next) {
ret_head = target;
ret_tail = target;
return nullptr; // 如果 target 节点后面没有节点了,返回空,表明不需要翻转
}
int cnt = 1;
ListNode *tmp = target;
for(; tmp->next != nullptr; tmp=tmp->next) {
if(cnt >= k ) {
break;
}
cnt++;
}
if(cnt < k) {
ret_head = target;
ret_tail = tmp;
return nullptr; // 如果剩余节点数量不足 k,返回空,表明不需要翻转
}
ret_tail = target; // 更新当前组的尾节点为 target
auto cur = target;
auto next = target->next;
cnt = 1;
while(next && cnt < k) {
auto next_next = next->next;
next->next = cur;
cur = next;
next = next_next;
cnt++;
}
ret_head = cur; // 更新当前组的头节点为 cur
ret_tail->next = nullptr; // 将当前组的尾节点的 next 指向空,表示该组翻转后的尾节点
return next; // 返回下一个组的头节点
}
};
Go 版本代码:
结构体与指针: Go 使用结构体表示链表节点,这是一种自定义的数据结构。代码中的 ListNode 结构体有一个整数字段 Val 和一个指向下一个节点的指针字段 Next。head 是指向链表头节点的指针。
递归: reverseKGroup 函数使用递归来处理每一组节点的翻转。递归是一种重要的编程技巧,它可以在解决问题时将问题拆分成更小的相同子问题。
Python 版本代码:
类与方法: Python 使用类来定义链表节点,代码中的 ListNode 类包含整数字段 val 和指向下一个节点的引用字段 next。Solution 类定义了处理问题的方法。
递归: 与 Go 版本相同,Python 版本也使用递归来处理每一组节点的翻转。
Java 版本代码:
类与方法: Java 也使用类来定义链表节点,代码中的 ListNode 类有一个整数字段 val 和一个指向下一个节点的引用字段 next。Solution 类定义了处理问题的方法。
递归: 与 Go 和 Python 版本类似,Java 版本也使用递归来处理每一组节点的翻转。
C++ 版本代码:
类与指针: C++ 使用 struct 结构体来定义链表节点,代码中的 ListNode 结构体有一个整数字段 val 和一个指向下一个节点的指针字段 next。Solution 类定义了处理问题的方法。
递归: 与其他版本一样,C++ 版本也使用递归来处理每一组节点的翻转。
指针操作: C++ 版本中涉及指针的操作,如节点的指针赋值、遍历等。理解指针的基本概念和操作对于理解这些代码是很重要的。
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Example 1:
Given nums = [1,1,2],
Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively.
It doesn't matter what you leave beyond the returned length.
Example 2:
Given nums = [0,0,1,1,1,2,2,3,3,4],
Your function should return length = 5, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively.
It doesn't matter what values are set beyond the returned length.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by reference. (i.e., without making a copy)
int len = removeElement(nums, val);
// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
print(nums[i]);
}
给定一个有序数组 nums,对数组中的元素进行去重,使得原数组中的每个元素只有一个。最后返回去重以后数组的长度值。
Go 版本解题思路:
i
,用于指向当前不重复元素的位置。i
和 j
,其中 i
指向当前不重复元素的位置,而 j
用于遍历数组。i
向前移动一位,然后将当前不重复的元素放置在指针 i
的位置。Python 版本解题思路:
i
,用于指向当前不重复元素的位置。i
和 j
,其中 i
指向当前不重复元素的位置,而 j
用于遍历列表。i
向前移动一位,然后将当前不重复的元素放置在指针 i
的位置。Java 版本解题思路:
i
,用于指向当前不重复元素的位置。i
和 j
,其中 i
指向当前不重复元素的位置,而 j
用于遍历数组。i
向前移动一位,然后将当前不重复的元素放置在指针 i
的位置。C++ 版本解题思路:
i
,用于指向当前不重复元素的位置。i
和 j
,其中 i
指向当前不重复元素的位置,而 j
用于遍历向量。i
向前移动一位,然后将当前不重复的元素放置在指针 i
的位置。// 定义一个函数,用于从排序后的整数数组中去除重复元素,并返回新数组的长度
func removeDuplicates(nums []int) int {
// 如果数组为空,直接返回0
if len(nums) == 0 {
return 0
}
// 初始化一个指针i,用于指向当前不重复元素的位置
i := 0
// 遍历数组,从第二个元素开始
for j := 1; j < len(nums); j++ {
// 如果当前元素不等于前一个元素,说明找到了一个新的不重复元素
if nums[i] != nums[j] {
// 将指针i向前移动一位
i++
// 将当前不重复的元素放置在指针i的位置
nums[i] = nums[j]
}
}
// 返回不重复元素的数量(长度),需要加1,因为数组下标从0开始
return i + 1
}
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# 如果数组为空,直接返回0
if not nums:
return 0
# 初始化一个指针i,用于指向当前不重复元素的位置
i = 0
# 遍历数组,从第二个元素开始
for j in range(1, len(nums)):
# 如果当前元素不等于前一个元素,说明找到了一个新的不重复元素
if nums[i] != nums[j]:
# 将指针i向前移动一位
i += 1
# 将当前不重复的元素放置在指针i的位置
nums[i] = nums[j]
# 返回不重复元素的数量(长度),需要加1,因为数组下标从0开始
return i + 1
class Solution {
public int removeDuplicates(int[] nums) {
// 如果数组为空,直接返回0
if (nums.length == 0) {
return 0;
}
// 初始化一个指针i,用于指向当前不重复元素的位置
int i = 0;
// 遍历数组,从第二个元素开始
for (int j = 1; j < nums.length; j++) {
// 如果当前元素不等于前一个元素,说明找到了一个新的不重复元素
if (nums[i] != nums[j]) {
// 将指针i向前移动一位
i++;
// 将当前不重复的元素放置在指针i的位置
nums[i] = nums[j];
}
}
// 返回不重复元素的数量(长度),需要加1,因为数组下标从0开始
return i + 1;
}
}
#include
class Solution {
public:
int removeDuplicates(std::vector& nums) {
// 如果数组为空,直接返回0
if (nums.empty()) {
return 0;
}
// 初始化一个指针i,用于指向当前不重复元素的位置
int i = 0;
// 遍历数组,从第二个元素开始
for (int j = 1; j < nums.size(); j++) {
// 如果当前元素不等于前一个元素,说明找到了一个新的不重复元素
if (nums[i] != nums[j]) {
// 将指针i向前移动一位
i++;
// 将当前不重复的元素放置在指针i的位置
nums[i] = nums[j];
}
}
// 返回不重复元素的数量(长度),需要加1,因为数组下标从0开始
return i + 1;
}
};
Go 版本解法所需基础知识:
for
循环和如何迭代数组的元素。i
是一个指向数组元素的指针,你需要了解指针的概念和如何使用它来修改数组元素。Python 版本解法所需基础知识:
for
循环和如何迭代列表的元素。Java 版本解法所需基础知识:
for
循环和如何迭代数组的元素。C++ 版本解法所需基础知识:
for
循环和如何迭代向量的元素。Given an array nums and a value val, remove all instances of that value in-place and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
The order of elements can be changed. It doesn’t matter what you leave beyond the new length.
Example 1:
Given nums = [3,2,2,3], val = 3,
Your function should return length = 2, with the first two elements of nums being 2.
It doesn't matter what you leave beyond the returned length.
Example 2:
Given nums = [0,1,2,2,3,0,4,2], val = 2,
Your function should return length = 5, with the first five elements of nums containing 0, 1, 3, 0, and 4.
Note that the order of those five elements can be arbitrary.
It doesn't matter what values are set beyond the returned length.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by reference. (i.e., without making a copy)
int len = removeElement(nums, val);
// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
print(nums[i]);
}
给定一个数组 nums 和一个数值 val,将数组中所有等于 val 的元素删除,并返回剩余的元素个数。
以下是每个版本的解题思路的详细介绍:
Go 版本解题思路:
使用两个指针 left
和 right
分别初始化为 0 和切片 nums
的长度。left
指向数组的起始位置,right
指向数组的结束位置。
使用一个循环,条件是 left
小于 right
,表示只要 left
还在 right
的左边,就继续执行循环。
在循环中,检查 nums[left]
是否等于目标值 val
。如果相等,说明需要将这个元素删除。
如果相等,将 nums[left]
的值替换为 nums[right-1]
的值,同时将 right
减 1,相当于将目标值移到数组的末尾。这一步不需要真正删除元素,只是覆盖了数组中的值。
如果 nums[left]
不等于目标值 val
,将 left
向右移动一位,继续检查下一个元素。
循环结束后,返回 left
的值,即新数组的长度,因为所有等于目标值 val
的元素都被移到数组末尾,而剩余元素的个数就是 left
的值。
Python 版本解题思路:
初始化两个指针 fast
和 slow
,初始都为 0。
使用一个循环遍历数组 nums
,条件是 fast
小于数组的长度。
如果 nums[slow]
不等于目标值 val
,将 slow
指针向后移动一位,以保留当前元素。
如果 nums[slow]
等于目标值 val
,需要删除该元素。使用另一个循环,将后面的元素逐个左移,覆盖掉当前元素,直到将目标值 val
移除。
每次循环结束后,将 fast
指针向后移动一位,以继续遍历下一个元素。
循环结束后,slow
的值表示新数组的长度,返回 slow
。
Java 版本解题思路:
初始化两个指针 left
和 right
,其中 left
指向数组的起始位置,right
指向数组的结束位置。
使用一个循环,条件是 left
小于 right
,表示只要 left
还在 right
的左边,就继续执行循环。
在循环中,检查 nums[left]
是否等于目标值 val
。如果相等,说明需要将这个元素删除。
如果相等,将 nums[left]
的值替换为 nums[right-1]
的值,同时将 right
减 1,相当于将目标值移到数组的末尾。这一步不需要真正删除元素,只是覆盖了数组中的值。
如果 nums[left]
不等于目标值 val
,将 left
向右移动一位,继续检查下一个元素。
循环结束后,返回 left
的值,即新数组的长度,因为所有等于目标值 val
的元素都被移到数组末尾,而剩余元素的个数就是 left
的值。
C++ 版本解题思路:
初始化两个指针 left
和 right
,其中 left
指向向量的起始位置,right
指向向量的结束位置。
使用一个循环,条件是 left
小于 right
,表示只要 left
还在 right
的左边,就继续执行循环。
在循环中,检查 nums[left]
是否等于目标值 val
。如果相等,说明需要将这个元素删除。
如果相等,将 nums[left]
的值替换为 nums[right-1]
的值,同时将 right
减 1,相当于将目标值移到数组的末尾。这一步不需要真正删除元素,只是覆盖了数组中的值。
如果 nums[left]
不等于目标值 val
,将 left
向右移动一位,继续检查下一个元素。
循环结束后,返回 left
的值,即新向量的长度,因为所有等于目标值 val
的元素都被移到向量末尾,而剩余元素的个数就是 left
的值。
func removeElement(nums []int, val int) int {
// 定义左右两个指针,初始位置分别为切片的起始位置和结束位置
left, right := 0, len(nums)
// 使用循环,当左指针小于右指针时,继续执行操作
for left < right {
// 如果左指针指向的元素等于目标值 val
if nums[left] == val {
// 将左指针指向的元素替换为右指针指向的元素
// 同时将右指针向左移动一位
nums[left] = nums[right - 1]
right--
} else {
// 如果左指针指向的元素不等于目标值 val
// 则将左指针向右移动一位,继续检查下一个元素
left++
}
}
// 循环结束后,左指针的位置就是新切片的长度
return left
}
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 初始化两个指针 fast 和 slow,表示快慢指针
fast, slow = 0, 0
# 使用 while 循环遍历列表
while fast < len(nums):
# 如果慢指针指向的元素不等于目标值 val
if nums[slow] != val:
slow += 1
else:
# 如果慢指针指向的元素等于目标值 val,需要删除该元素
i = slow
while i < len(nums) - 1:
nums[i] = nums[i + 1]
i += 1
# 快指针向右移动一位
fast += 1
# 返回慢指针的位置,即新列表的长度
return slow
class Solution {
public int removeElement(int[] nums, int val) {
// 初始化左右两个指针,left指向切片的起始位置,right指向切片的结束位置
int left = 0;
int right = nums.length;
// 遍历数组,当left小于right时,继续执行
while (left < right) {
// 如果当前元素等于目标值val
if (nums[left] == val) {
// 将当前元素替换为右指针指向的元素
// 同时将右指针向左移动一位
nums[left] = nums[right - 1];
right--;
} else {
// 如果当前元素不等于目标值val
// 则将左指针向右移动一位,继续检查下一个元素
left++;
}
}
// 循环结束后,左指针的位置就是新数组的长度
return left;
}
}
class Solution {
public:
int removeElement(vector& nums, int val) {
// 初始化左右两个指针,left指向向量的起始位置,right指向向量的结束位置
int left = 0;
int right = nums.size();
// 遍历向量,当left小于right时,继续执行
while (left < right) {
// 如果当前元素等于目标值val
if (nums[left] == val) {
// 将当前元素替换为右指针指向的元素
// 同时将右指针向左移动一位
nums[left] = nums[right - 1];
right--;
} else {
// 如果当前元素不等于目标值val
// 则将左指针向右移动一位,继续检查下一个元素
left++;
}
}
// 循环结束后,left的位置就是新向量的长度
return left;
}
};
当讨论这些不同编程语言版本的解决方案时,需要了解以下基本知识:
Go 版本
left
和 right
。Python 版本
Java 版本
C++ 版本
vector
来表示动态数组,也可以使用数组。vector
的基本操作,包括访问和修改元素。Implement strStr().
Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
Example 1:
Input: haystack = "hello", needle = "ll"
Output: 2
Example 2:
Input: haystack = "aaaaa", needle = "bba"
Output: -1
Clarification:
What should we return when needle is an empty string? This is a great question to ask during an interview.
For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C’s strstr() and Java’s indexOf().
实现一个查找 substring 的函数。如果在母串中找到了子串,返回子串在母串中出现的下标,如果没有找到,返回 -1,如果子串是空串,则返回 0 。
Go 版本
解法一:
haystack
中迭代每个字符,外循环从 i
开始,内循环从 j
开始。haystack[i+j]
和 needle[j]
,如果不相等,则跳出内循环,继续外循环。j
等于 needle
的长度),则表示找到了匹配的子串,返回 i
,即匹配子串在 haystack
中的起始索引。i+j
等于 haystack
的长度),仍未找到匹配,返回 -1。解法二:
strings.Index
函数,该函数会在主字符串 haystack
中查找子串 needle
的第一次出现,并返回其索引。Python 版本
解法一:
haystack
中迭代每个字符,外循环从 i
开始,内循环从 j
开始。haystack[i+j]
和 needle[j]
,如果不相等,则跳出内循环,继续外循环。j
等于 needle
的长度),则表示找到了匹配的子串,返回 i
,即匹配子串在 haystack
中的起始索引。i+j
等于 haystack
的长度),仍未找到匹配,返回 -1。解法二:
str.find(needle)
,该方法会在主字符串 haystack
中查找子串 needle
的第一次出现,并返回其索引。Java 版本
解法一:
haystack
中迭代每个字符,外循环从 i
开始,内循环从 j
开始。haystack.charAt(i+j)
和 needle.charAt(j)
,如果不相等,则跳出内循环,继续外循环。j
等于 needle
的长度),则表示找到了匹配的子串,返回 i
,即匹配子串在 haystack
中的起始索引。i+j
等于 haystack
的长度),仍未找到匹配,返回 -1。解法二:
haystack.indexOf(needle)
,该方法会在主字符串 haystack
中查找子串 needle
的第一次出现,并返回其索引。C++ 版本
解法一:
haystack
中迭代每个字符,外循环从 i
开始,内循环从 j
开始。haystack[i+j]
和 needle[j]
,如果不相等,则跳出内循环,继续外循环。j
等于 needle
的长度),则表示找到了匹配的子串,返回 i
,即匹配子串在 haystack
中的起始索引。i+j
等于 haystack
的长度),仍未找到匹配,返回 -1。解法二:
haystack.find(needle)
,该方法会在主字符串 haystack
中查找子串 needle
的第一次出现,并返回其索引。这些解题思路详细描述了每个版本的代码是如何逐步寻找匹配子串的过程,并在找到匹配时返回相应的索引,或在未找到匹配时返回 -1。
import "strings"
// 解法一
func strStr(haystack string, needle string) int {
// 外层循环遍历haystack中的每个字符
for i := 0; ; i++ {
// 内层循环遍历needle中的每个字符
for j := 0; ; j++ {
// 如果j等于needle的长度,说明needle中的所有字符都已经在haystack中匹配成功
if j == len(needle) {
return i // 返回匹配成功的起始索引位置i
}
// 如果i+j等于haystack的长度,说明已经遍历完haystack但仍未找到匹配
if i+j == len(haystack) {
return -1 // 返回-1表示未找到匹配
}
// 如果当前needle中的字符与当前haystack中的字符不相等,跳出内层循环
if needle[j] != haystack[i+j] {
break
}
}
}
}
// 解法二
func strStr1(haystack string, needle string) int {
// 使用标准库strings的Index函数来查找needle在haystack中的位置
// 如果找到,返回第一次出现的索引位置;如果未找到,返回-1
return strings.Index(haystack, needle)
}
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 解法一:自己实现的字符串匹配算法
for i in range(len(haystack) + 1):
for j in range(len(needle) + 1):
if j == len(needle):
return i
if i + j == len(haystack):
return -1
if needle[j] != haystack[i + j]:
break
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 解法二:使用Python内置函数index
return haystack.find(needle)
class Solution {
public int strStr(String haystack, String needle) {
// 解法一:自己实现的字符串匹配算法
for (int i = 0; ; i++) {
for (int j = 0; ; j++) {
if (j == needle.length()) {
return i;
}
if (i + j == haystack.length()) {
return -1;
}
if (needle.charAt(j) != haystack.charAt(i + j)) {
break;
}
}
}
}
}
class Solution {
public int strStr(String haystack, String needle) {
// 解法二:使用Java内置函数indexOf
return haystack.indexOf(needle);
}
}
class Solution {
public:
int strStr(string haystack, string needle) {
// 解法一:自己实现的字符串匹配算法
for (int i = 0; ; i++) {
for (int j = 0; ; j++) {
if (j == needle.length()) {
return i;
}
if (i + j == haystack.length()) {
return -1;
}
if (needle[j] != haystack[i + j]) {
break;
}
}
}
}
};
class Solution {
public:
int strStr(string haystack, string needle) {
// 解法二:使用C++内置函数find
size_t index = haystack.find(needle);
return index != string::npos ? index : -1;
}
};
Go 版本
解法一:
解法二:
strings.Index
。Python 版本
解法一:
解法二:
str.find
。Java 版本
解法一:
解法二:
indexOf
。C++ 版本
解法一:
解法二:
find
。static_cast
进行数据类型转换,因为 find
返回的是 size_t
类型,而我们需要返回 int
类型的结果。