vlambda博客
学习文章列表

笔试题 判定合法二叉树

首先解决留下的笔试题。


如果 n 根绳子可以剪成 m 根长度为 k 的绳子,那一定可以剪成更小的。所以,原问题就转化成了求最后一个满足题意数。


下面给出核心代码:

bool check(double x){ int cnt = 0; for(auto u : a) cnt += (int)(1.0 * u / x); return cnt >= m;}
double l = 0, r = maxn; //maxn表示最长的绳子while(r - l > eps) //eps为精度,要求保留两位小数通常精度设为1e-4{ double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid;}

--------------------------


leetcode331. 验证二叉树的前序序列化


https://leetcode-cn.com/problems/verify-preorder-serialization-of-a-binary-tree/


在遍历二叉树时,我们可以依次记录下节点的值,如果遇到空节点用 # 标记。

现给定一串以逗号分隔的序列,在不构造树的前提下,判断是否是正确的二叉树前序遍历的结果。


样例:

 _9_ / \ 3 2 / \ / \ 4 1 # 6/ \ / \ / \# # # # # #

输入:"9, 3, 4, #, #, 1, #, #, 2, #, 6, #, #"

输出:true


思路

由于不能建树,所以只能从二叉树的属性入手,寻找足够限制二叉树的条件。下面给出两种思路。


1. 开心消消乐 (栈)


在前序遍历的过程中,第一颗子树被完全遍历时,一定会有这样的结构:

   x      / \    # #

所以我们只要每遇到 "x, #, #" 这样的结构,就把它替换为 “#” ,如果是合法二叉树,最后一定是只剩 “#“,下面模拟一下过程:

整个过程可以用栈来实现:


依次将遇到的节点入栈,每当 ”#“ 入栈时,检查当前栈顶三个元素是否依次为 "#", "#", "x"(不是 # 即可)。我们可以实现一个 merge 函数:

string stk;  //使用字符串来模拟栈,方便取栈顶三个元素int idx;    //表示当前栈内有多少元素void merge(){ while(idx >= 3 && stk[idx - 1] == '#' &&  stk[idx - 2] == '#' && stk[idx - 3] != '#') { stk.pop_back(); stk.pop_back(); stk.pop_back(); stk.push_back('#'); idx -= 2; }}

这里使用字符串来模拟栈,方便检查栈顶三个元素,在元素进栈和出栈时维护元素个数 idx 的值。核心代码部分:

bool isValidSerialization(string p) { int n = p.size(); for(int i = 0; i < n; i++) { if(p[i] == '#') stk.push_back(p[i]), idx++, merge(); else if(isdigit(p[i])) { while(i < n && isdigit(p[i])) i++; stk.push_back('0'); idx++; } } return stk == "#";}


2. 入度与出度

对于一个二叉树,除根结点以外:

每个非空节点提供 1 个入度和 2 个出度;

每个空节点提供 1 个入度。


我们可以在遍历的时候维护一个 diff = out(出度) - in(入度):

每出现一个节点,diff - 1,因为每个节点都会提供一个入度;

若是非空节点 diff + 2,因为提供两个出度。


由于前序遍历中,头节点都是先出现的,因此在整个过程中 diff >= 0,且最后为0。下面给出实现:

bool isValidSerialization(string p) { int n = p.size(); int d = 1; for(int i = 0; i < n; i++) { if(p[i] == '#') d--; else if(isdigit(p[i])) { if(--d < 0) return false;  while(i < n && isdigit(p[i])) i++; d += 2;        } if(d < 0) return false; } return d == 0;}

参考资料:

https://www.hrwhisper.me/leetcode-verify-preorder-serialization-of-a-binary-tree/


举一反三

思考下,如果给出层序遍历的序列,该怎么做呢?


如果不去建树,在层序遍历中难以获得节点间的父子关系,不好用 ”消消乐“ 的方法去做。但在层序遍历中,每颗子树的头节点一定会先被遍历,因此仍然可以用 出度和入度 去维护。


个人感悟

其实,个人在做这道题的时候,一度觉得题面是 稍微有一点点问题的笔试题 判定合法二叉树

首先,一个带空节点的合法遍历序列是一定可以唯一确定一颗二叉树的(模拟建树过程就可以了)。


但题目中说,可以是非法的二叉树,那花样可就多了

我们来看这样两个例子:

     _1_                  _1_    /   \                /   \   2     #              2     #  / \  / \ #   #                      #   #

显然,第二个是非法二叉树,但是,这两颗二叉树的前序遍历序列都是 ”1, 2, #, #, #“。一个有可能非法序列是无法确定一颗二叉树的。


所以,个人觉得题面改成,”判断是否有可能是正确的二叉树的遍历序列“会更好一点。当然,这样就很容易想到用二叉树的属性去维护了。


--------------------------

ttt0h
分享干货满满的算法和数学知识。
3篇原创内容
Official Account