红黑树代码(QMap)解读

最近结合下面的文章研究了下红黑树理论:
https://blog.youkuaiyun.com/zzy520comzzy/article/details/92688831
同时,结合理论读懂了qmap的实现源码,补充代码的注释,做个备忘。

/*
* 红黑树的规则:
* 1. 所有节点非黑即白;
* 2. 根节点为黑色节点;
* 3. 红色节点的子节点只能为黑色节点;
* 4. 从任一节点到各叶节点的黑色节点数相同;
* 5. 每个叶子节点是黑色;

* 推论:1) 黑色节点若存在父节点,则其一定有兄弟节点,否则违背规则4;
*      2) 若节点为红色节点则必定存在父节点,因为根节点为黑色;
*      3) 红色节点的所有子节点都是黑色,即不存在红色子节点。
*/

const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, 0, 0 }, 0 };

const QMapNodeBase *QMapNodeBase::nextNode() const
{
    const QMapNodeBase *n = this;
	// 找大于当前节点的最小节点,即右子树的最左侧节点
    if (n->right) {
        n = n->right;
        while (n->left)
            n = n->left;

	// 找所属左子树的根节点,找不到则空节点
    } else {
        const QMapNodeBase *y = n->parent();
        while (y && n == y->right) {
            n = y;
            y = n->parent();
        }
        n = y;
    }
    return n;
}

const QMapNodeBase *QMapNodeBase::previousNode() const
{
    const QMapNodeBase *n = this;

	// 找到小于当前节点的最大节点,即左子树的最右侧节点
    if (n->left) {
        n = n->left;
        while (n->right)
            n = n->right;

	// 找所属右子树的根节点,不存在则返回空
    } else {
        const QMapNodeBase *y = n->parent();
        while (y && n == y->left) {
            n = y;
            y = n->parent();
        }
        n = y;
    }
    return n;
}

// x为旋转的中心节点,其右侧节点y取代x的位置
void QMapDataBase::rotateLeft(QMapNodeBase *x)
{
	// header根节点的容器,header.left为根节点
    QMapNodeBase *&root = header.left;

	// 找右子节点
    QMapNodeBase *y = x->right; 
	// 将右子节点指派给x作右节点
    x->right = y->left;
	// 若节点非空重新指派父节点
    if (y->left != 0)
        y->left->setParent(x);
	// 将y的父节点关联x的父节点
    y->setParent(x->parent());
	// 若x为根节点,则替换y为新的根节点
    if (x == root)
        root = y;
	// 非根节点,则必然存在父节点,将y与父节点左或者右绑定
    else if (x == x->parent()->left)
        x->parent()->left = y;
    else
        x->parent()->right = y;

	// x移至y的左侧
	y->left = x;
    x->setParent(y);
}

//  右旋转类似左旋转
void QMapDataBase::rotateRight(QMapNodeBase *x)
{
    QMapNodeBase *&root = header.left;
    QMapNodeBase *y = x->left;
    x->left = y->right;
    if (y->right != 0)
        y->right->setParent(x);
    y->setParent(x->parent());
    if (x == root)
        root = y;
    else if (x == x->parent()->right)
        x->parent()->right = y;
    else
        x->parent()->left = y;
    y->right = x;
    x->setParent(y);
}

//I. 新增节点x再平衡的处理
void QMapDataBase::rebalance(QMapNodeBase *x)
{
    QMapNodeBase *&root = header.left;
	// 新增节点置为红色
    x->setColor(QMapNodeBase::Red); 

	// 父节点为红色则调整否则违反规则3,要做调整
    while (x != root && x->parent()->color() == QMapNodeBase::Red) { 
		//1. 父节点为左侧节点
        if (x->parent() == x->parent()->parent()->left) {
			// 父节点为红色,必定存在祖父节点
            QMapNodeBase *y = x->parent()->parent()->right;
			//1.1 叔节点是红色(推导:叔节点的子节点均为黑色,即不存在红色子节点)
            if (y && y->color() == QMapNodeBase::Red) {
				// 叔和父节点置为黑色,祖父节点置为红色,由父节点去做平衡处理
                x->parent()->setColor(QMapNodeBase::Black);
                y->setColor(QMapNodeBase::Black);
                x->parent()->parent()->setColor(QMapNodeBase::Red);
                x = x->parent()->parent();
			//1.2 叔节点为空或黑色
            } else {
				//1.2.1 新插入节点在右侧分支,则先左旋,转换为1.2.2的场景
                if (x == x->parent()->right) {
                    x = x->parent();
                    rotateLeft(x);
                }
				//1.2.2 新插入节点在左侧分支,即与父节点在同侧,直接右旋转
                x->parent()->setColor(QMapNodeBase::Black);
                x->parent()->parent()->setColor(QMapNodeBase::Red);
                rotateRight (x->parent()->parent());
            }

		//2. 父节点为右侧节点,处理逻辑与左侧分支相似
        } else {
            QMapNodeBase *y = x->parent()->parent()->left;
            if (y && y->color() == QMapNodeBase::Red) {
                x->parent()->setColor(QMapNodeBase::Black);
                y->setColor(QMapNodeBase::Black);
                x->parent()->parent()->setColor(QMapNodeBase::Red);
                x = x->parent()->parent();
            } else {
                if (x == x->parent()->left) {
                    x = x->parent();
                    rotateRight(x);
                }
                x->parent()->setColor(QMapNodeBase::Black);
                x->parent()->parent()->setColor(QMapNodeBase::Red);
                rotateLeft(x->parent()->parent());
            }
        }
    }
    root->setColor(QMapNodeBase::Black);
}

