二叉树索引树
[TOC]
1. 结构介绍
二叉索引树,Binary Indexed Tree,又名树状数组,或者Fenwick Tree,因为本算法由Fenwick创造。
2. 问题定义
假设有这样一个问题:
给定一个数组array[0....n-1],实现两个函数:1)求取前i项和;2)更新第i项值。
其中一个比较简单的思路就是通过循环累计 [0, i-1]
求和(需要O(n)时间),根据下标更新值(需要O(1)时间)。
另一种解法是创建一个前n项和数组(即,第 i
项存储前 i
项的和),这样求和需要O(1)时间,更新需要O(n)时间。
而使用BIT数据结构,可以在O(Logn)时间内进行求和操作和更新操作。下面具体介绍一下。
3. 表现形式
BIT这种数据结构使用一般数组进行存储,每个节点存储 部分 输入数组的元素之和。BIT数组的元素个数与输入数组的元素个数一致,均为n。BIT数组的初始值均为0,通过更新操作获取输入数组部分元素之和,并存储在相应的位置上。
4. 更新操作
更新操作是指根据输入数组的元素来更新BIT数组的元素,进而根据BIT数组求前i项和。
update(index, val): Updates BIT for operation arr[index] += val
// Note that arr[] is not changed here. It changes
// only BI Tree for the already made change in arr[].
1) Initialize index as index+1.
2) Do following while index is smaller than or equal to n.
...a) Add value to BITree[index]
...b) Go to parent of BITree[index]. Parent can be obtained by removing
the last set bit from index, i.e., index = index + (index & (-index))
令BIT数组为:BIT[i] = Input[i-lowbit(i)+1] + Input[i-lowbit(i)+2] + ... + Input[i];
即从最左边的孩子,到自身的和,如BIT[12]= A9+A10+A11+A12
5. 求和操作
求和操作是指利用BIT数组求原来输入数组的前i项和。
getSum(index): Returns sum of arr[0..index]
// Returns sum of arr[0..index] using BITree[0..n]. It assumes that
// BITree[] is constructed for given array arr[0..n-1]
1) Initialize sum as 0 and index as index+1.
2) Do following while index is greater than 0.
...a) Add BITree[index] to sum
...b) Go to parent of BITree[index]. Parent can be obtained by removing
the last set bit from index, i.e., index = index - (index & (-index))
3) Return sum.
说明:
如果节点y是节点x的父节点,那么节点y可以由节点x通过移除Lowbit(x)获得。Lowbit(natural)为自然数(即1,2,3…n)的二进制形式中最右边出现1的值。即通过以下公式获得 $parent(i) = i - i & (-i)$。
节点y的子节点x(对BIT数组而言)存储从y(不包括y)到x(包括x)(对输入数组而言)的的元素之和。即BIT数组的值由输入数组两者之间下标的节点对应关系获得。
计算前缀和Sum(i)的计算:顺着节点i往左走,边走边“往上爬”,把经过的BIT[i]累加起来即可。

