一天一大 leet(从先序遍历还原二叉树)难度:困难 DAY-18

20200618

img

题目(难度:困难):

我们从二叉树的根节点 root 开始进行深度优先搜索。

在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。

如果节点只有一个子节点,那么保证该子节点为左子节点。

给出遍历输出 S,还原树并返回其根节点 root。

示例

  • 示例 1
    1
   / \
  2   3
 / \   / \
4  5  6   7
输入:"1-2--3--4-5--6--7"
输出:[1,2,5,3,4,6,7]
  • 示例 2
        1
      /  \
     2    5
    /    /
   3    6
  /    /
 4    7
输入:"1-2--3---4-5--6---7"
输出:[1,2,5,3,null,6,null,4,null,7]
  • 示例 3
       1
      /
    401
    /  \
  349  88
  /
 90
输入:"1-401--349---90--88"
输出:[1,401,null,349,88,90]

提示

  1. 原始树中的节点数介于 1 和 1000 之间。
  2. 每个节点的值介于 1 和 10 ^ 9 之间。

抛砖引玉

img

  • 输入一个字符串
  • 字符串中n个’-'表示n级
  • '—n…—m’表示n和m都是上一级的子集,n是left,m是right,先left后right
  • 字符串顺序:一条线所有的子节点+下一条线所有子节点…

逻辑

  • 先取出第一个数组作为树的根节点
  • 循环字符串记录’-'的个数,多少个n就记录多少级,根据级数把能得到的树放到数组中
  • 记录level和取本层数据的注意事项:
    • 数字和’-'切换时重置level和value
  • 因为字符串顺序:一条线所有的子节点+下一条线所有子节点…,以第二层为例:
    • 第一条线循环到结束后可能还会在之后一个有level为2的数据,
    • 那就可以判断这个level是不是已经有了left、right没有的话就给员树增加新的树节点
    • 依次类推,所有后面出现的节点本身都是有依附节点的,最终所有的节点都会追加到起始的根节点
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {string} S
 * @return {TreeNode}
 */
var recoverFromPreorder = function (S) {
  var level = 0,
    value = 0
  var _result = [new TreeNode(S.split('-')[0])]
  for (let i = 1; i < S.length; i++) {
    if (S[i] == '-') {
      level++
      value = 0
    } else if (level > 0) {
      value = value * 10 + Number(S[i])
      if ((S[i + 1] && S[i + 1] === '-') || i === S.length - 1) {
        var nodeTree = new TreeNode(value)
        if (_result[level - 1].left == null) {
          _result[level - 1].left = nodeTree
        } else {
          _result[level - 1].right = nodeTree
        }
        _result[level] = nodeTree
        level = 0
      }
    }
  }
  return _result[0]
}

其他解法

/**
 * @param {number[]} A
 * @return {number}
 */
var recoverFromPreorder = function (S) {
	const stack = []
	for (let i = 0; i < S.length;) {
		let curLevel = 0 //一个curNode对应一个curLevel
		while (i < S.length && S[i] == '-') { // 避免循环半途中出界
			i++
			curLevel++    // 连字符个数代表level
		}
		const start = i // 记录当前节点值的开始位置
		while (i < S.length && S[i] != '-') {
			i++           // 指针移到当前节点值的结束位置
		}
		const curNode = new TreeNode(S.slice(start, i)) //创建当前节点
		if (stack.length == 0) { // ROOT入栈,不用找父亲,continue
			stack.push(curNode)
			continue
		}
		while (stack.length > curLevel) { // 栈顶不是父亲,栈顶出栈
			stack.pop()                     // 直到栈顶是父亲
		}
		if (stack[stack.length - 1].left) {       // 左儿子已存在
			stack[stack.length - 1].right = curNode // 安排为右儿子
		} else {
			stack[stack.length - 1].left = curNode  // 安排为左儿子
		}
		stack.push(curNode) // 节点肯定要入栈一次
	}
	return stack[0]       // 栈底就是根节点
};
  • 使用递归,一个层级一次递归
  • 使用正则切分除本层的数据和未分离出来的字符串
    • 本层数据直接生成树结构
    • 未分离的数据根据切位的位置,递归进入left和right
/**
 * @param {number[]} A
 * @return {number}
 */
var maxScoreSightseeingPair = function (A) {
	if (!S) return null
	const reg = new RegExp(`(?<=\\d)${char}(?=\\d)`),
		[root, left, right] = S.split(reg),
		treeNode = new TreeNode(+root);
	treeNode.left = recoverFromPreorder(left, char + '-')
	treeNode.right = recoverFromPreorder(right, char + '-')
	return treeNode
}