//II. 删除节点再平衡的处理
// 靶节点--z,其位置被“代删除”位置占用,节点对象(QMapNodeBase)被移出树结构
// “代删除”节点--y,顶替靶节点的位置,节点对象仍在树结构中,其源位置留空。所有的平衡操作针对该源位置
void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
{
    QMapNodeBase *&root = header.left;
    QMapNodeBase *y = z;    // y-“代删除”节点
    QMapNodeBase *x;        // x-补位“代删除”位置的节点
    QMapNodeBase *x_parent; // x_parent-“代删除”的父节点,同时也是x补位删除位后的父节点

	//1. 查找“代删除”节点和补位删除位置的节点
	//1.1 左侧节点为空,替代节点为其唯一的右侧节点
	//推导:1) “代删除”节点与删除节点的相同,即y == z
	//     2) y的一侧节点为空,则其另一侧节点至多有一个红色节点。
    if (y->left == 0) {
        x = y->right;

		// 若当前删除的节点为最左侧节点,则重新计算和更新最左侧节点
        if (y == mostLeftNode) {
			//y删除后,该右侧节点则为替代删除节点后的最左侧节点
            if (x)
                mostLeftNode = x; // It cannot have (left) children due the red black invariant.
			//右侧节点为空,则y为叶子节点,删除后的最左侧节点为其父节点
            else
                mostLeftNode = y->parent();
        }

	//1.2 左侧节点非空
    } else {
		// 1.2.1 右侧节点为空,则替代节点为其左侧子节点
        if (y->right == 0) {
            x = y->left;
        } else {
		// 1.2.2 右侧节点非空,则找右子树的最左侧节点为"代删除"节点
            y = y->right;
            while (y->left != 0)
                y = y->left;

			//顶替节点为“代删除”节点的右侧节点
            x = y->right;
        }
    }

	// 2. 用“代删除”节点补位到删除节点的位置
	// 2.1 顶替节点与删除节点不同
	// 推导:“代删除”节点的左子节点一定为空,见“代删除”节点的查找过程
    if (y != z) {
		// z->left为空,则y恒等于z。逆否命题:y不等z,则z->left一定不为空

		//将“代删除”节点代换到靶节点:“代删除”节点左子节点为空,所以先接管靶节点左侧子节点
        z->left->setParent(y);
        y->left = z->left;

		// y != z->right推导:1) y一定是其父节点的左侧节点;2) y一定无左子节点
        if (y != z->right) {
            x_parent = y->parent();

			// a. 将x补位到“代删除”节点,剥离“代删除”节点,更新父子关系
            if (x)
                x->setParent(y->parent());
            y->parent()->left = x;

			// b. 将“代删除”节点代换到靶节点:接管右侧子节点,此时靶节点变为空闲节点
            y->right = z->right;
            z->right->setParent(y);

		// y==z->right,则相当于“待删除”节点的右侧子节点整体上移:右子节点保持不变,无x的补位过程
        } else {
			// “代删除”节点的父节点为靶节点,此时置y为父节点
            x_parent = y;
        }

		// 将补位的“待删除”节点与靶节点的父节点简建立关联
		// a. 父对子
        if (root == z)
            root = y;
        else if (z->parent()->left == z)
            z->parent()->left = y;
        else
            z->parent()->right = y;
		// b. 子对父
        y->setParent(z->parent());

        // Swap the colors(交换颜色)
		// 推导:此时z保存的是y的原始颜色,该颜色对后续的平衡处理有影响
        QMapNodeBase::Color c = y->color();
        y->setColor(z->color());
        z->setColor(c);
        y = z;

	// 2.2 顶替节点与删除节点相同
    } else {
		// 比较简单,就是单侧节点的上移,更新父子关系
        x_parent = y->parent();
        if (x)
            x->setParent(y->parent());
        if (root == z)
            root = x;
        else if (z->parent()->left == z)
            z->parent()->left = x;
        else
            z->parent()->right = x;
    }

	// 3. “代删除”节点为黑色节点,需要做平衡处理:判断“代删除”节点的兄弟节点的颜色状态,做换色或者旋转处理
    if (y->color() != QMapNodeBase::Red) {
		//3.1 补位节点为黑色
        while (x != root && (x == 0 || x->color() == QMapNodeBase::Black)) {

			//3.1.1 “代删除” 节点的父节点为左侧节点
            if (x == x_parent->left) {
                QMapNodeBase *w = x_parent->right;

				//a. 场景1:右侧节点为红色,则其子节点必为黑色,左侧旋转,交互颜色后,变为场景2
                if (w->color() == QMapNodeBase::Red) {
                    w->setColor(QMapNodeBase::Black);
                    x_parent->setColor(QMapNodeBase::Red);
                    rotateLeft(x_parent);
                    w = x_parent->right;
                }

				//b. 场景2:右侧兄弟节点的左右子节点均为黑黑色,将右兄弟节点置为红色,树的高度整体减一,由父节点去做平衡调整
                if ((w->left == 0 || w->left->color() == QMapNodeBase::Black) &&
                    (w->right == 0 || w->right->color() == QMapNodeBase::Black)) {
                    w->setColor(QMapNodeBase::Red);
                    x = x_parent;
                    x_parent = x_parent->parent();
					//!!!该处无break,由父节点继续做判断调整

				//c. 场景3:右侧兄弟节点的左、右子节之一点为红色
                } else {
					//场景3-1:左侧节点为红色
                    if (w->right == 0 || w->right->color() == QMapNodeBase::Black) {

						// 交换颜色,并以w为中心做右旋,转换为场景3-2
                        if (w->left)
                            w->left->setColor(QMapNodeBase::Black);
                        w->setColor(QMapNodeBase::Red);
                        rotateRight(w);
                        w = x_parent->right;
                    }

					//场景3-2:右侧节点为红色,交换颜色并左旋,完成平衡
                    w->setColor(x_parent->color());
                    x_parent->setColor(QMapNodeBase::Black);
                    if (w->right)
                        w->right->setColor(QMapNodeBase::Black);
                    rotateLeft(x_parent);
                    break;
                }

			//3.1.2 “代删除”节点为右侧节点,逻辑处理同左侧
            } else {
            QMapNodeBase *w = x_parent->left;
            if (w->color() == QMapNodeBase::Red) {
                w->setColor(QMapNodeBase::Black);
                x_parent->setColor(QMapNodeBase::Red);
                rotateRight(x_parent);
                w = x_parent->left;
            }
            if ((w->right == 0 || w->right->color() == QMapNodeBase::Black) &&
                (w->left == 0 || w->left->color() == QMapNodeBase::Black)) {
                w->setColor(QMapNodeBase::Red);
                x = x_parent;
                x_parent = x_parent->parent();
            } else {
                if (w->left == 0 || w->left->color() == QMapNodeBase::Black) {
                    if (w->right)
                        w->right->setColor(QMapNodeBase::Black);
                    w->setColor(QMapNodeBase::Red);
                    rotateLeft(w);
                    w = x_parent->left;
                }
                w->setColor(x_parent->color());
                x_parent->setColor(QMapNodeBase::Black);
                if (w->left)
                    w->left->setColor(QMapNodeBase::Black);
                rotateRight(x_parent);
                break;
            }
        }
    }

	// 3.2 补位节点为红色,补位后直接变为红色即可
    if (x)
        x->setColor(QMapNodeBase::Black);
    }

	// 3.3 y已经移出树结构,所以将其释放
    free(y);
    --size;
}

