C++——AVL树

AVL树

本章思维导图:
在这里插入图片描述注:本章思维导图对应的.xmind.png文件都已同步导入至资源

1. AVL树的来历

我们之前学习过一种搜索结构——搜索二叉树,它可以以O(logN)的时间复杂度找到一个值,但是如果遇到某些极端情况(单边二叉树),查找的效率就会将为O(N)
在这里插入图片描述

为了解决这一问题,我们就要尽可能的避免类似单边二叉树的出现,也即要尽可能保证每个节点的左右子树的高度要尽可能相等,也就是说要将搜索二叉树优化为平衡搜索二叉树

在这种背景下,AVL树出现了:

  • AVL树是最早被发明的自平衡搜索二叉树
  • AVL树的发明者为G. M. Adelson-Velsky和E. M. Landis,因此被叫做AVL树

2. AVL树的性质与特点

在这里插入图片描述

上图就是一棵AVL树,AVL树具有如下的特点:

  1. AVL树是一棵搜索二叉树,因此右节点的键值大于根节点的;左节点的键值小于根节点的。并且每个节点的键值都不同

  2. 每个节点的左右子树都是一棵AVL树

  3. 每个节点的左右子树的高度差的绝对值不超过1

  4. 为了确保第3点,一般的对于每个节点都有一个平衡因子``bf(balance factor)`来记录该节点左右子树的高度差,如果bf的绝对值大于2,就要对该树进行调整

    • 在本篇博客中,规定bf = 右子树高度 - 左子树高度

可能有小伙伴会疑惑,为什么平衡的条件是左右子树的高度差的绝对值不超过1,为什么不能是0,做到完全平衡呢

我们可以考虑一些特殊情况,例如两个节点和四个节点的二叉树,无论如何他们都无法做到任意节点的左右子树的高度差为0:

在这里插入图片描述

因此AVL树只能保证尽可能的平衡,而不能做到绝对的平衡

2. 了解操作

注:本篇博客的AVL树统一采用KV(key_value)模型

2.1 AVL树节点类

AVL树节点类定义如下:

template<class K, class V>
struct AVLTreeNode
{
	typedef AVLTreeNode<K, V> Node;

	Node* _left = nullptr;
	Node* _right = nullptr;
	Node* _parent = nullptr;
	pair<K, V> _kv;
	int   _bf = 0;	//bf为平衡因子,即[右子树高度 - 左子树高度]

	AVLTreeNode(const pair<K, V>& kv)
		: _kv(kv)
	{}
};

这是一个三叉链链式结构:

  • _left:指向节点的左子树
  • _right:指向节点的右子树
  • _parent:指向节点的父节点
  • _kv:存储keyvalue
  • _bf:平衡因子,为右子树高度与左子树高度的差

2.2 insert 插入节点

2.2.1 找到插入位置

作为一棵搜索二叉树,AVL树的插入首先同样要找到一个插入位置

  • 如果新节点的键值大于当前节点,则去该节点的右子树寻找

  • 如果新节点的键值小于当前节点,则去该节点的左子树寻找

  • 如果新节点的键值等于当前节点,插入失败

  • 如果遍历到空,则该位置就是插入位置

bool insert(const pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        return true;
    }

    //找到插入位置
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
        parent = cur;
        if (cur->_kv.first > kv.first)
            cur = cur->_left;
        else if (cur->_kv.first < kv.first)
            cur = cur->_right;
        else
            return false;
    }

    //连接新节点
    cur = new Node(kv);
    if (parent->_kv.first > kv.first)
        parent->_left = cur;
    else
        parent->_right = cur;

    cur->_parent = parent;
    
    //……………………
}

2.2.2 更新平衡因子

在这里插入图片描述

一个新节点插入成功后,首先更新的应该是它的父节点:

  • 如果新节点为父节点的右,bf++
  • 如果新节点为父节点的左,bf– –

在这里插入图片描述

之后,对于更新后父节点bf的值,我们要分以下三种情况:

情况一:父节点bf == 0

首先我们应该清楚,AVL树规定左右子树的高度差不会超过1,因此在一个节点的bf只能能为0,1,-1

如果插入一个节点之后,父节点的bf为0,这就说明:

  • 父节点之前的bf一定为1或者-1
  • 新节点一定插入在较矮的子树

如果不明白,我们可以方向思考:

  • 如果父节点bf之前为0,那么插入节点后必然会使左右子树的一棵变高,从而使插入后的bf变为1或-1
  • 如果新节点插入在较高的子树,那么bf的绝对值(左右子树的高度差)并不会变小,而是会变大

因此,这种情况可以用下图总结:

在这里插入图片描述

可以看出,由于新节点插入在父节点较矮的子树,因此插入后并没有改变以父节点为根的树的高度,从而也就没有父节点的上级节点左右子树的高度差,例如:

在这里插入图片描述

因此,对于插入节点后父节点 bf == 0的情况,由于没有破坏整棵树的平衡,插入过程直接结束

情况二:父节点bf == ±1

经过和上面类似的分析,要使插入后父节点的bf == ±1,插入前,父节点的bf必须等于0

在这里插入图片描述

可以看出,如果插入后父节点的bf == ±1,那么以父节点为根节点的树的高度必然发生变化,从而会影响到父节点的上级节点

在这里插入图片描述

因此,针对插入后父节点 bf == ±1这种情况,我们需要通过循环来不断调节上级节点,直至平衡

情况三:父节点bf == ±2

同样的分析,如果插入后父节点bf == ±2,那么插入之前,父节点的bf一定满足:bf == ±1,并且新节点插入在较高的子树

在这里插入图片描述

针对这种情况,我们需要通过旋转来调节树的平衡
分为以下四种情况:


首先规定,链接新节点的节点称为cur,cur的父节点称为parent

2.2.2.1 左单旋

当插入节点后满足 cur->bf == 1 并且 parent->bf == 2时,需要使用左单旋来调节以parent为根的树的平衡:

在这里插入图片描述

旋转步骤为:

  • cur的左子树链接给parent的右子树
  • parent链接给cur的左子树

在这里插入图片描述

编写代码时需要注意以下几个细节问题:

  • 这是一个三叉链结构,除了左右子树的链接,还需要注意父节点的链接
  • 在将cur的左子树链接给parent时,需要注意cur的左子树为空的情况,否则可能会导致空节点的访问。例如:

在这里插入图片描述

  • parent可能就是整棵树的根节点,因此在旋转过后需要注意_root根节点的更新
  • 旋转过后需要注意更新平衡因子bfparent->bf = 0cur->bf = 0
  • 由于旋转过后新的根(即cur)的bf等于0,因此可以直接结束插入过程

代码实现:

void rotateL(Node* parent)
{
    Node* pparent = parent->_parent;
    Node* parentR = parent->_right;
    Node* parentRL = parentR->_left;

    parent->_right = parentRL;
    
    //考虑cur->_left为空的情况,防止空节点的访问
    if (parentRL)
        parentRL->_parent = parent;
    
    parentR->_left = parent;
    parentR->_parent = pparent;
    parent->_parent = parentR;
	
    //如果cur就是_root根节点,那么就要更新根节点
    if (pparent == nullptr)
        _root = parentR;
    else	//否则就要将新的根(即cur)链接给上级节点
    {
        if (parent == pparent->_left)
            pparent->_left = parentR;
        else
            pparent->_right = parentR;
    }
	
    //更新平衡因子
    parent->_bf = 0;
    parentR->_bf = 0;
}
2.2.2.2 右单旋

当插入节点后满足 cur->bf == -1 并且 parent->bf == -2时,需要使用右单旋来调节以parent为根的树的平衡:

在这里插入图片描述

旋转步骤为:

  • cur的右子树链接给parent的左子树
  • parent链接给cur的右子树

在这里插入图片描述

编写代码时需要注意以下几个细节问题:

和左单旋类似:

  • 这是一个三叉链结构,除了左右子树的链接,还需要注意父节点的链接
  • 在将cur的右子树链接给parent时,需要注意cur的右子树为空的情况,否则可能会导致空节点的访问。例如:

在这里插入图片描述

  • parent可能就是整棵树的根节点,因此在旋转过后需要注意_root根节点的更新
  • 旋转过后需要注意更新平衡因子bfparent->bf = 0cur->bf = 0
  • 由于旋转过后新的根(即cur)的bf等于0,因此可以直接结束插入过程

代码实现:

void rotateR(Node* parent)
{
    Node* pparent = parent->_parent;
    Node* parentL = parent->_left;
    Node* parentLR = parentL->_right;

    parent->_left = parentLR;
    
    //考虑cur->_right为空的情况
    if (parentLR)
        parentLR->_parent = parent;
    
    parentL->_right = parent;
    parent->_parent = parentL;
    parentL->_parent = pparent;
	
	//如果cur就是_root根节点,那么就要更新根节点
    if (pparent == nullptr)
        _root = parentL;
    else	//否则就要将新的根(即cur)链接给上级节点
    {
        if (parent == pparent->_left)
            pparent->_left = parentL;
        else
            pparent->_right = parentL;
    }
	
    //更新平衡因子
    parent->_bf = 0;
    parentL->_bf = 0;
}
2.2.2.3 右左双旋

当插入节点后,如果出现 cur->bf == -1 并且 parent->bf == 2,如图:

在这里插入图片描述

此时,如果我们仍然像情况cur->bf == 1 && parent->bf == 2一样,将parent进行左单旋:

在这里插入图片描述

可以发现,新的父节点cur的平衡因子仍为-2,仍没有达到平衡,可见,仅仅靠单旋并不能解决问题

针对这种情况,要将树调整平衡,需要用到双旋操作

为了便于分析,我们先将高度为h + 1的子树分为两部分,我们称这两部分的父节点为parentRL,如图:

在这里插入图片描述

这里还需要考虑一个特殊情况:h == 0,即这棵树只有两个节点parent和cur

在这里插入图片描述

双旋的步骤为:

  • 先对cur为根的树进行右单旋

在这里插入图片描述

  • 再对parent为根的树进行左单旋

在这里插入图片描述

旋转过后需要对树的节点的平衡因子进行调整,可以根据新节点插入在parentRL的左子树还是右子树进行分析:

  • 新节点插入parentRL的左子树,即parentRL->bf == -1

    在这里插入图片描述

    • 则旋转过后,parnet->bf = 0parentRL->bf = 0cur->bf = 1
  • 新节点插入parentRL的右子树,即parentRL->bf == 1

    在这里插入图片描述

    • 则旋转过后,parent->bf = -1parentRL = 0cur->bf = 0
  • 最后要注意上面提到的特殊情况parentRL->bf == 0

    在这里插入图片描述

    • 则旋转过后,parent->bf = 0parentRL = 0cur->bf = 0

实现代码:

void rotateRL(Node* parent)
{
    Node* parentR = parent->_right;	//即为图中提到的cur节点
    Node* parentRL = parentR->_left;	//即为图中提到的parentRL节点
    int bf = parentRL->_bf;		

    rotateR(parentR);	//先对cur为根的树进行右单旋
    rotateL(parent);	//再对parent为根的树进行左单旋
	
    //通过对旋转前parentRL平衡因子的大小区分插入情况
    if (bf == 0)
    {
        parent->_bf = 0;
        parentR->_bf = 0;
        parentRL->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = -1;
        parentR->_bf = 0;
        parentRL->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 0;
        parentR->_bf = 1;
        parentRL->_bf = 0;
    }
    else	//如果以上三张情况都不符合,说明发生错误
        assert(false);
}
2.2.2.4 左右双旋

当插入节点后,如果出现 cur->bf == 1 并且 parent->bf == -2,如图:

在这里插入图片描述

此时,如果我们仍然像情况cur->bf == 1 && parent->bf == 2一样,将parent进行右单旋:

在这里插入图片描述

可以发现,新的父节点cur的平衡因子仍为2,仍没有达到平衡,可见,仅仅靠单旋并不能解决问题

针对这种情况,要将树调整平衡,需要用到双旋操作

为了便于分析,我们先将高度为h + 1的子树分为两部分,我们称这两部分的父节点为parentLR,如图:

在这里插入图片描述

同样,这里还需要考虑一个特殊情况:h == 0,即这棵树只有两个节点parent和cur

在这里插入图片描述

双旋的步骤为:

  • 先对cur为根的树进行左单旋

在这里插入图片描述

  • 再对parent为根的树进行右单旋

在这里插入图片描述

旋转过后需要对树的节点的平衡因子进行调整,用相同的方法,根据新节点插入在parentLR的左子树还是右子树进行分析:

  • 新节点插入parentLR的左子树,即parentLR->bf == -1

    在这里插入图片描述

    • 则旋转过后,parnet->bf = 1parentLR->bf = 0cur->bf = 0
  • 新节点插入parentLR的右子树,即parentLR->bf == 1

    在这里插入图片描述

    • 则旋转过后,parent->bf = 0parentRL = 0cur->bf = -1
  • 最后要注意上面提到的特殊情况parentRL->bf == 0

    在这里插入图片描述

    • 则旋转过后,parent->bf = 0parentRL = 0cur->bf = 0

实现代码;

void rotateLR(Node* parent)
{
    Node* parentL = parent->_left;	//即图中的cur节点
    Node* parentLR = parentL->_right;	//即图中的parentLR节点
    int bf = parentLR->_bf;

    rotateL(parentL);	//先对cur为根的树进行左单旋
    rotateR(parent);	//再对parent为根的树进行右单旋
	
    //通过对旋转前parentLR平衡因子的大小区分插入情况
    if (bf == 1)
    {
        parent->_bf = 0;
        parentL->_bf = -1;
        parentLR->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        parentL->_bf = 0;
        parentLR->_bf = 0;
    }
    else if (bf == 0)
    {
        parent->_bf = 0;
        parentL->_bf = 0;
        parentLR->_bf = 0;
    }
    else	//如果以上三张情况都不符合,说明发生错误
        assert(false);
}

2.2.5 insert 完整代码

bool insert(const pair<K, V>& kv)
{	
    //树为空的情况单独讨论
    if (_root == nullptr)
    {
        _root = new Node(kv);
        return true;
    }

    //找到插入位置
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
        parent = cur;
        if (cur->_kv.first > kv.first)
            cur = cur->_left;
        else if (cur->_kv.first < kv.first)
            cur = cur->_right;
        else
            return false;
    }

    //连接新节点
    cur = new Node(kv);
    if (parent->_kv.first > kv.first)
        parent->_left = cur;
    else
        parent->_right = cur;

    cur->_parent = parent;

    //更新平衡因子,调节树的平衡
    while (parent)
    {
        if (cur == parent->_left)
            parent->_bf--;
        else
            parent->_bf++;

        if (parent->_bf == 0)
            break;
        else if (abs(parent->_bf) == 1)
        {
            cur = parent;
            parent = parent->_parent;
        }
        else if (abs(parent->_bf) == 2)
        {
            if (parent->_bf == 2 && cur->_bf == 1)
                rotateL(parent);
            else if (parent->_bf == -2 && cur->_bf == -1)
                rotateR(parent);
            else if (parent->_bf == 2 && cur->_bf == -1)
                rotateRL(parent);
            else
                rotateLR(parent);

            break;
        }
        else
        {
            assert(false);
        }
    }

    return true;
}

2.3 isbalance 检查一棵树是否为AVL树

尽管我们写出了一个AVL树的插入算法,并可以通过这个插入算法创建一棵AVL树,但是我们并不能确保我们写的insert算法是正确定,也即我们不能保证自己创建的树就是AVL树,因此有必要写一个函数isbalance()来验证这棵树是否平衡,并满足AVL树的条件。

isbalance()应该验证以下两个方面:

  • 任意节点的左右子树的高度差是否小于2
  • 任意节点的右子树与左子树的高度差是否等于该节点的平衡因子bf

可以很容易想到一个实现方法:

前序遍历每一个节点,求得该节点的左子树高度和右子树高度,验证上面的两个条件:

//求树的高度
int height(Node* root)
{
 if (root == nullptr)
     return 0;

 return 1 + max(height(root->_left), height(root->_right));
}

//前序遍历每个节点,验证:
//1. 左右子树的**高度差是否小于2
//2. 右子树与左子树的高度差是否等于该节点的平衡因子bf
bool _isbalance(Node* root)
{
 if (root == nullptr)
     return true;

 int leftHeight = height(root->_left);
 int rightHeight = height(root->_right);
 if (abs(root->_bf) >= 2)
 {
     cout << "高度异常" << root->_kv.first << endl;
     return false;
	}
 if (rightHeight - leftHeight != root->_bf)
 {
     cout << "平衡因子异常" << root->_kv.first << endl;
     return false;
 }

 return _isbalance(root->_left) && _isbalance(root->_right);
}

但是,上面这种方法是浪费了许多时间的——每一次继续向下遍历节点时,都要重新求一次该节点的左右子树的高度,但这显然是没必要的,我们应该像一种方法来省去这些不必要的计算

我们知道,树的高度等于左右子树高度的最大值 + 1,因此只要左右子树的高度确定了,这棵树的高度也就确定了

所以,我么可以转变我们的思路:先判断左右子树的平衡,再判断根即整棵树的平衡。也就是说,要利用后序的思想来解决这个问题

int height(Node* root)
{
 if (root == nullptr)
     return 0;

 return 1 + max(height(root->_left), height(root->_right));
}

//利用引用来记录高度
//height必须是引用,如果只是一个整形变量,那么当递归退出后,并不会改变原来的值
bool _isbalance(Node* root, int& height)
{
 if (root == nullptr)
     return true;

 //leftHeight表示左子树高度,rightHeight表示右子树高度
 int leftHeight = 0, rightHeight = 0;

 //如果左子树或右子树发生错误,则返回false
 if (!_isbalance(root->_left, leftHeight) || !_isbalance(root->_right, rightHeight))
     return false;

 if (abs(rightHeight - leftHeight) >= 2)
 {
     cout << "不平衡" << root->_kv.first << endl;
     return false;
 }
 if (rightHeight - leftHeight != root->_bf)
 {
     cout << "平衡因子异常" << root->_kv.first << endl;
     return false;
 }

 //走到这里,就说明左右子树已经验证完毕
 //求得树的高度
 height = max(leftHeight, rightHeight) + 1;

 return true;
}

3. 总结

看到这里,相信大家已经对AVL树的结构和特点有了较为深刻的了解,对于AVL树的其他操作,例如删除节点erase(),这里不再讲述,如有兴趣大家可以自行研究。

最后,我给出自己写的AVLTree类,如有错误,欢迎大家指出:

 #pragma once

#include <iostream>
#include <cmath>
#include <cassert>
#include <ctime>

using namespace std;

template<class K, class V>
struct AVLTreeNode
{
	typedef AVLTreeNode<K, V> Node;

	Node* _left = nullptr;
	Node* _right = nullptr;
	Node* _parent = nullptr;
	pair<K, V> _kv;
	int   _bf = 0;	//bf为平衡因子,即[右子树高度 - 左子树高度]

	AVLTreeNode(const pair<K, V>& kv)
		: _kv(kv)
	{}
};

template<class K, class V>
class AVLTree
{
public:
	typedef AVLTreeNode<K, V> Node;

	AVLTree() = default;

	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		//找到插入位置
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_kv.first > kv.first)
				cur = cur->_left;
			else if (cur->_kv.first < kv.first)
				cur = cur->_right;
			else
				return false;
		}

		//连接新节点
		cur = new Node(kv);
		if (parent->_kv.first > kv.first)
			parent->_left = cur;
		else
			parent->_right = cur;

		cur->_parent = parent;
		
		//更新平衡因子,调节搜索二叉树的平衡
		while (parent)
		{
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

			if (parent->_bf == 0)
				break;
			else if (abs(parent->_bf) == 1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				if (parent->_bf == 2 && cur->_bf == 1)
					rotateL(parent);
				else if (parent->_bf == -2 && cur->_bf == -1)
					rotateR(parent);
				else if (parent->_bf == 2 && cur->_bf == -1)
					rotateRL(parent);
				else
					rotateLR(parent);

				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

	void inOrder()
	{
		_inOrder(_root);
		cout << endl;
	}

	bool isbalance()
	{
		int i = 0;
		return _isbalance(_root, i);
	}

private:
	int height(Node* root)
	{
		if (root == nullptr)
			return 0;

		return 1 + max(height(root->_left), height(root->_right));
	}

	bool _isbalance(Node* root, int& height)
	{
		if (root == nullptr)
			return true;

		int leftHeight = 0, rightHeight = 0;
		if (!_isbalance(root->_left, leftHeight) || !_isbalance(root->_right, rightHeight))
			return false;

		if (abs(rightHeight - leftHeight) >= 2)
		{
			cout << "不平衡" << root->_kv.first << endl;
			return false;
		}
		if (rightHeight - leftHeight != root->_bf)
		{
			cout << "平衡因子异常" << root->_kv.first << endl;
			return false;
		}

		height = max(leftHeight, rightHeight) + 1;

		return true;
	}


	/*
	* 左单旋:
	* 将parent的右孩子的左孩子链接给parent的右
	* 再将parent链接给parent右孩子的左
	* 需要注意:parent的右孩子的左孩子为空的情况,parent为根节点的情况、parent->_parent的链接
	* 平衡因子的调节
	*/
	void rotateL(Node* parent)
	{
		Node* pparent = parent->_parent;
		Node* parentR = parent->_right;
		Node* parentRL = parentR->_left;

		parent->_right = parentRL;
		if (parentRL)
			parentRL->_parent = parent;
		parentR->_left = parent;
		parentR->_parent = pparent;
		parent->_parent = parentR;

		if (pparent == nullptr)
			_root = parentR;
		else
		{
			if (parent == pparent->_left)
				pparent->_left = parentR;
			else
				pparent->_right = parentR;
		}

		parent->_bf = 0;
		parentR->_bf = 0;
	}

	/*
	* 右单旋:
	* 将parent的左孩子的右孩子链接给parent的左
	* 再将parent链接给parent的左孩子的右
	*/
	void rotateR(Node* parent)
	{
		Node* pparent = parent->_parent;
		Node* parentL = parent->_left;
		Node* parentLR = parentL->_right;

		parent->_left = parentLR;
		if (parentLR)
			parentLR->_parent = parent;
		parentL->_right = parent;
		parent->_parent = parentL;
		parentL->_parent = pparent;

		if (pparent == nullptr)
			_root = parentL;
		else
		{
			if (parent == pparent->_left)
				pparent->_left = parentL;
			else
				pparent->_right = parentL;
		}

		parent->_bf = 0;
		parentL->_bf = 0;
	}

	/*
	* 右左双旋:
	* 先parent的右子树右旋,再parent左旋
	* 平衡因子的更新:根据parentRL的平衡因子进行判断
	*/
	void rotateRL(Node* parent)
	{
		Node* parentR = parent->_right;
		Node* parentRL = parentR->_left;
		int bf = parentRL->_bf;

		rotateR(parentR);
		rotateL(parent);

		if (bf == 0)
		{
			parent->_bf = 0;
			parentR->_bf = 0;
			parentRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			parentR->_bf = 0;
			parentRL->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			parentR->_bf = 1;
			parentRL->_bf = 0;
		}
		else
			assert(false);
	}

	/*
	* 左右双旋:
	* 先parent的左子树左旋,再parent右旋
	* 平衡因子的更新:根据parentLR的平衡因子进行判断
	*/
	void rotateLR(Node* parent)
	{
		Node* parentL = parent->_left;
		Node* parentLR = parentL->_right;
		int bf = parentLR->_bf;

		rotateL(parentL);
		rotateR(parent);

		if (bf == 1)
		{
			parent->_bf = 0;
			parentL->_bf = -1;
			parentLR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			parentL->_bf = 0;
			parentLR->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			parentL->_bf = 0;
			parentLR->_bf = 0;
		}
		else
			assert(false);
	}

	void _inOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_inOrder(root->_left);
		cout << root->_kv.first << ' ';
		_inOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};
  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forward♞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值