菜鸟的自白

  • 开始的思路也是通过正则匹配出来,但是对数据的切分一直错误,才不得不去按部就班的去循环,个人觉得,正则出来起来会更简洁
// 配置 两个数字包含着的层级数[val-left,*-right]
/\d-{n}\d/g
// 跟 数字 -- 数字 切分字符串
/?<=\\d)${char}(?=\\d/g

博客: 小书童博客(http://gaowenju.com)

公号: 坑人的小书童
坑人的小书童

探险家小扣的行动轨迹,都将保存在记录仪中。expeditions[i] 表示小扣第 i 次探险记录,用一个字符串数组表示。其中的每个「营地」由大小写字母组成,通过子串 -> 连接。例:"Leet->code->Campsite",表示到访了 "Leet"、"code"、"Campsite" 三个营地。expeditions[0] 包含了初始小扣已知的所有营地;对于之后的第 i 次探险(即 expeditions[i] 且 i > 0),如果记录中包含了之前均没出现的营地,则表示小扣 新发现 的营地。 请你找出小扣发现新营地最多且索引最小的那次探险,并返回对应的记录索引。如果所有探险记录都没有发现新的营地,返回 -1。注意: 大小写不同的营地视为不同的营地; 营地的名称长度均大于 0。用python实现。给你几个例子:示例 1: 输入:expeditions = ["leet->code","leet->code->Campsite->Leet","leet->code->leet->courier"] 输出:1 解释: 初始已知的所有营地为 "leet" 和 "code" 第 1 次,到访了 "leet"、"code"、"Campsite"、"Leet",新发现营地 2 处:"Campsite"、"Leet" 第 2 次,到访了 "leet"、"code"、"courier",新发现营地 1 处:"courier" 第 1 次探险发现的新营地数量最多,因此返回 1。示例 2: 输入:expeditions = ["Alice->Dex","","Dex"] 输出:-1 解释: 初始已知的所有营地为 "Alice" 和 "Dex" 第 1 次,未到访任何营地; 第 2 次,到访了 "Dex",未新发现营地; 因为两次探险均未发现新的营地,返回 -1
04-23
我的理解是,你想让我编写一段 Python 代码,实现一个算法,用于在给定的探险记录中找到发现新营地最多且索引最小的探险记录。 那么首先,我需要解析题目中的输入输出要求。 输入要求: - expeditions:一个字符串数组,表示小扣第一次到访的所有营地,和之后的每次探险记录(一个记录就是一个字符串,其中营地通过子串 -> 连接)。 输出要求: - 如果所有的探险记录都没有发现新的营地,则返回 -1; - 否则,返回发现新营地最多且索引最小的探险记录的索引。注:探险记录的索引从 0 开始计数。 接下来,我可以考虑如何编写代码来解决这个问题。 我的思路是,首先用一个集合记录所有已知的营地名称,然后遍历每个探险记录,依次将其中的营地名称加入到集合中,并判断是否有新的营地被发现。 如果某个探险记录中发现了新的营地,那么我就记录下发现了多少个新营地,以及这个探险记录的索引。最后,遍历所有的探险记录,找到发现新营地最多且索引最小的记录。 代码如下: ```python def find_new_camp(expeditions): known_camps = set() # 已知的营地集合 max_new_camps = 0 # 最多的新营地数量 max_new_camps_index = -1 # 最多新营地数量的探险记录的索引 for i in range(len(expeditions)): sub_camps = expeditions[i].split("->") # 提取探险记录中所有的营地名称 num_new_camps = 0 # 记录这个探险记录发现的新营地数量 for camp in sub_camps: if camp not in known_camps: # 如果这个营地是新的,就记录一下 known_camps.add(camp) num_new_camps += 1 if num_new_camps > max_new_camps: # 如果发现了更多的新营地,就更新最大值和索引 max_new_camps = num_new_camps max_new_camps_index = i return max_new_camps_index if max_new_camps > 0 else -1 ``` 我使用了两个变量 `max_new_camps` 和 `max_new_camps_index` 来记录已经遍历过的探险记录中,发现了最多的新营地数量,以及具有最多新营地数量的探险记录的索引。然后我遍历所有的探险记录,统计其中发现的新营地数量,如果大于当前的最大值,就更新 `max_new_camps` 和 `max_new_camps_index` 变量。最后,如果发现了新的营地,就返回 `max_new_camps_index`。如果所有的探险记录都没有发现新营地,就返回 -1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值