LeetCode——919.完全二叉树插入器
完全二叉树是每一层(除最后一层外)都是完全填充(即,结点数达到最大)的,并且所有的结点都尽可能地集中在左侧。
设计一个用完全二叉树初始化的数据结构 CBTInserter,它支持以下几种操作:
CBTInserter(TreeNode root) 使用头结点为 root 的给定树初始化该数据结构;
CBTInserter.insert(int v) 将 TreeNode 插入到存在值为 node.val = v 的树中以使其保持完全二叉树的状态,并返回插入的 TreeNode 的父结点的值;
CBTInserter.get_root() 将返回树的头结点。
示例 1:
输入:inputs = ["CBTInserter","insert","get_root"], inputs = [[[1]],[2],[]]
输出:[null,1,[1,2]]
示例 2:
输入:inputs = ["CBTInserter","insert","insert","get_root"], inputs = [[[1,2,3,4,5,6]],[7],[8],[]]
输出:[null,3,4,[1,2,3,4,5,6,7,8]]
提示:
最初给定的树是完全二叉树,且包含 1 到 1000 个结点。
每个测试用例最多调用 CBTInserter.insert 操作 10000 次。
给定结点或插入结点的每个值都在 0 到 5000 之间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/complete-binary-tree-inserter
一
首先需要搞清楚的是完全二叉树的定义,即对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,换句话说,完全二叉树从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。由于插入操作要找到最后一层的第一个空缺的位置,所以很自然的就想到了使用层序遍历的方法,由于插入函数返回的是插入位置的父结点,所以在层序遍历的时候,只要遇到某个结点的左子结点或者右子结点不存在,则跳出循环,则这个残缺的父结点刚好就在队列的首位置。那么在插入函数时,只要取出这个残缺的父结点,判断若其左子结点不存在,说明新的结点要连接在左子结点上,否则将新的结点连接在右子结点上,并把此时的左右子结点都存入队列中,并将之前的队首元素移除队列即可,参见代码如下:
解法一:
C++
class CBTInserter {
public:
CBTInserter(TreeNode* root) {
tree_root = root;
q.push(root);
while (!q.empty()) {
auto t = q.front();
if (!t->left || !t->right) break;
q.push(t->left);
q.push(t->right);
q.pop();
}
}
int insert(int v) {
TreeNode *node = new TreeNode(v);
auto t = q.front();
if (!t->left) t->left = node;
else {
t->right = node;
q.push(t->left);
q.push(t->right);
q.pop();
}
return t->val;
}
TreeNode* get_root() {
return tree_root;
}
private:
TreeNode *tree_root;
queue<TreeNode*> q;
};
java
class CBTInserter {
TreeNode root;
Queue<TreeNode> q;
public CBTInserter(TreeNode root) {
this.root = root;
q = new LinkedList();
q.offer(root);
while(!q.isEmpty()){
TreeNode t = q.peek();
if(t.left == null || t.right == null) break;
q.offer(t.left);
q.offer(t.right);
q.poll();
}
}
public int insert(int v) {
TreeNode node = new TreeNode(v);
TreeNode t = q.peek();
if(t.left == null) t.left =node;
else {
t.right = node;
q.offer(t.left);
q.offer(t.right);
q.poll();
}
return t.val;
}
public TreeNode get_root() {
return root;
}
}
下面这种解法缩短了建树的时间,但是极大的增加了插入函数的运行时间,因为每插入一个结点,都要从头开始再遍历一次,并不是很高效,可以当作一种发散思维吧,参见代码如下:
解法二:
class CBTInserter {
public:
CBTInserter(TreeNode* root) {
tree_root = root;
}
int insert(int v) {
queue<TreeNode*> q{{tree_root}};
TreeNode *node = new TreeNode(v);
while (!q.empty()) {
auto t = q.front(); q.pop();
if (t->left) q.push(t->left);
else {
t->left = node;
return t->val;
}
if (t->right) q.push(t->right);
else {
t->right = node;
return t->val;
}
}
return 0;
}
TreeNode* get_root() {
return tree_root;
}
private:
TreeNode *tree_root;
};
再来看一种不使用队列的解法,因为队列总是要遍历,比较麻烦,如果使用数组来按层序遍历的顺序保存这个完全二叉树的结点,将会变得十分的简单。而且有个最大的好处是,可以直接通过坐标定位到其父结点的位置,通过 (i-1)/2 来找到父结点,这样的话就完美的解决了插入函数要求返回父结点的要求,而且通过判断当前完整二叉树结点个数的奇偶,可以得知最后一个结点是在左子结点上还是右子结点上,这样就可以直接将新加入的结点连到到父结点的正确的子结点位置,参见代码如下:
解法三:
class CBTInserter {
public:
CBTInserter(TreeNode* root) {
tree.push_back(root);
for (int i = 0; i < tree.size(); ++i) {
if (tree[i]->left) tree.push_back(tree[i]->left);
if (tree[i]->right) tree.push_back(tree[i]->right);
}
}
int insert(int v) {
TreeNode *node = new TreeNode(v);
int n = tree.size();
tree.push_back(node);
if (n % 2 == 1) tree[(n - 1) / 2]->left = node;
else tree[(n - 1) / 2]->right = node;
return tree[(n - 1) / 2]->val;
}
TreeNode* get_root() {
return tree[0];
}
private:
vector<TreeNode*> tree;
};
二
方法 1:双端队列
想法
将所有节点编号,按照从上到下从左到右的顺序。
在每个插入步骤中,我们希望插入到一个编号最小的节点(这样有 0 或者 1 个孩子)。
通过维护一个 deque (双端队列),保存这些节点的编号,我们可以解决这个问题。插入一个节点之后,将成为最高编号的节点,并且没有孩子,所以插入到队列的后端。为了找到最小数字的节点,我们从队列前端弹出元素。
算法
首先,通过广度优先搜索将 deque 中插入含有 0 个或者 1 个孩子的节点编号。
然后插入节点,父亲是 deque 的第一个元素,我们将新节点加入我们的 deque。
java
class CBTInserter {
TreeNode root;
Deque<TreeNode> deque;
public CBTInserter(TreeNode root) {
this.root = root;
deque = new LinkedList();
Queue<TreeNode> queue = new LinkedList();
queue.offer(root);
// BFS to populate deque
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node.left == null || node.right == null)
deque.offerLast(node);
if (node.left != null)
queue.offer(node.left);
if (node.right != null)
queue.offer(node.right);
}
}
public int insert(int v) {
TreeNode node = deque.peekFirst();
deque.offerLast(new TreeNode(v));
if (node.left == null)
node.left = deque.peekLast();
else {
node.right = deque.peekLast();
deque.pollFirst();
}
return node.val;
}
public TreeNode get_root() {
return root;
}
}
Python
class CBTInserter(object):
def __init__(self, root):
self.deque = collections.deque()
self.root = root
q = collections.deque([root])
while q:
node = q.popleft()
if not node.left or not node.right:
self.deque.append(node)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
def insert(self, v):
node = self.deque[0]
self.deque.append(TreeNode(v))
if not node.left:
node.left = self.deque[-1]
else:
node.right = self.deque[-1]
self.deque.popleft()
return node.val
def get_root(self):
return self.root
复杂度分析
时间复杂度:预处理 O(N),其中 N 是树上节点编号。每个插入步骤是 O(1)。
空间复杂度:O(N_cur),其中当前插入操作树的大小为 N_cur
三
完全二叉树的特性,节点位置与数组下标映射关系:
若节点位置位k, 则lchild位置位2k, rchild位置位2k+1; (从1开始计算)
java
class CBTInserter {
int size;
TreeNode[] A;
void traverse(TreeNode root,int k){
if(root==null)return;
A[k]=root;
if(k>size){
size=k;
}
traverse(root.left,k<<1);
traverse(root.right,(k<<1) | 1);
}
public CBTInserter(TreeNode root) {
A=new TreeNode[11004];
size=0;
traverse(root,1);
}
public int insert(int v) {
A[++size]=new TreeNode(v);
if(size%2 == 0){
A[size/2].left=A[size];
}
else{
A[size/2].right=A[size];
}
return A[size/2].val;
}
public TreeNode get_root() {
return A[1];
}
}
LeetCode——919.完全二叉树插入器的更多相关文章
- [LeetCode] 919. Complete Binary Tree Inserter 完全二叉树插入器
A complete binary tree is a binary tree in which every level, except possibly the last, is completel ...
- [Swift]LeetCode919. 完全二叉树插入器 | Complete Binary Tree Inserter
A complete binary tree is a binary tree in which every level, except possibly the last, is completel ...
- leetcode_919. Complete Binary Tree Inserter_完全二叉树插入
https://leetcode.com/problems/complete-binary-tree-inserter/ 给出树节点的定义和完全二叉树插入器类的定义,为这个类补全功能.完全二叉树的定义 ...
- [LeetCode] Insert Interval 插入区间
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessa ...
- LeetCode 1051. 高度检查器(Height Checker) 28
1051. 高度检查器 1051. Height Checker 题目描述 学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列. 请你返回至少有多少个学生没有站在正确位置数量.该人数指的是 ...
- LeetCode 222.完全二叉树的节点个数(C++)
给出一个完全二叉树,求出该树的节点个数. 说明: 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置.若最底 ...
- Leetcode 661.图片平滑器
图片平滑器 包含整数的二维矩阵 M 表示一个图片的灰度.你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个 ...
- Leetcode 222.完全二叉树的节点个数
完全二叉树的节点个数 给出一个完全二叉树,求出该树的节点个数. 说明: 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最 ...
- LeetCode 919. Complete Binary Tree Inserter
原题链接在这里:https://leetcode.com/problems/complete-binary-tree-inserter/ 题目: A complete binary tree is a ...
随机推荐
- Python 官方推荐的一款打包工具
译者:Jiong 链接: https://robots.thoughtbot.com/how-to-manage-your-python-projects-with-pipenv 在thoughtbo ...
- 二十七、CI框架之自己写分页类并加载(写分页还是有难度,搞了一整天)
一.我们写好自己的分页代码,防止library目录中,带构造函数 二.在模型中,添加2个函数,一个是查询数据的条数,第二个是取出数据库中的数据 三.在控制中,写入相应的代码,如下: 四.在界面中,写入 ...
- Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX. 出现这种异常,我需要自 ...
- 如何搞定Critical Thinking写作?
受中国传统教育模式与国外一流大学之间的差异的影响,在海外留学的学子们常常会在新的学习生活中面临许多难题,Critical Thinking就是其中之一.国内的教育方法常常以灌输式的教育模式为主,忽略了 ...
- 快速幂(51Nod1046 A^B Mod C)
快速幂也是比较常用的,原理在下面用代码解释,我们先看题. 51Nod1046 A^B Mod C 给出3个正整数A B C,求A^B Mod C. 例如,3 5 8,3^5 Mod 8 = 3. In ...
- 使用super调用被子类覆盖的父类方法
1.没有super方法 /* * 子类方法覆盖父类方法,用super方法可以调用父类被覆盖的方法 */ class fruit{ public fruit() { System.out.println ...
- android studio 黑屏问题
AVD配置不对,打开AVD从新配置
- django 过滤器-查询集-比较运算符-FQ对象-mysql的命令窗口
""" 返回查询集的方法称为过滤器 all() 返回查询集中所有数据 filter() 返回符合条件的数据 一.filter(键=值) 二.filter(键=值,键=值) ...
- 设计模式讲解3:ChainOfResponsibility模式源码
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 责任链模式,和普通的函数逐层调用栈形成的逻辑链条不通,责任链会落实到某一个具体实施者完成该责任,而普通函数 ...
- 201612-2 工资计算 Java
思路: 税+税后所得A=税前工资S. 因为工资是整百的数,每次减100来判断.好理解但是超时. import java.util.Scanner; //只有90分,超时了 public class M ...