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

server/2025/3/18 2:17:18/

篇1:数组/字符串

    • 1. [简单] 合并两个有序数组(leetcode88题)
      • 题目描述
      • 解题思路
      • python代码
    • 2. [简单] 删除有序数组中的重复项(leetcode 26题)
      • 题目描述
      • python代码
    • 3. [中等] 删除有序数组中的重复项2(leetcode 80题)
    • 4. [中等] 轮转数组(leetcode 189题)
      • 题目描述
      • python代码

leetcode88_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 - 1while p1 >= 0 and p2 >= 0:if nums1[p1] > nums2[p2]:nums1[p] = nums1[p1]p1 -= 1else:nums1[p] = nums2[p2]p2 -= 1p -= 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 = 3merge_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),因为我们只使用了常数级别的额外空间。这是一种非常高效的解决方案,特别适合于原地合并两个有序数组的情况。

leetcode_26_75">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 += 1return 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),因为我们只使用了常数级别的额外空间。这使得该方法非常适合处理大型数组的情况。

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

leetcode_189_147">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) - 1n = 0while n < len(nums):pos = target_index + kif pos >= len(nums):pos = pos - len(nums)#保持将要替换的值tmp = nums[pos]target_index = posnums[pos] = targettarget = tmpn += 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 += 1end -= 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]

http://www.ppmy.cn/server/175833.html

相关文章

leetcode日记(101)填充每个节点的下一个右侧节点指针Ⅱ

意料之中有这题&#xff0c;将之前的思路换一下即可&#xff0c;层序遍历的思路将record&#xff08;记录下一个循环的次数&#xff09;手动加减。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL)…

IP 协议

文章目录 IP 协议概述数据包格式首部校验和实例分析实例一 分片抓包分析参考 本文为笔者学习以太网对网上资料归纳整理所做的笔记&#xff0c;文末均附有参考链接&#xff0c;如侵权&#xff0c;请联系删除。 IP 协议 概述 IP 协议是 TCP/IP 协议簇中的核心协议&#xff0c;也…

图像处理篇---图像预处理

文章目录 前言一、通用目的1.1 数据标准化目的实现 1.2 噪声抑制目的实现高斯滤波中值滤波双边滤波 1.3 尺寸统一化目的实现 1.4 数据增强目的实现 1.5 特征增强目的实现&#xff1a;边缘检测直方图均衡化锐化 二、分领域预处理2.1 传统机器学习&#xff08;如SVM、随机森林&am…

kafka 中的 rebalance

Kafka 的 Rebalance&#xff08;重平衡&#xff09;机制本质上是一个协调过程&#xff0c;用于在消费者组内动态分配分区&#xff0c;以保证消费任务均匀分布。Rebalance 主要由 Kafka Consumer Group 协议&#xff08;Group Membership Protocol&#xff09;驱动&#xff0c;涉…

[特殊字符] 深度实战:Android 13 系统定制之 Recovery 模式瘦身指南

&#x1f31f; 核心需求 在 Android 13 商显设备开发中&#xff0c;需精简 Recovery 模式的菜单选项&#xff08;如Reboot to bootloader/Enter rescue&#xff09;&#xff0c;但直接修改g_menu_actions后在User 版本出现黑屏卡死问题&#xff0c;需综合方案解决。 &#x1f5…

Flask中的装饰器

在 Flask 中&#xff0c;装饰器&#xff08;Decorator&#xff09;是一种 Python 语法特性&#xff0c;它允许你在不修改原始函数的情况下&#xff0c;扩展其功能。Flask 使用装饰器来定义路由、请求前后钩子、中间件等。 1. Flask 装饰器的基本概念 Python 的装饰器本质上是一…

FPGA学习(二)——实现LED流水灯

FPGA学习(二)——实现LED流水灯 目录 FPGA学习(二)——实现LED流水灯一、DE2-115时钟源二、控制6个LED灯实现流水灯1、核心逻辑2、代码实现3、引脚配置4、实现效果 三、模块化代码1、分频模块2、复位暂停模块3、顶层模块 四、总结 一、DE2-115时钟源 DE2-115板子包含一个50MHz…

DataWhale 大语言模型 - GPT和DeepSeek模型介绍

本课程围绕中国人民大学高瓴人工智能学院赵鑫教授团队出品的《大语言模型》书籍展开&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的核心技术。并且&#xff0c;课程…