// 更新最左侧节点
void QMapDataBase::recalcMostLeftNode()
{
    mostLeftNode = &header;
    while (mostLeftNode->left)
        mostLeftNode = mostLeftNode->left;
}

static inline int qMapAlignmentThreshold()
{
    // malloc on 32-bit platforms should return pointers that are 8-byte
    // aligned or more while on 64-bit platforms they should be 16-byte aligned
    // or more
    return 2 * sizeof(void*);
}

static inline void *qMapAllocate(int alloc, int alignment)
{
    return alignment > qMapAlignmentThreshold()
        ? qMallocAligned(alloc, alignment)
        : ::malloc(alloc);
}

static inline void qMapDeallocate(QMapNodeBase *node, int alignment)
{
    if (alignment > qMapAlignmentThreshold())
        qFreeAligned(node);
    else
        ::free(node);
}

// 新增节点,挂在到指定父节点的左侧或右侧分支下
QMapNodeBase *QMapDataBase::createNode(int alloc, int alignment, QMapNodeBase *parent, bool left)
{
    QMapNodeBase *node = static_cast<QMapNodeBase *>(qMapAllocate(alloc, alignment));
    Q_CHECK_PTR(node);

    memset(node, 0, alloc);
    ++size;

    if (parent) {
        if (left) {
            parent->left = node;
            if (parent == mostLeftNode)
                mostLeftNode = node;
        } else {
            parent->right = node;
        }
        node->setParent(parent);
        rebalance(node);
    }
    return node;
}

// 递归析构正树结构,先左侧后右侧
void QMapDataBase::freeTree(QMapNodeBase *root, int alignment)
{
    if (root->left)
        freeTree(root->left, alignment);
    if (root->right)
        freeTree(root->right, alignment);
    qMapDeallocate(root, alignment);
}

QMapDataBase *QMapDataBase::createData()
{
    QMapDataBase *d = new QMapDataBase;

    d->ref.initializeOwned();
    d->size = 0;

    d->header.p = 0;     // 节点数据
    d->header.left = 0;  // 左侧节点
    d->header.right = 0; // 右侧节点
    d->mostLeftNode = &(d->header);

    return d;
}

void QMapDataBase::freeData(QMapDataBase *d)
{
    delete d;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值