目录

leetcode-面试经典-150-题篇1数组字符串

[leetcode] 面试经典 150 题——篇1:数组/字符串

1 [简单] 合并两个有序数组(leetcode88题)

题目描述

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 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 中。

解题思路

为了合并两个按非递减顺序排列的整数数组 nums1 和 nums2,同时保持合并后的数组也按非递减顺序排列,我们可以采用从后向前遍历的方式进行合并。这种方法利用了两个数组已经排序的特点,并且由于 nums1 已经预留了足够的空间(大小为 m+n),可以避免额外的空间开销和不必要的元素移动。

python代码

def merge_sorted_arrays(nums1, m, nums2, n): """ 合并两个已排序的整数数组到第一个数组中,使结果保持排序状态。 参数: nums1 (list of int): 第一个整数数组,其大小至少为 m + n,前m个元素是有效的数字,后面的n个位置初始化为0留作合并使用 m (int): nums1 中实际填充的元素数量 nums2 (list of int): 第二个整数数组 n (int): nums2 中的元素数量 返回: None: 结果直接存储在 nums1 中 """

从后向前遍历数组

p1, p2, p = m - 1, n - 1, m + n - 1 while p1 >= 0 and p2 >= 0: if nums1[p1] > nums2[p2]: nums1[p] = nums1[p1] p1 -= 1 else: nums1[p] = nums2[p2] p2 -= 1 p -= 1

如果nums2还有剩余元素,直接复制到nums1前面

注意:如果nums1有剩余,不需要处理,因为这些元素已经在正确的位置上

nums1[:p2 + 1] = nums2[:p2 + 1]

示例调用

nums1 = [1, 2, 3, 0, 0, 0] # 注意这里的长度是m+n,后面n个位置初始化为0 m = 3 nums2 = [2, 5, 6] n = 3 merge_sorted_arrays(nums1, m, nums2, n) print(nums1) # 输出应为 [1, 2, 2, 3, 5, 6]

  • 初始化指针:我们定义三个指针 p1, p2, 和 p。p1 指向 nums1 中有效部分的最后一个元素,p2 指向 nums2 的最后一个元素,而 p 则指向整个 nums1 数组(包括预留空间)的最后一个位置。
  • 从后向前合并:通过比较 nums1[p1] 和 nums2[p2] 的值,将较大的值放置到 nums1[p] 的位置,并相应地更新指针 p1 或 p2 和 p。这样做可以确保不会覆盖 nums1 中未处理的有效数据。
  • 处理剩余元素:如果 nums2 中仍有剩余元素(即 p2 >= 0),则直接将其复制到 nums1 的开头。这是因为当 p1 先变为负数时,意味着 nums1 原有的所有元素都已经被适当安排,而剩下的位置应该由 nums2 的剩余元素填充。
  • 这种方法的时间复杂度是 O(m + n),因为我们只需要一次遍历来完成合并操作,空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。这是一种非常高效的解决方案,特别适合于原地合并两个有序数组的情况。

