真c++ 从二叉树到红黑树(1)之二叉树节点类及遍历详解

  此文章为从二叉树到红黑树系列文章的第一节,主要介绍写这系列文章的起因,致谢邓老师,解释二叉树节点类和二叉树的四种遍历写法(包括递归和迭代写法)


文章目录

一、前言与致谢~(点击右边波浪线可以返回目录)

  c++是一门与时俱进的语言,纵观网上绝大多数c++的数据结构实现,都是c with class,不得不说,这样确实有利于理解和学习,但对于那些想借助数据结构来巩固c++的人来说,不由得有点遗憾(初学直接啃stl源码会直接自闭的)。

  在此,感谢清华的邓俊辉老师的c++数据结构教材和视频,多亏了邓老师的辛勤耕耘,我才有幸见到现代化的适用于初学者的c++数据结构代码。在邓老师教材的第五章,第七章和第八章,全部是介绍树的知识,看完和仿照敲完代码之后,获益匪浅,不止是对数据结构和算法的理解更深了一步,也对c++的特性掌握的更加牢固了一步。

  笔者将其代码中的一些特性进行了一些更新和修改,让其更符合现代风格。由于老师对堆区数据的回收都处理的很好,有的地方用智能指针的话,需要改比较多的东西,所以还是保持原来的new和delete。

  本系列文章为了方便,会引用教材和视频中的一些插图(侵删),虽然我对原代码进行了些许修改,但原代码所有权依然归邓老师所有

  在写此系列文章的时候,笔者也对文章进行了排版,让其更容易进行阅读,大家放心阅读,笔者在适当的地方都留了空行和分段。(md的排版真心没word好= =,排个版都要敲代码!)

  本系列文章的所有代码,均在vs2019版本下编译通过,如果你无法编译过,请把编译器的c++版本开启c++17以上。

二、本系列文章内容基本介绍~

1.与邓老师原有代码对比~

我主要对以下几点进行了更新和修改

  1. 将所有的宏定义换成了c++11的constexpr和inline。有的换成了全局静态函数,有的换成了类中使用的函数。
  2. 大概是处于教学需要,邓公用的都是自定义的stack和queue,笔者就偷懒,直接用stl现成的stack和queue了,实现上大致相同。
  3. 将原来的某些代码进行了增减,这样更便于呈现主要内容。
  4. 所有主要代码都有迭代形式,主要代码流程中没有递归,有的代码会分别给出递归和迭代的形式,以便于理解。

2.主要流程(重点)~

笔者根据代码的内容不同,将分六部分来展现

  1. 基本二叉树节点,通用函数
  2. 基本二叉树类的定义和实现
  3. BST(二叉搜索树的实现)
  4. AVL(二叉平衡搜索树的实现)
  5. B树的实现(如果你只想了解B树,可以跳过所有章节,直接看B树)
  6. 红黑树的实现

  强烈建议按照流程进行浏览,只有这样,你才能真正了解红黑树!

  但若你没有时间,也没关系,我会在一些重点部分,涉及到之前知识点的地方弄一个提示,去指明你去哪才能看到相关的内容。

3.要看懂文章内的代码你需要了解的内容~

  1. 掌握c++的引用,指针,类的基本使用
  2. 知道什么是指针引用
  3. 了解命名空间,using声明,using别名。
  4. 了解c++的模板,仿函数的使用
  5. 熟悉stl中stack和queue容器的用法
  6. 对constexpr和inline不陌生
  7. 对树有一定的了解

  要完全看懂本系列文章,如果你是初次接触,不要妄想能在两三个小时内弄懂此系列文章所有内容,没人可以做的到,正确的方法是一天抽一两个小时研究其中一篇文章,对于红黑树可能需要更长的时间,你才能真正的看懂代码,如果你看过邓老师的教程的话,会更能体会到我这段话的意思。

  如果你嫌长,大可以看其他的文章,但我相信,弄懂本系列文章之后,不仅你的数据结构的功底,还是c++的功底,都可以更上一层楼。(针对c++初学者)。

三、树的语义规定~

1.树的高度与深度~

在此系列文章中,会大量用到树的高度和深度的概念,在此做一个强调

深度: 从根到该节点的唯一路径长,根的深度为0;从上到下数
高度 从该节点到叶节点的最长路径,所有树叶的高度为0;从下往上数

另外,规定空树的高度为-1

所以,若只有根节点存在,根节点的高度为0。

2.树的遍历~

在这里插入图片描述

(1)树的先序遍历~

先访问根节点,再依次遍历左子树和右子树
即根左右(一直左到底再右)
3102546

(2)树的中序遍历~

先访问左子树,再依次遍历根和右子树
即左根右(即将各个节点映射到一条直线上)
0123456

(3)树的后序遍历~

先访问左子树,再依次遍历右子树和根
即左右根
0214653

(4)树的层次遍历~

从上而下,从左到右访问树中各个节点
3150246

四、基本二叉树节点类~

(一)定义变量和接口~

1.需要的变量~