BIT工作原理
BIT工作原理得益于所有的正整数,均可以表示为多个2的幂次方之和。例如,19 = 16 + 2 + 1。BIT的每个节点都存储n个元素的总和,其中n是2的幂。例如,在上面的getSum()的第一个图中,前12个元素的和可以通过最后4个元素(从9到12) )加上8个元素的总和(从1到8)。 数字n的二进制表示中的设置位数为O(Logn)。 因此,我们遍历getSum()和update()操作中最多的O(Logn)节点。 构造的时间复杂度为O(nLogn),因为它为所有n个元素调用update()。
代码实现
void updateBIT(int BITree[], int n, int index, int val) {
index = index + 1;
while(index <= n) {
BITree[index] += val;
index += index & (-index);
}
}
int getSum(int BITree[], int index) {
int sum = 0;
index = index + 1;
while(index > 0) {
sum += BITree[index];
index -= index & (-index);
}
return sum;
}
int *conBIT(int arr[], int n) {
int *BITree = new int[n + 1];
for(int i = 0; i <= n; i++)
BITree[i] = 0;
for(int i = 0; i < n; i++)
updateBIT(BITree, n, i, arr[i]);
return BITree;
}
二叉搜索树
顾名思义,二叉搜索树是以一棵二叉树来组织的,其满足一下条件:x为二叉树的一个节点,如果y是x左子树的一个节点,那么y.Val <= x.Val
;如果y是x右子树的一个节点,则y.Val >= x.Val
根据二叉树的性质可得知,二叉搜索树的中序遍历为升序序列,相反如果交换左右子树的遍历顺序亦可得到降序序列。
// inorder伪代码
func inOrder(root *TreeNode) []int {
res := make([]int, 0)
if root != nil {
res = append(res, inOrder(root.Left)...)
res = append(res, root.Val)
res = append(res, inOrder(root.Right)...)
}
return res
}
func decOrder(root *TreeNode) []int {
res := make([]int, 0)
if root != nil {
res = append(res, decOrder(root.Right)...)
res = append(res, root.Val)
res = append(res, decOrder(root.Left)...)
}
return res
}
二叉搜索树的基本操作
二叉搜索树的基本操作包括查询,最小关键字元素,最大关键字元素,前继节点,后继节点,插入和删除,这些基本操作所花费的时间与二叉搜索树的高度成正比。
查询
如果二叉搜索树的高度为h,则查询的时间复杂度为O(h)。
// 查询伪代码
// 递归版
func searchBST(root *TreeNode, key int) *TreeNode {
if root == nil || root.Val == key { return root }
if root.Val < key {
return searchBST(root.Right, key)
}
return searchBST(root.Left, key)
}
// 迭代版
func searchBST(root *TreeNode, key int) *TreeNode {
for root != nil && root.Val != key {
if root.Val < key {
root = root.Right
} else {
root = root.Left
}
}
return root
}
最大关键字元素
如果二叉搜素树的高度为h,则最大关键字元素的操作时间复杂度为O(h)。
// 最大关键字元素的伪代码
func maximumBST(root *TreeNode) *TreeNode {
for root.Right != nil { root = root.Right }
return root
}
// 递归版
func maximumBST(root *TeeNode) *TreeNode {
if root.Right != nil {
return maximumBST(root.Right)
}
return root
}
最小关键字元素
如果二叉搜素树的高度为h,则最大关键字元素的操作时间复杂度为O(h)。
// 最小关键字元素的伪代码
func minimumBST(root *TreeNode) *TreeNode {
for root.Left != nil { root = root.Left }
return root
}
// 递归版
func minimumBST(root *TreeNode) *TreeNode {
if root.Left != nil {
return minimum(root.Left)
}
return root
}
前继节点
前继节点是指给定一个节点x,如果存在x的前继节点则返回前继节点,否则返回nil。
如果二叉搜素树的高度为h,则查找前继节点的操作时间复杂度为O(h)。
func predecessorBST(x *TreeNode) *TreeNode {
// x节点存在左子树,则返回左子树中最大的节点
if x.Left != nil {
return maximum(root.Left)
}
// 如果x节点没有左子树,则找到其父节点,且父节点的右子树包含x
y := x.Present
for y != nil && x == y.Left {
x = y
y = y.Present
}
return y
}
后继节点
后继节点是指给定一个节点x,如果存在x的后继节点则返回后继节点,否则返回nil。
如果二叉搜素树的高度为h,则查找后继节点的操作时间复杂度为O(h)。
// 后继节点的伪代码
func successorBST(x *TreeNode) *TreeNode {
// x节点存在右子树,则返回右子树中最小的节点
if x.Right != nil {
return minimum(root.Right)
}
// 如果x节点没有右子树,则找到其父节点,且父节点的左子树包含x
y := x.Present
for y != nil && x == y.Right {
x = y
y = y.Present
}
return y
}
插入
插入和删除会引起二叉搜索树所表示的动态集合的变化。一定要修改数据结构来反映这种变化,单修改要保持二叉搜索树的性质不变。
// x.Val = val, x.Right = x.Left = x.Present = nil
// 插入的伪代码
func insertBST(root, x *TreeNode) {
var y *TreeNode
for root != nil {
y = root
if x.Val < root.Val {
root = root.Left
} else {
root = root.Right
}
}
x.Present = y
if y == nil { // empty tree
root = x
} else if x.Val < y.Val {
y.Left = x
} else {
y.Right = x
}
}
// 没有父指针
// 递归版
func insertBST(root, x *TreeNode) *TreeNode {
if root == nil {
root = x
return root
}
if root.Val < x.Val {
return insertBST(root.Right)
}
return insertBST(root.Left)
}
// 迭代版
func insertBST(root, x *TreeNode) *TreeNode {
if root == nil {
root == x
return root
}
pre, head := root, root
for head != nil {
pre = head
if root.Val < x.Val {
head = head.Right
} else {
head = head.Left
}
}
if y.Val < x.Val {
y.Right = x
} else {
y.Left = x
}
return root
}
删除
从一棵二叉搜索树中删除一个节点x的整个策略分为三个情况,分别是:
1)如果x没有孩子节点,那么直接删除即可;
2)如果x只有一个孩子节点,那么将该孩子节点提升到x即可;
3)如果x有两个孩子,那么需要找到x的后继节点y(一定存在x的右子树中),让y占据x的位置,那么x原来的右子树部分称为y的右子树,x的左子树称为y的新的左子树。
func deleteBST(root *TreeNode, key int) *TreeNode{
if root == nil { return root }
if root.Val < key {
return deleteBST(root.Right, key)
} else if root.Val > key {
return deleteBST(root.Left, key)
} else {
// no child node
if root.Left == nil && root.Right == nil {
root = nil
} else if root.Right != nil {
root.Val = postNodeVal(root.Right)
root.Right = deleteBST(root.Right, root.Val)
} else {
root.Val = preNodeVal(root.Left)
root.Left = deleteBST(root.Left, root.Val)
}
}
return root
}
// find post node val in right
func postNodeVal(root *TreeNode) int {
for root.Left != nil { root = root.Left }
return root.Val
}
// find pre node val in left
func preNodeVal(root *TreeNode) int {
for root.Right != nil { root = root.Right }
return root.Val
}
二叉搜索树相关题目
108 将有序数组转换为二叉搜索树【M】
235 二叉搜素树的最近公共祖先
230 二叉搜索树中第K小的元素
450 删除二叉搜索树中的节点
1038 从二叉搜索树到更大和树【M】
1214 查找两棵二叉搜索树之和【M】
面试题 17.12 BiNode【E】
面试题54 二叉搜索树的第K大节点
108 将有序数组转换为二叉搜索树
题目要求:给定一个升序数据,返回一棵高度平衡二叉树。
思路分析:
二叉搜索树的定义:
1)若任意节点的左子树不为空,则左子树上所有的节点值均小于它的根节点值;
2)若任意节点的右子树不为空,则右子树上所有的节点值均大于它的根节点值;
3)任意节点的左,右子树均为二叉搜索树;
4)没有键值相等的点。
如何构造一棵树,可拆分成无数个这样的子问题:构造树的每个节点,以及节点之间的关系。对于每个节点来说,都需要:
1)选取节点;
2)构造该节点的左子树;
3)构造该节点的右子树。
算法一:对于升序序列,可以选择中间的节点作为根节点,然后递归调用。因为每个节点只遍历一遍,所以时间复杂度为O(n);因为递归调用,空间复杂度为O(log(n))。
//date 2020/02/20
// 算法一:递归版
func sortedArrayToBST(nums []int) *TreeNode {
if len(nums) == 0 { return nil }
// 选择根节点
mid := len(nums) >> 1
// 构造左子树和右子树
left := nums[:mid]
right := nums[mid+1:]
return &TreeNode{
Val: nums[mid],
Left: sortedArrayToBST(left),
Right: sortedArrayToBST(right),
}
}
230 二叉搜索树中第K小的元素
题目要求:给定一个二叉搜索树,返回其第K小的元素,你可以假设K总是有效的,即1<=k<=n。
算法:因为是二叉搜索树,先得到其中序遍历结果,然后直接返回k-1所在的元素。
// date 2020/01/11
func kthSmallest(root *TreeNode) int {
data := inOrder(root)
if len(data) < k {return 0}
return data[k-1]
}
func inOrder(root *TreeNode) []int {
if root == nil {return nil}
res := make([]int, 0)
if root.Left != nil { res = append(res, inOrder(root.Left)...) }
res = append(res, root.Val)
if root.Right != nil { res = append(res, inOrder(root.Right)...) }
}
235 二叉搜索树的最近公共祖先
题目要求:给定一个二叉搜素树和两个节点,返回其最近公共祖先。
算法:递归,并充分利用二叉搜索树的性质left.Val < root.Val < right.Val
。
// date 2020/02/18
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil || root == p || root == q {return root }
if p == nil { return q }
if q == nil { return p }
if root.Val > q.Val && root.Val > p.Val { return lowestCommonAncestor(root.Left) }
if root.Val < q.Val && root.Val < p.Val { return lowestCommonAncestor(root.Right) }
return root
}
450 删除二叉搜索树中的节点
题目要求:给定一个二叉搜索树和key,删除key节点,并返回树的根节点
算法一:二叉搜索树的中序遍历序列是个升序序列,则欲要删除的节点需要用其前驱节点或后驱节点来代替。
1)如果为叶子节点,则直接删除;
2)如果不是叶子节点且有右子树,则用后驱节点代替。后驱节点为右子树中的较低位置;
3)如果不是叶子节点且有左子树,则用前驱节点代替。前驱节点为左子树中的较高位置;
func deleteBST(root *TreeNode, key int) *TreeNode{
if root == nil { return root }
if root.Val < key {
return deleteBST(root.Right, key)
} else if root.Val > key {
return deleteBST(root.Left, key)
} else {
// no child node
if root.Left == nil && root.Right == nil {
root = nil
} else if root.Right != nil {
root.Val = postNodeVal(root.Right)
root.Right = deleteBST(root.Right, root.Val)
} else {
root.Val = preNodeVal(root.Left)
root.Left = deleteBST(root.Left, root.Val)
}
}
return root
}
// find post node val in right
func postNodeVal(root *TreeNode) int {
for root.Left != nil { root = root.Left }
return root.Val
}
// find pre node val in left
func preNodeVal(root *TreeNode) int {
for root.Right != nil { root = root.Right }
return root.Val
}
1038 从二叉搜索树到更大和树
题目要求:给出二叉搜索树的根节点,该二叉树的节点值各不相同,修改二叉树,使每个节点 node
的新值等于原树中大于或等于 node.val
的值之和
思路分析:
root.Val = root.Val + root.Right.Val
root.Left.Val = root.Left.Val + root.Val
首先得到二叉搜索树的中序遍历序列的逆序列,然后从序列的第一个开始,将累加值记录到当前树的节点上。
// date 2020/02/23
func bsttoGST(root *TreeNode) *TreeNode{
array := decOrder(root)
val := 0
for i := 0; i < len(array); i++ {
val += array[i]
setNewVal(root, array[i], val)
}
}
func decOrder(root *TreeNode) []int {
res := make([]int, 0)
if root != nil {
res = append(res, decOrder(root.Right)...)
res = append(res, root.Val)
res = append(res, decOrder(root.Left)...)
}
return res
}
func setNewVal(root *TreeNode, key, val int) {
if root != nil {
if root.Val == key {
root.Val = val
} else if root.Val > key {
setNewVal(root.Left, key, val)
} else {
setNewVal(root.Right, key, val)
}
}
}
面试题 BiNode
题目要求:将一颗二叉搜索树转换成单链表,Right指针指向下一个节点。
思路分析:
要求返回头节点,所以先转换left。
// date 2020/02/19
func convertBiNode(root *TreeNode) *TreeNode {
if root == nil { return nil }
// 左子树不为空,需要转换
if root.Left != nil {
// 得到左子树序列,并找到最后一个节点
left := convertBiNode(root.Left)
pre := left
for pre.Right != nil { pre = pre.Right }
// 将最后一个节点right指向root
pre.Right = root
root.Left = nil
// 得到右子树序列
root.Right = convertBiNode(root.Right)
return left
}
root.Right = convertBiNode(root.Right)
return root
}
面试题54 二叉搜索树的第K大节点
题目要求:给定一个二叉搜索树,找出其中第k大节点。
算法一:二叉搜索树的中序遍历为升序序列,按照Right->root->Left的顺序遍历可得到降序序列,然后返回第k节点。
// date 2020/02/18
func kthLargest(root *TreeNode, k int) int {
if root == nil || k < 0 { return -1 }
nums := decOrder(root)
if len(nums) < k {return -1}
return nums[k-1]
}
// right->root->left
func decOrder(root *TreeNode) []int {
if root == nil { return []int{} }
res := make([]int, 0)
if root.Right != nil {
res = append(res, decOrder(root.Right)...)
}
res = append(res, root.Val)
if root.Left != nil {
res = append(res, decOrder(root.Left)...)
}
return res
}
95 不同的二叉搜索树 II【中等】
题目要求:给定一个整数n,生成所有由1...n为节点所组成的二叉搜索树。
思路分析
题目看着很难,实际利用递归思想没有那么难。
从序列1..n
取出数字1
作为当前树的根节点,那么就有i-1个元素用来构造左子树,二另外的i+1..n个元素用来构造右子树。最后我们将得到G(i-1)棵不同的左子树,G(n-i)棵不同的右子树,其中G为卡特兰数。
这样就可以以i为根节点,和两个可能的左右子树序列,遍历一遍左右子树序列,将左右子树和根节点链接起来。
func generateTrees(n int) []*TreeNode {
if n == 0 { return nil }
return generateTreesWithNode(1, n)
}
func generateTreesWithNode(start, end int) []*TreeNode {
res := make([]*TreeNode, 0)
if start > end {
// 表示空树
res = append(res, nil)
return res
}
for i := start; i <= end; i++ {
left := generateTreesWithNode(start, i-1)
right := generateTreesWithNode(i+1, end)
for _, l := range left {
for _, r := range right {
res = append(res, &TreeNode{
Val: i,
Left: l,
Right: r,
})
}
}
}
return res
}
106 从中序和后序遍历序列构造二叉树【中等】
思路分析
利用根节点在中序遍历的位置
// 递归, 中序和后序
func buildTree(inorder, postorder []int) *TreeNode {
if len(postorder) == 0 {return nil}
n := len(postorder)
root = &TreeNode{
Val: postorder[n-1],
}
index := -1
for i := 0; i < len(inorder); i++ {
if inorder[i] == root.Val {
index = i
break
}
}
if index >= 0 && index < n {
root.Left = buildTree(inorder[:index], postorder[:index])
root.Right = buildTree(inorder[index+1:], postorder[index:n-1]))
}
return root
}
// 递归,前序和中序
func buildTree(preorder, inorder []int) *TreeNode {
if 0 == len(preorder) {return nil}
n := len(preorder)
root := &TreeNode{
Val: preorder[0],
}
index := -1
for i := 0; i < len(inorder); i++ {
if inorder[i] == root.Val{
index = i
break
}
}
if index >= 0 && index < n {
root.Left = buildTree(preorder[1:index+1], inorder[:index])
root.Right = buildTree(preorder[index+1:], inorder[index+1:])
}
return root
}
110 平衡二叉树【简单】
题目要求:给定一个二叉树,判断其是否为高度平衡二叉树。【一个二叉树的每个节点的左右两个子树的高度差的绝对值不超过1,视为高度平衡二叉树】
算法一:递归
// date 2020/02/18
func isBalanced(root *TreeNode) bool {
if root == nil { return true }
if !isBalanced(root.Left) || !isBalanced(root.Right) { return false }
if Math.Abs(float64(getHeight(root.Left)-getHeight(root.Right))) > float64(1) { return false }
return true
}
func getHeight(root *TreeNode) int {
if root == nil { return 0 }
if root.Left == nil && root.Right == nil { return 1 }
l, r := getHeight(root.Left), getHeight(root.Right)
if l > r { return l+1 }
return r+1
}
156 上下翻转二叉树【中等】
题目要求:给定一个二叉树,其中所有的右节点要么是具有兄弟节点(拥有相同父节点的左节点)的叶节点,要么为空,将此二叉树上下翻转并将它变成一棵树, 原来的右节点将转换成左叶节点。返回新的根。
算法分析:
// date 2020/02/25
func upsideDownBinaryTree(root *TreeNode) *TreeNode {
var parent, parent_right *TreeNode
for root != nil {
// 0.保存当前left, right
root_left := root.Left
root.Left = parent_right // 重新构建root,其left为上一论的right
parent_right := root.Right // 保存right
root.Right = parent // 重新构建root,其right为上一论的root
parent = root
root = root_left
}
return parent
}
199 二叉树的右视图【中等】
算法分析:层序遍历的最后一个节点值。
// date 2020/02/26
func rightSideView(root *TreeNode) []int {
res := make([]int, 0)
if root == nil { return res }
stack := make([]*TreeNode, 0)
stack = append(stack, root)
for len(stack) != 0 {
n := len(stack)
res = append(res, stack[0].Val)
for i := 0; i < n; i++ {
if stack[i].Right != nil { stack = append(stack, stack[i].Right) }
if stack[i].Left != nil { stack = append(stack, stack[i].Left) }
}
stack = stack[n:]
}
return res
}
236 二叉树的最近公共祖先【中等】
题目要求:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
思路分析:
// date 2020/03/29
// 递归
// 在左右子树中分别查找p,q结点
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil || root == p || root == q { return root }
if p == q { return p }
left := lowestCommonAncestor(root.Left, p, q)
right := lowestCommonAncestor(root.Right, p, q)
// 左右均不为空,表示p,q分别位于左右子树中
if left != nil && right != nil { return root }
// 左为空,表示p,q位于右子树,否则位于左子树
if left == nil { return right }
return left
}
257 二叉树的所有路径【简单】
题目要求:给定一个二叉树,返回所有从根节点到叶子节点的路径。
算法分析:
// date 2020/02/23
func binaryTreePaths(root *TreeNode) []string {
res := make([]string, 0)
if root == nil { return res }
if root.Left == nil && root.Right == nil {
res = append(res, fmt.Sprintf("%d", root.Val))
return res
} else if root.Left != nil {
for _, v := range binaryTreePaths(root.Left) {
res = append(res, fmt.Sprintf("%d->%s", root.Val, v))
}
} else if root.Right != nil {
for _, v := range binaryTreePaths(root.Right) {
res = append(res, fmt.Sprintf("%d->%s", root.Val, v))
}
}
return res
}
class MinStack {
public:
/** initialize your data structure here. */
stack<int> _stack;
int _min = INT_MAX;
MinStack() {
}
void push(int x) {
if(_min >= x){
if(!_stack.empty()){
_stack.push(_min);
}
_min = x;
}
_stack.push(x);
}
void pop() {
if(_stack.empty())
return;
if(_stack.size() == 1)
_min = INT_MAX;
else if(_min == _stack.top()){//下一个元素是下一个最小值
_stack.pop();
_min = _stack.top();
}
_stack.pop();
}
int top() {
return _stack.top();
}
int getMin() {
return _min;
}
};
270 最接近的二叉搜索树值【简单】
题目要求:给定一个不为空的二叉搜索树和一个目标值 target,请在该二叉搜索树中找到最接近目标值 target 的数值。
算法分析:使用层序遍历,广度优先搜索。
// date 2020/02/23
func closestValue(root *TreeNode, target float64) int {
if root == nil { return 0 }
var res int
cur := math.Inf(1)
stack := make([]*TreeNode, 0)
stack = append(stack, root)
for len(stack) != 0 {
n := len(stack)
for i := 0; i < n; i++ {
if math.Abs(float64(stack[i].Val) - target) < cur {
cur = math.Abs(float64(stack[i].Val) - target)
res = stack[i].Val
}
if stack[i].Left != nil { stack = append(stack, stack[i].Left) }
if stack[i].Right != nil { stack = append(stack, stack[i].Right) }
}
stack = stack[n:]
}
return res
}
538 把二叉树转换成累加树【简单】
算法:逆序的中序遍历,查找。
// date 2020/02/26
func convertBST(root *TreeNode) *TreeNode {
var sum int
decOrder(root, &sum)
return root
}
func decOrder(root *TreeNode, sum *int) {
if root == nil { return }
decOrder(root.Right, sum)
*sum += root.Val
root.Val = *sum
decOrder(root.Left, sum)
}
543 二叉树的直径【简单】
题目要求:给定一棵二叉树,返回其直径。
算法分析:左右子树深度和的最大值。
// date 2020/02/23
func diameterOfBinaryTree(root *TreeNode) int {
v1, _ := findDepth(root)
return v1
}
func findDepth(root *TreeNode) (int, int) {
if root == nil { return 0, 0 }
var v int
v1, l := findDepth(root.Left)
v2, r := findDepth(root.Right)
if v1 > v2 {
v = v1
} else {
v = v2
}
if l+r > v { v = l+r }
if l > r { return v, l+1}
return v, r+1
}
545 二叉树的边界【中等】
题目要求:给定一个二叉树,按逆时针返回其边界。
思路分析:分别求其左边界,右边界,和所有的叶子节点,用visited辅助去重,算法如下:
// date 2020/02/23
func boundaryOfBinaryTree(root *TreeNode) []int {
if root == nil { return []int{} }
res := make([]int, 0)
left, right := make([]*TreeNode, 0), make([]*TreeNode, 0)
visited := make(map[*TreeNode]int, 0)
res = append(res, root.Val)
visited[root] = 1
// find left node
pre := root.Left
for pre != nil {
left = append(left, pre)
visited[pre] = 1
if pre.Left != nil {
pre = pre.Left
} else {
pre = pre.Right
}
}
// find right node
pre = root.Right
for pre != nil {
right = append(right, pre)
visited[pre] = 1
if pre.Right != nil {
pre = pre.Right
} else {
pre = pre.Left
}
}
leafs := findLeafs(root)
// make res
for i := 0; i < len(left); i++ {
res = append(res, left[i].Val)
}
for i := 0; i < len(leafs); i++ {
if _, ok := visited[leafs[i]]; ok { continue }
res = append(res, ,leafs[i].Val)
}
for i := len(right) - 1; i >= 0; i-- {
res = append(res, right[i].Val)
}
return res
}
func findLeafs(root *TreeNode) []*TreeNode {
res := make([]*TreeNode, 0)
if root == nil { return res }
if root.Left == nil && root.Right == nil {
res = append(res, root)
return res
}
res = append(res, findLeafs(root.Left)...)
res = append(res, findLeafs(root.Right)...)
return res
}
563 二叉树的坡度【简单】
题目要求:给定一棵二叉树,返回其坡度。
算法一:根据定义,递归计算。
func findTilt(root *TreeNode) int {
if root == nil || root.Left == nil && root.Right == nil { return 0 }
left := sum(root.Left)
right := sum(root.Right)
var res int
if left > right {
res = left - right
} else {
res = right - left
}
return res + findTilt(root.Left) + findTilt(root.Right)
}
func sum(root *TreeNode) int {
if root == nil { return 0 }
return sum(root.Left) + sum(root.Right) + root.Val
}
617 合并二叉树【简单】
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
// date 2020/02/23
// 递归
func mergeTrees(t1, t2 *TreeNode) *TreeNode {
if t1 == nil { return t2 }
if t2 == nil { return t1 }
t1.Val += t2.Val
t1.Left = mergeTrees(t1.Left, t2.Left)
t1.Right = mergeTrees(t1.Right, t2.Right)
return t1
}
654 最大二叉树【中等】
题目要求:
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。
算法分析:
找到数组中的最大值,构建根节点,然后递归调用。
// date 2020/02/25
// 递归版
func constructMaximumBinaryTree(nums []int) *TreeNode {
if len(nums) == 0 { return nil }
if len(nums) == 1 { return &TreeNode{Val: nums[0]} }
p := 0
for i, v := range nums {
if v > nums[p] { p = i }
}
root := &TreeNode{Val: nums[p]}
root.Left = constructMaximumBinaryTree(nums[:p])
root.Right = constructMaximumBinaryTree(nums[p+1:])
return root
}
655 输出二叉树【中等】
题目要求:将二叉树输出到m*n的二维字符串数组中。
算法思路:先构建好res,然后逐层填充。
// date 2020/02/25
func printTree(root *TreeNode) [][]string {
depth := findDepth(root)
length := 1 << depth - 1
res := make([][]string, depth)
for i := 0; i < depth; i++ {
res[i] = make([]string, length)
}
fill(res, root, 0, 0, length)
}
func fill(res [][]string, t *TreeNode, i, l, r int) {
if t == nil { return }
res[i][(l+r)/2] = fmt.Sprintf("%d", root.Val)
fill(res, t.Left, i+1, l, (l+r)/2)
fill(res, t.Right, i+1, (l+r+1)/2, r)
}
func findDepth(root *TreeNode) int {
if root == nil { return 0 }
l, r := findDepth(root.Left), findDepth(root.Right)
if l > r { return l+1 }
return r+1
}
662 二叉树的最大宽度【中等】
题目要求:给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。 链接:https://leetcode-cn.com/problems/maximum-width-of-binary-tree
算法分析:使用栈stack逐层遍历,计算每一层的宽度,注意全空节点的时候要返回。时间复杂度O(n),空间复杂度O(logn)。
// date 2020/02/26
func widthOfBinaryTree(root *TreeNode) int {
res := 0
stack := make([]*TreeNode, 0)
stack = append(stack, root)
var n, i, j int
var isAllNil bool
for 0 != len(stack) {
i, j, n = 0, len(stack)-1, len(stack)
// 去掉每一层两头的空节点
for i < j && stack[i] == nil { i++ }
for i < j && stack[j] == nil { j-- }
// 计算结果
if j-i+1 > res { res = j-i+1 }
// 添加下一层
isAllNil = true
for ; i <= j; i++ {
if stack[i] != nil {
stack = append(stack, stack[i].Left, stack[i].Right)
isAllNil = false
} else {
stack = append(stack, nil, nil)
}
}
if isAllNil { break }
stack = stack[n:]
}
return res
}
669 修剪二叉搜索树【简单】
题目要求:给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
算法分析:
// date 2020/02/25
func trimBST(root *TreeNode, L, R int) *TreeNode {
if root == nil { return nil }
if root.Val < L { return trimBST(root.Right, L, R) }
if root.Val > R { return trimBST(root.Left, L, R) }
root.Left = trimBST(root.Left, L, R)
root.Right = trimBST(root.Right, L, R)
return root
}
703 数据流中第K大元素【简单】
解题思路:
0。使用数据构建一个容量为k的小顶堆。
1。如果小顶堆堆满后,向其增加元素,若大于堆顶元素,则重新建堆,否则掉弃。
2。堆顶元素即为第k大元素。
// date 2020/02/19
type KthLargest struct {
minHeap []int
size
}
func Constructor(k int, nums []int) KthLargest {
res := KthLargest{
minHeap: make([]int, k),
}
for _, v := range nums {
res.Add(v)
}
}
func (this *KthLargest) Add(val int) int {
if this.size < len(this.minHeap) {
this.size++
this.minHeap[this.size-1] = val
if this.size == len(this.minHeap) {
this.makeMinHeap()
}
} else if this.minHeap[0] < val {
this.minHeap[0] = val
this.minHeapify(0)
}
return this.minHeap[0]
}
func (this *KthLargest) makeMinHeap() {
// 为什么最后一个根节点是n >> 1 - 1
// 长度为n,最后一个叶子节点的索引是n-1; left = i >> 1 + 1; right = i >> 1 + 2
// 父节点是 n >> 1 - 1
for i := len(this.minHeap) >> 1 - 1; i >= 0; i-- {
this.minHeapify(i)
}
}
func (this *KthLargest) minHeapify(i int) {
if i > len(this.minHeap) {return}
temp, n := this.minHeap[i], len(this.minHeap)
left := i << 1 + 1
right := i << 1 + 2
for left < n {
right = left + 1
// 找到left, right中较小的那个
if right < n && this.minHeap[left] > this.minHeap[right] { left++ }
// left, right 均小于root否和小顶堆,跳出
if this.minHeap[left] >= this.minHeap[i] { break }
this.minHeap[i] = this.minHeap[left]
this.minHeap[left] = temp
// left发生变化,继续调整
i = left
left = i << 1 + 1
}
}
814 二叉树剪枝【中等】
题目要求:给定一个二叉树,每个节点的值要么是0,要么是1。返回移除了所有不包括1的子树的原二叉树。
算法分析:
使用containOne()函数判断节点以及节点的左右子树是否包含1。
// date 2020/02/25
func pruneTree(root *TreeNode) *TreeNode {
if containsOne(root) { return root }
return nil
}
func containsOne(root *TreeNode) bool {
if root == nil { return false }
l, r := containsOne(root.Left), containsOne(root.Right)
if !l { root.Left = nil }
if !r { root.Right = nil }
return root.Val == 1 || l || r
}
993 二叉树的堂兄弟节点【简单】
题目链接:https://leetcode-cn.com/problems/cousins-in-binary-tree/
题目要求:给定一个二叉树和两个节点元素值,判断两个节点值是否是堂兄弟节点。
堂兄弟节点的定义:两个节点的深度一样,但父节点不一样。
算法分析:
使用findFatherAndDepth函数递归找到每个节点的父节点和深度,并进行比较。
// date 2020/02/25
func isCousins(root *TreeNode, x, y int) bool {
xf, xd := findFatherAndDepth(root, x)
yf, yd := findFatherAndDepth(root, y)
return xd == yd && xf != yf
}
func findFatherAndDepth(root *TreeNode, x int) (*TreeNode, int) {
if root == nil || root.Val == x { return nil, 0 }
if root.Left != nil && root.Left.Val == x { return root, 1 }
if root.Right != nil && root.Right.Val == y { return root, 1 }
l, lv := findFatherAndDepth(root.Left, x)
r, rv := findFatherAndDepth(root.Right, x)
if l != nil { return l, lv+1 }
return r, rv+1
}
1104 二叉树寻路【中等】
题目要求:https://leetcode-cn.com/problems/path-in-zigzag-labelled-binary-tree/
算法分析:
// date 2020/02/25
func pathInZigZagTree(label int) []int {
res := make([]int, 0)
for label != 1 {
res = append(res, label)
label >>= 1
y := highBit(label)
lable = ^(label-y)+y
}
res = append(res, 1)
i, j := 0, len(res)-1
for i < j {
t := res[i]
res[i] = res[j]
res[j] = t
i++
j--
}
return res
}label = label ^(1 << (label.bit_length() - 1)) - 1
func bitLen(x int) int {
res := 0
for x > 0 {
x >>= 1
res++
}
return res
}
面试题27 二叉树的镜像【简单】
题目链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/
题目要求:给定一棵二叉树,返回其镜像二叉树。
// date 2020/02/23
// 递归算法
func mirrorTree(root *TreeNode) *TreeNode {
if root == nil { return root }
left := root.Left
root.Left = root.Right
root.Right = left
root.Left = mirrorTree(root.Left)
root.Right = mirrorTree(root.Right)
return root
}
最后更新于