268. 丢失的数字,283. 移动零,297. 二叉树的序列化与反序列化,每题做详细思路梳理,配套Python&Java双语代码, 2024.04.02 可通过leetcode所有测试用例。
目录
268. 丢失的数字
给定一个包含
[0, n]
中n
个数的数组nums
,找出[0, n]
这个范围内没有出现在数组中的那个数。示例 1:
输入:nums = [3,0,1] 输出:2 解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。示例 2:
输入:nums = [0,1] 输出:2 解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1] 输出:8 解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。示例 4:
输入:nums = [0] 输出:1 解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
解题思路
- 计算总和:首先计算出 [0, n] 的总和。由于是等差数列,可以直接使用等差数列求和公式
(n * (n + 1)) / 2
来计算。 - 计算数组总和:遍历数组,计算数组中所有元素的总和。
- 计算差值:用步骤 1 中计算的总和减去步骤 2 中的数组总和,得到的差值就是缺失的数字。
完整代码
Python
class Solution:
def missingNumber(self, nums: List[int]) -> int:
n = len(nums)
total_sum = n * (n + 1) // 2 # 计算 [0, n] 的总和
array_sum = sum(nums) # 计算数组的总和
return total_sum - array_sum # 缺失的数字是两者的差值
Java
public class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int totalSum = n * (n + 1) / 2; // 计算 [0, n] 的总和
int arraySum = 0;
for (int num : nums) {
arraySum += num; // 计算数组的总和
}
return totalSum - arraySum; // 缺失的数字是两者的差值
}
}
283. 移动零
给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
提示:
1 <= nums.length <= 10^4
-2^31 <= nums[i] <= 2^31-1
进阶:你能尽量减少完成的操作次数吗?
解题思路
- 初始化两个指针:一个指针(
nonZeroIndex
)用于追踪非零元素应该放置的位置,另一个指针(i
)用于遍历数组。 - 遍历数组:使用
i
指针遍历数组。当遇到非零元素时,检查i
和nonZeroIndex
是否相同:- 如果不相同,说明之前遇到了0,需要将当前非零元素移动到
nonZeroIndex
指向的位置,并将i
指向的位置设置为0。 - 如果相同,不需要做任何操作,直接增加
nonZeroIndex
。
- 如果不相同,说明之前遇到了0,需要将当前非零元素移动到
- 更新
nonZeroIndex
:每次在nonZeroIndex
位置放置了一个非零元素后,增加nonZeroIndex
。 - 重复步骤 2 和 3,直到遍历完数组。
这种方法的关键在于,nonZeroIndex
始终指向第一个0元素的位置,或者如果数组中没有0,它就指向下一个非零元素应该放置的位置。
完整代码
Python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
nonZeroIndex = 0 # 初始化非零元素应该放置的位置
for i in range(len(nums)):
if nums[i] != 0:
if i != nonZeroIndex: # 当前非零元素不在它应该在的位置
nums[nonZeroIndex], nums[i] = nums[i], 0 # 交换非零元素与第一个0的位置
nonZeroIndex += 1
Java
class Solution {
public void moveZeroes(int[] nums) {
int nonZeroIndex = 0; // 初始化非零元素应该放置的位置
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
if (i != nonZeroIndex) { // 当前非零元素不在它应该在的位置
int temp = nums[i];
nums[i] = 0;
nums[nonZeroIndex] = temp; // 交换非零元素与第一个0的位置
}
nonZeroIndex++;
}
}
}
}
297. 二叉树的序列化与反序列化
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
示例 1:
输入:root = [1,2,3,null,null,4,5] 输出:[1,2,3,null,null,4,5]示例 2:
输入:root = [] 输出:[]示例 3:
输入:root = [1] 输出:[1]示例 4:
输入:root = [1,2] 输出:[1,2]
解题思路
设计二叉树的序列化与反序列化算法是一项挑战性任务,这里将使用一种常见的方法:层次遍历(也称为广度优先遍历)。通过这种方式,我们可以保证序列化的字符串包含了足够的信息来恢复原始的树结构,包括那些空节点,这对于恢复非完全二叉树尤为重要。
序列化
- 初始化:使用队列存储待遍历的节点,首先将根节点加入队列。
- 层次遍历:当队列非空时,从队列中取出一个节点。
- 如果节点非空,将节点的值加入字符串,并将其左右子节点(即使是空节点)加入队列。
- 如果节点为空,将一个特定的占位符(如"null")加入字符串。
- 格式化:在每个值之间加入分隔符(如","),以确保序列化的字符串可以被正确分割。
反序列化
- 分割字符串:按照分隔符分割字符串,得到值的数组。
- 初始化:创建一个指针(如
index
),指向当前处理的节点值。使用队列存储父节点,首先创建根节点并将其加入队列。 - 层次遍历:当队列非空时,从队列中取出一个节点作为当前父节点,并使用
index
指针指向的值来创建左右子节点。- 如果值非占位符,创建新节点,更新
index
指针,并将新节点加入队列。 - 如果值是占位符,表示没有子节点,
index
指针向前移动即可。
- 如果值非占位符,创建新节点,更新
- 构建树:通过层次遍历,逐层构建树的结构。
完整代码
Python
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Codec:
def serialize(self, root):
"""将二叉树序列化为字符串"""
if not root:
return ""
queue = [root]
result = []
while queue:
node = queue.pop(0)
if node:
result.append(str(node.val))
queue.append(node.left)
queue.append(node.right)
else:
result.append("null")
return ",".join(result)
def deserialize(self, data):
"""将字符串反序列化为二叉树"""
if not data:
return None
values = data.split(",")
root = TreeNode(int(values[0]))
queue = [root]
index = 1
while queue:
node = queue.pop(0)
if values[index] != "null":
node.left = TreeNode(int(values[index]))
queue.append(node.left)
index += 1
if values[index] != "null":
node.right = TreeNode(int(values[index]))
queue.append(node.right)
index += 1
return root
# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# ans = deser.deserialize(ser.serialize(root))
Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// 序列化二叉树
public String serialize(TreeNode root) {
if (root == null) return "";
StringBuilder sb = new StringBuilder();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node != null) {
sb.append(node.val).append(",");
queue.add(node.left);
queue.add(node.right);
} else {
sb.append("null,");
}
}
return sb.toString();
}
// 反序列化二叉树
public TreeNode deserialize(String data) {
if (data.isEmpty()) return null;
String[] values = data.split(",");
Queue<TreeNode> queue = new LinkedList<>();
TreeNode root = new TreeNode(Integer.parseInt(values[0]));
queue.add(root);
for (int i = 1; i < values.length; i++) {
TreeNode parent = queue.poll();
if (!values[i].equals("null")) {
TreeNode left = new TreeNode(Integer.parseInt(values[i]));
parent.left = left;
queue.add(left);
}
if (!values[++i].equals("null")) {
TreeNode right = new TreeNode(Integer.parseInt(values[i]));
parent.right = right;
queue.add(right);
}
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));