剑指offer39:平衡二叉树
1 题目描述
2 思路和方法
平衡二叉树,又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。https://blog.csdn.net/qq_43091156/article/details/88558966
从叶节点开始,依次往上求其子树高度,如果在某一子树上不满足要求,则一路返回,不再继续遍历。即,先依次遍历左子树,如果左子树是平衡二叉树,再依次遍历右子树。时间最坏O(n),空间O(n)。
3 C++核心代码
class Solution {
public:
// 返回值:树的深度
bool IsBalanced_Solution(TreeNode* pRoot){
if (pRoot == nullptr)
return true; // ko
return TreeDepth(pRoot)!=-;
}
// 返回值:
// -1:子树不平衡
// >0:子树深度
int TreeDepth(TreeNode* pRoot){
if (pRoot == nullptr)
return ;
int left = TreeDepth(pRoot->left);
if(left==-) //若左子树不满足平衡,则整个树已不是平衡二叉树了,直接返回,不处理右子树
return -;
int right = TreeDepth(pRoot->right);
if(right ==-)
return -;
if(left-right > || left - right <-)
return -;
return left>right ? left+:right+;
}
};
4 AVL平衡二叉树的代码
#include <iostream>
#include <algorithm> using namespace std; class AVLNode{
public:
int data;
int height;//结点的高度,叶子结点高度为1
AVLNode* lChild;
AVLNode* rChild;
public:
AVLNode(int data) :data(data), height(), lChild(), rChild(){}
}; class AVL{
public:
AVLNode* root;
public:
AVL(){
root = nullptr;
}
~AVL(){
delete root;
}
int height(AVLNode* root){
if (root){
return root->height;
}
return ;
}
//找到树中最大结点并将其返回
AVLNode* finMaxNode(AVLNode* root){
//一直往右找
if (root->rChild){
root = root->rChild;
}
return root;
}
//找到树中最小结点并将其返回
AVLNode* finMinNode(AVLNode* root){
//一直往左找
if (root->lChild){
root = root->lChild;
}
return root;
}
//以p为根结点右旋转,返回新的根结点
AVLNode* llRotate(AVLNode* p){
AVLNode* pleft = p->lChild;
p->lChild = pleft->rChild;
pleft->rChild = p;
//结点的高度由该节点的子树唯一决定,所以只有子树发生变化的结点才需要更新高度值
pleft->height = max(height(pleft->lChild), height(pleft->rChild)) + ;
p->height = max(height(p->lChild), height(p->rChild)) + ;
return pleft;
}
//左旋转
AVLNode* rrRotate(AVLNode* p){
AVLNode* pright = p->rChild;
p->rChild = pright->lChild;
pright->lChild = p;
pright->height = max(height(pright->lChild), height(pright->rChild)) + ;
p->height = max(height(p->lChild), height(p->rChild)) + ;
return pright;
}
//先左,再右
AVLNode* lrRotate(AVLNode* p){
AVLNode* pleft = rrRotate(p->lChild);
return llRotate(p);
}
//先右,再左
AVLNode* rlRotate(AVLNode* p){
AVLNode* pright = llRotate(p->rChild);
return rrRotate(p);
}
//插入新结点,保持平衡
void insert(int data, AVLNode*& root){
if (!root){
root = new AVLNode(data);
}
else{
if (data < root->data){
insert(data, root->lChild);
//插入新结点后,如果打破平衡,则需要动态调整
if (height(root->lChild) - height(root->rChild) == ){
if (data < root->lChild->data)
root = llRotate(root);
else
root = lrRotate(root);
}
}
else if (data > root->data){
insert(data, root->rChild);
//插入新结点后,如果打破平衡,则需要动态调整
if (height(root->rChild) - height(root->lChild) == ){
if (data > root->rChild->data)
root = rrRotate(root);
else
root = rlRotate(root);
}
}
else{
cout << "AVL中已存在该值:" << data << endl;
}
}
//平衡后,需要更新根结点的高度值
root->height = max(height(root->lChild), height(root->rChild)) + ;
}
//删除结点,保持平衡
void del(int data, AVLNode*& root){
if (data < root->data){
del(data, root->lChild);
//删除点之后,若AVL树失去平衡,则进行调整
if (height(root->rChild) - height(root->lChild) == ){
AVLNode* r = root->rChild;
if (height(r->lChild) > height(r->rChild))
root = rlRotate(root);
else
root = rrRotate(root);
}
}
else if (data > root->data){
del(data, root->rChild);
//删除点之后,若AVL树失去平衡,则进行调整
if (height(root->lChild) - height(root->rChild) == ){
AVLNode* l = root->lChild;
if (height(l->lChild) > height(l->rChild))
root = llRotate(root);
else
root = lrRotate(root);
}
}
else{
//此时root为要删除的点
if (root->lChild && root->rChild){
if (height(root->lChild) > height(root->rChild)){
// 如果root的左子树比右子树高;
// 则(01)找出root的左子树中的最大节点
// (02)将该最大节点的值赋值给root。
// (03)删除该最大节点。
// 这类似于用"root的左子树中最大节点"做"root"的替身;
// 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
AVLNode* maxNode = finMaxNode(root->lChild);
root->data = maxNode->data;
del(maxNode->data, root->lChild);
}
else{
AVLNode* minNode = finMinNode(root->rChild);
root->data = minNode->data;
del(minNode->data, root->rChild);
}
}
else{
if (root->lChild){
root->data = root->lChild->data;
root->lChild = nullptr;
}
else if (root->rChild){
root->data = root->rChild->data;
root->rChild = nullptr;
}
else{
root = nullptr;//参数是引用,所以此处修改了主函数中的root值
}
}
}
}
void inOrder(AVLNode* root){
if (root){
inOrder(root->lChild);
cout << root->data << endl;
inOrder(root->rChild);
}
}
}; int main(){
AVL tree;
tree.insert(, tree.root);
tree.insert(, tree.root);
tree.insert(, tree.root);
tree.insert(, tree.root);
tree.insert(, tree.root);
tree.insert(, tree.root);
tree.del(, tree.root);
tree.inOrder(tree.root); system("pause");
return ;
}
参考资料
https://blog.csdn.net/qq_39559641/article/details/83720734(代码很详细全面,写的很好,知识点讲解详细)
https://blog.csdn.net/zjwreal/article/details/88833908
https://blog.csdn.net/qq_43091156/article/details/88558966
https://blog.csdn.net/vaemusicsky/article/details/81607251(AVL平衡二叉树的代码)
剑指offer39:平衡二叉树的更多相关文章
- 剑指offer39 平衡二叉树
剑指上用了指针传递,这里用的引用传递 class Solution { public: bool IsBalanced_Solution(TreeNode* pRoot) { ; return IsB ...
- 剑指offer-39:平衡二叉树
题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 解题思路 在做这题是,我第一反应就是遍历两次二叉树.第一遍记录每个节点的深度,并将信息存入HashMap中,key = node,value ...
- 剑指Offer——平衡二叉树
题目描述: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 平衡二叉树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质 ...
- 剑指Offer-38.平衡二叉树(C++/Java)
题目: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 可以从根节点开始遍历每一个节点,求得节点左右子树的最大高度,判断是不是平衡二叉树.这样做的问题在于会重复遍历节点,造成不必要的浪费. 所 ...
- 用java刷剑指offer(平衡二叉树)
题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 牛客网链接 java代码 import java.lang.Math; public class Solution { public bool ...
- 剑指Offer39 数组中寻找和为sum的两个数字
/************************************************************************* > File Name: 39_TwoNum ...
- 剑指offer--39. 跳台阶
时间限制:1秒 空间限制:32768K 热度指数:375795 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). cla ...
- 剑指Offer-39.把数组排成最小的数(C++/Java)
题目: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 分析: 将数组 ...
- C++版 - 剑指offer 面试题39:判断平衡二叉树(LeetCode 110. Balanced Binary Tree) 题解
剑指offer 面试题39:判断平衡二叉树 提交网址: http://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId= ...
随机推荐
- python爬虫demo01
python爬虫demo01 1 import requests, json, time, sys 2 from bs4 import BeautifulSoup 3 from contextlib ...
- masm 编译贪吃蛇游戏
code: ;TITLE GAME4TH PAGE , STSEG SEGMENT DB DUP () STSEG ENDS ;----------------------------------- ...
- CF1030C
CF1030C 题意: 给你一个数字,问能否拆分成k段,使得每一段的每一位数字相加结果相等. 解法: 考虑数位DP. 暴力按位考虑每一位是否满足条件 CODE: #include<cstdio& ...
- Sketch教程
去年夏天开始用Sketch,觉得它放Dock里闪亮亮的很好看,当时笑称是男盆友送我的第一颗小钻石噗哈哈.所以那段时间几乎刷遍了所有关于 Sketch 的网站.文章.教程,之后又在自学设计,因为想和更多 ...
- Java并发指南7:JUC的核心类AQS详解
一行一行源码分析清楚AbstractQueuedSynchronizer 转自https://www.javadoop.com/post/AbstractQueuedSynchronizer#toc4 ...
- vs.net2017在编辑的wpf的xaml文件引用本程序集下的类提示“找不到”
local对应就是当前exe程序下的类,会提示“...命令空间...找不到...” 因为我调整过生成的,于是尝试调回来anyCPU 问题解决. 看了一下vs.net2017的所在目录"C:\ ...
- Vue插件编写、用法详解(附demo)
Vue插件编写.用法详解(附demo) 1.概述 简单来说,插件就是指对Vue的功能的增强或补充. 比如说,让你在每个单页面的组件里,都可以调用某个方法,或者共享使用某个变量,或者在某个方法之前执行一 ...
- GO 语言常用排序
1. 冒泡排序(bubble sort)的基本思想:比较相邻两个 元素的关键字值,如果反序,则交换 func BubbleSort(arr []int) { flag := false //外层控制行 ...
- idea使用Snyk对项目进行安全漏洞审核、修复
笔者今天早上打开idea,看到右侧插件栏有一个大狗头,不懂什么时候好奇心驱使安装了这个插件.按图索骥,打开插件. 打开狗,里面会出现好多英文,其中有一处蓝色标底,here 字样的,你点击进去, 用Go ...
- pycharm中指定ip和端口
pycharm中指定ip和端口 环境: 系统:win7 本机ip:192.168.0.100 1.建立工程请参照:https://www.cnblogs.com/effortsing/p/103945 ...