2 [简单] 删除有序数组中的重复项(leetcode 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 。不需要考虑数组中超出新长度后面的元素。

python代码

def remove_duplicates(nums): """ 原地删除数组中的重复项,使每个元素只出现一次,并返回数组的新长度。 参数: nums (list of int): 输入的整数数组。 返回: int: 数组中唯一元素的个数。 """ if not nums: return 0

初始化指针

length = 1 # 第一个元素总是唯一的,从第二个位置开始检查 for i in range(1, len(nums)):

如果当前元素与前一个元素不同,则它是唯一的

if nums[i] != nums[length - 1]: nums[length] = nums[i] length += 1 return length

示例调用

nums = [1, 1, 2] k = remove_duplicates(nums) print(k) # 输出应为2 print(nums[:k]) # 输出应为[1, 2] nums = [0,0,1,1,1,2,2,3,3,4] k = remove_duplicates(nums) print(k) # 输出应为5 print(nums[:k]) # 输出应为[0, 1, 2, 3, 4]

  • 初始化 :首先检查输入数组是否为空,如果为空则直接返回 0,因为没有元素也就没有唯一元素。然后,我们设定 length 变量为 1,这是因为第一个元素(索引为 0)总是唯一的,我们从第二个元素开始进行比较。
  • 遍历数组 :使用一个循环从数组的第二个元素开始遍历(即从索引 1 开始)。对于每个元素,检查它是否与已经确定为唯一元素的最后一个元素(即 nums[length - 1])相同。如果不相同,这意味着当前元素是新的唯一元素,因此将其放置在 nums[length] 的位置,并增加 length 计数器。
  • 返回结果 :循环结束后,length 就是数组中唯一元素的数量。函数返回 length,并且此时 nums 的前 length 个元素包含了所有不重复的元素,按它们最初出现的顺序排列。 这种方法的时间复杂度是 O(n),其中 n 是数组的长度,因为我们只需要遍历数组一次。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。这使得该方法非常适合处理大型数组的情况。

3 [中等] 删除有序数组中的重复项2(leetcode 80题)

4 [中等] 轮转数组(leetcode 189题)

题目描述

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例 1: 输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4] 示例 2: 输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]

python代码

这是一段错误的代码: def rotate(self, nums, k): """ :type nums: List[int] :type k: int :rtype: None Do not return anything, modify nums in-place instead. """

if k = 0:

target = nums[-1] target_index = len(nums) - 1 n = 0 while n < len(nums): pos = target_index + k if pos >= len(nums): pos = pos - len(nums) #保持将要替换的值 tmp = nums[pos] target_index = pos nums[pos] = target target = tmp n += 1 代码尝试通过逐个元素移动的方式来实现数组的右轮转,但存在一些问题可能导致其不能正确运行。以下是代码中的几个主要问题:

  • if k = 0: 这行被注释掉了,但是即使未注释,这里的条件判断使用的是赋值运算符 = 而不是比较运算符 ==。如果保留这行,应该改为 if k == 0:
  • 算法逻辑错误:这段代码的逻辑是每次只移动一个元素到它的新位置,然后用临时变量保存被覆盖位置的原始值,继续进行下一个元素的放置。然而,这种方法没有考虑到所有元素完成一次轮换可能需要多于一轮的遍历,并且在某些情况下(如当k是数组长度的倍数时),它不会正确地将所有元素放到它们的新位置上。
  • 处理k大于数组长度的情况:代码中没有对k大于数组长度的情况进行处理。在这种情况下,实际的轮转次数等效于k % len(nums),因为向右轮转数组长度次相当于没有改变数组。 循环遍历整个数组的必要性:在当前的实现方式下,遍历整个数组(n < len(nums))并不是总是正确的解决方案,特别是对于较大的k值或特定的数组配置,可能会导致不必要的替换或者进入无限循环(取决于具体的边界条件和初始状态)。 为了修复这些问题,可以考虑以下改进方案之一:
  • 使用额外的空间来创建一个新的数组并直接计算每个元素的新位置。
  • 或者,更高效的方法是反转整个数组 ,然后分别反转前k个元素和剩余的元素。这样可以在O(1)额外空间复杂度内解决问题。 通过代码反转实现: def rotate(nums, k): """ :type nums: List[int] :type k: int :rtype: None Do not return anything, modify nums in-place instead. """ n = len(nums) k %= n # 处理k大于数组长度的情况 def reverse(start, end): while start < end:

交换元素

nums[start], nums[end] = nums[end], nums[start] start += 1 end -= 1

反转整个数组

reverse(0, n - 1)

反转前k个元素

reverse(0, k - 1)

反转剩余的元素

reverse(k, n - 1) #示例用法 nums_example = [1, 2, 3, 4, 5, 6, 7] rotate(nums_example, 3) print(nums_example) # 应输出: [5, 6, 7, 1, 2, 3, 4]