序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如#.
_9_
/ \
3 2
/ \ / \
4 1 # 6
/ \ / \ / \
# # # # # #
例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中#代表一个空节点。
给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的'#'。
你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3"。
示例 1: 输入: "9,3,4,#,#,1,#,#,2,#,6,#,#" 输出: true 示例 2: 输入: "1,#" 输出: false 示例 3: 输入: "9,#,#,1" 输出: false不需要建立解法一中的额外数组,而是边解析边判断,遇到不合题意的情况直接返回 false,而不用全部解析完再来验证是否合法,提高了运算的效率。我们用一个变量 degree 表示能容忍的 "#" 的个数,degree 初始化为 1。再用一个布尔型变量 degree_is_zero 来记录 degree 此时是否为 0 的状态,这样的设计很巧妙,可以 cover 到 "#" 开头,但后面还跟有数字的情况,比如 "#,1,2" 这种情况,当检测到 "#" 时,degree 自减 1,此时若 degree 为 0 了,degree_is_zero 赋值为 true,那么如果后面还跟有其他东西的话,在下次循环开始开始前,先判断 degree_is_zero,如果为 true 的话,直接返回 false。而当首字符为数字的话,degree 自增 1,那么此时 degree 就成了 2,表示后面可以再容忍两个 "#"。当循环退出的时候,此时判断 degree 是否为 0,因为我们要补齐 "#" 的个数,少了也是不对的。
class Solution {public: bool isValidSerialization(string preorder) { istringstream in(preorder); string t = ""; int degree = 1; bool degree_is_zero = false;; while (getline(in, t, ',')) { if (degree_is_zero) return false; if (t == "#") { if (--degree == 0) degree_is_zero = true; } else ++degree; } return degree == 0; }};
给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。
示例 1:
输入: nums = [1,3], n = 6
输出: 1
解释:根据 nums 里现有的组合 [1], [3], [1,3],可以得出 1, 3, 4。现在如果我们将 2 添加到 nums 中, 组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。其和可以表示数字 1, 2, 3, 4, 5, 6,能够覆盖 [1, 6] 区间里所有的数。所以我们最少需要添加一个数字。
示例 2:
输入: nums = [1,5,10], n = 20
输出: 2
解释: 我们需要添加 [2, 4]。
示例 3:
输入: nums = [1,2,2], n = 5
输出: 0
我们定义一个变量 miss,用来表示 [0,n] 之间最小的不能表示的值,那么初始化为 1,为啥不为 0 呢,因为 n=0 没啥意义,直接返回 0 了。那么此时我们能表示的范围是 [0, miss),表示此时我们能表示 0 到 miss-1 的数,如果此时的 num <= miss,那么我们可以把我们能表示数的范围扩大到 [0, miss+num),如果 num>miss,那么此时我们需要添加一个数,为了能最大限度的增加表示数范围,我们加上 miss 它本身,以此类推直至遍历完整个数组,我们可以得到结果。
举个例子说明:
给定 nums = [1, 2, 4, 11, 30], n = 50,我们需要让 [0, 50] 之间所有的数字都能被 nums 中的数字之和表示出来。
首先使用 1, 2, 4 可能表示出 0 到 7 之间的所有数,表示范围为 [0, 8),但我们不能表示 8,因为下一个数字 11 太大了,所以我们要在数组里加上一个 8,此时能表示的范围是 [0, 16),那么我们需要插入 16 吗,答案是不需要,因为我们数组有 1 和 4,可以组成 5,而下一个数字 11,加一起能组成 16,所以有了数组中的 11,我们此时能表示的范围扩大到 [0, 27),但我们没法表示 27,因为 30 太大了,所以此时我们给数组中加入一个 27,那么现在能表示的范围是 [0, 54),已经满足要求了,我们总共添加了两个数 8 和 27,所以返回 2 即可。
class Solution {public: int minPatches(vector& nums, int n) { long miss = 1, res = 0, i = 0; while (miss <= n) { if (i < nums.size() && nums[i] <= miss) { miss += nums[i++]; } else { miss += miss; ++res; } } return res; }};
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i 使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
示例 1:
输入: [1,2,3,4,5]
输出: true
示例 2:
输入: [5,4,3,2,1]
输出: false
"中间" 那个数是很重要的。所以就是用 m 来代替。m 之前始终有个比他小的数(l,或曾经的 l)。所以如果当前遍历到的元素大于了 m,那么就 return true。
1 class Solution { 2 public boolean increasingTriplet(int[] nums) { 3 int numsSize=nums.length; 4 if(numsSize<3) return false; 5 int l=nums[0],m=Integer.MAX_VALUE; 6 for(int i=1;i 7 int a=nums[i]; 8 if(a<=l) l=a; 9 else if(a 10 else if(a>m) return true; 11 } 12 return false; 13 } 14 }