T _data;//存放数据
BinNodePtr _parent;
BinNodePtr _lchild;
BinNodePtr _rchild;
int _height;//高度(通用)
RBColor _color;//红黑树专用

2.需要的接口~

四种遍历
取得当前节点的直接后继(BST,AVL,RedBlack用)

3.其他辅助函数~

判等器等操作符重载

4.BinNode.h~

	namespace {
   
		enum class RBColor {
    RED, BLACK };
	}

	//===二叉树节点类===//
	template<typename T = int>
	class BinNode {
   

	public:
		using BinNodePtr = BinNode<T>*;

	public:
		T _data;//存放数据
		BinNodePtr _parent;
		BinNodePtr _lchild;
		BinNodePtr _rchild;
		int _height;//高度(通用)
		RBColor _color;//红黑树专用

	public:
		BinNode() :_data(0), _parent(nullptr),_lchild(nullptr), _rchild(nullptr), _height(0), _color(RBColor::RED) {
   }

		BinNode(
			const T& data, 
			const BinNodePtr parent = nullptr, 
			const BinNodePtr lchild=nullptr, 
			const BinNodePtr rchild=nullptr,
			const int &height=0, 
			const RBColor& color = RBColor::RED)
			:_data(data), _parent(parent), _lchild(lchild), _rchild(rchild),_height(height),_color(color) {
   }

	public:
		constexpr bool operator==(const BinNode& bN)const {
   
			return this->_data == bN._data;
		}

		constexpr bool operator!=(const BinNode& bN)const {
   
			return !(this->_data == bN._data);
		}

		constexpr bool operator<(const BinNode& bN)const {
   
			return this->_data < bN._data;
		}

		constexpr bool operator>(const BinNode& bN)const {
   
			return this->_data > bN._data;
		}

		constexpr bool operator<=(const BinNode& bN)const {
   
			return !(this->_data > bN._data);
		}

		constexpr bool operator>=(const BinNode& bN)const {
   
			return !(this->_data < bN._data);
		}
	public:

		BinNodePtr succ();//取得中序遍历当前节点的直接后继,必然无左孩子

	public:
		template <typename VST> void travLevel(VST); //子树层次遍历

		template <typename VST> void travPre(VST,const int &method=1); //子树先序遍历

		template <typename VST> void travPost(VST,const int&method=1); //子树后序遍历

		template <typename VST> void travIn(VST,const int&method=1); //子树中序遍历

	};//class BinNode

(二)通用全局静态函数(不包含后面代码就运行不了哦)~

  邓老师书上的宏定义有很多个,我将用的多的封装成相应的constexpr和inline函数,将用的少的放入对应的树的类中,这样更有利于管理和应用。

BInNode_Macro.h~

#pragma once

/******************************************************************************************
 * BinNode状态与性质的判断
 ******************************************************************************************/

#include<algorithm>
using std::max;

namespace mytree_marcro {
   
    template<typename BinNodePtr>
    static constexpr bool IsRoot(const BinNodePtr& x) {
   
        return !(x->_parent);
    }

    template<typename BinNodePtr>
    static constexpr bool IsLChild(const BinNodePtr& x) {
   
        return (!IsRoot(x) && (x == x->_parent->_lchild));
    }

    template<typename BinNodePtr>
    static constexpr bool IsRChild(const BinNodePtr& x) {
   
        return (!IsRoot(x) && (x == x->_parent->_rchild));//不是根节点,并且其地址跟其父亲的右孩子地址相同
    }


    template<typename BinNodePtr>
    static constexpr bool HasLChild(const BinNodePtr& x) {
   
        return (x == nullptr) ? false : x->_lchild;
	}

    template<typename BinNodePtr>
    static constexpr bool HasRChild(const BinNodePtr& x) {
   
        return (x == nullptr) ? false : x->_rchild;
	}

    template<typename BinNodePtr>
    static constexpr int stature(const BinNodePtr& x) {
   //获取高度
        return x ? x->_height : -1;//空指针高度为-1
    }

}// namespace mytree_marcro

(三)树的四种遍历~

(重点)借助仿函数来实现遍历的高明方法~

  对于老手和善用stl的人而言, 这种利用回调函数来进行访问对应节点的方式,再正常不过。在邓老师的遍历代码中,就高明的用了这种方式,通过这种高内聚,低耦合的方式,从而能对节点进行更多的操作,而并非仅仅是cout就完事。在之后的BST,AVL和RedBlack中,我都会提供一些测试用的仿函数,读者可根据自己的需要进行改进。

1.层次遍历的队列写法~

  根节点入队,然后弹出根节点,并将其左右节点入队。直至队列为空。
在这里插入图片描述

template<typename T>template<typename VST>
void BinNode<T>::travLevel(VST visit) {
   
	if (this == nullptr)//提前判断是否为空树
		return;
	queue<BinNodePtr> Q;
	Q.push(this);//根节点入队
	BinNodePtr current = nullptr;//防止多次调用构造析构,故在循环为创建临时变量
	while (!Q.empty(
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值