数据结构(三)简单树操作

数据结构…本系列旨在对基础算法进行记录学习,为了之后的面试一个弥补~~

本系列不是科普文,是为了找工作快速拾遗系列.

快速浏览,不会把学过的东西忘记~~

1.看图理解概念

图片已经经过原作者同意转载!

树的层次

树的结构

树的状态

二叉树

查找树

2.二叉查找树基本操作

简单的就不一一说明了,有几个难点会详细说明.

插入节点
遍历二叉树

前序遍历树a:10 5 4 3 6 15 16

前序遍历树b:5 3 2 4 8 7 9

中序遍历树a:3 4 5 6 10 15 16

中序遍历树b:2 3 4 5 7 8 9

后序遍历树a:3 4 6 5 16 15 10

后序遍历树b:2 4 3 7 9 8 5

中序前继
中序后继

先继和后继与遍历的方式联合起来说得,比如这里就是以中序遍历来说明情况.

这里与其用图来说明,不如直接上代码方便:

有左子树很简单就理解了,其它的两种情况是找规律问题,自己画个图结合代码一下子就理解了.

/*寻找中序遍历的前驱节点*/
/*
一个节点的前驱节点有3种情况:
1. 它有左子树,则左子树根节点为其前驱节点
2. 它没有左子树,且它本身为右子树,则其父节点为其前驱节点
3. 它没有左子树,且它本身为左子树,则它的前驱节点为“第一个拥有右子树的父节点”
*/
template <typename T>
BSNode<T>* BStree<T>::predecessor(BSNode<T>* pnode)
{
    if (pnode->lchild != nullptr)
    {//第一种情况
        pnode = pnode->lchild;
        while (pnode->rchild != nullptr)
        {
            pnode = pnode->rchild;
        }
        return pnode;
    }
    //第二/三种情况
    BSNode<T>* pparent = pnode->parent;
    while (pparent != nullptr && pparent->lchild == pnode)
    {
        pnode = pparent;
        pparent = pparent->parent;
    }
    return pparent;
}
/*
一个节点的后继节点有3种情况:
它有右子树;则其后继节点为其右子树的最左节点
它没有右子树,但它本身是一个左孩子,则后继节点为它的双亲
它没有右子树,但它本身是一个右孩子,则其后继节点为“具有左孩子的最近父节点”
*/
template <typename T>
BSNode<T>* BStree<T>::successor(BSNode<T>* pnode)
{
    if (pnode->rchild != nullptr)
    {//第一种情况
        pnode = pnode->rchild;
        while (pnode->lchild != nullptr)
        {
            pnode = pnode->lchild;
        }
        return pnode;
    }

    BSNode<T>* pparent = pnode->parent;
    while (pparent!=nullptr&& pparent->rchild == pnode)
    {//第二/三种情况
        pnode = pparent;
        pparent = pparent->parent;
    }
    return pparent;
}
删除节点

这里是最难的一点,前面都很容易理解,这一点我看了好久.

先说一下原理,然后再结合代码看,那就容易理解了.

对与第二和第三种情况较为简单,这里只说明第一种情况.

解释说明
查找极值

总的代码:

#ifndef TREE_H
#define TREE_H

#include <iostream>
using namespace std;

//二叉查找树的节点结构
template<typename T>
struct BSNode
{
    BSNode(T t):
        value(t),lchild(nullptr),rchild(nullptr),parent(nullptr)
    {}
    BSNode() = default;
public:
    T value;
    BSNode<T>* lchild;
    BSNode<T>* rchild;
    BSNode<T>* parent;
};
//
template<typename T>
class BStree
{
public:
    BStree();
    ~BStree();

    void preOrder();    //前序遍历二叉树
    void inOrder();     //中序遍历二叉树
    void postOrder();   //后序遍历二叉树
    //void layerOrder();    //层次遍历二叉树

    BSNode<T>* search_recursion(T key);     //递归地进行查找
    //BSNode<T>* search_Iterator(T key);        //迭代地进行查找

    T search_minimun(); //查找最小元素
    T search_maximum(); //查找最大元素

    BSNode<T>* successor  (BSNode<T>* x);   //查找指定节点的后继节点
    BSNode<T>* predecessor(BSNode<T>* pnode);   //查找指定节点的前驱节点

    void insert(T key); //插入指定值节点

    void remove(T key); //删除指定值节点
    void destory();     //销毁二叉树
    //void print();     //打印二叉树


private:
    BSNode<T>* root; //根节点
    typedef BSNode<T>* pointer;//redefine
private:
    BSNode<T>* search(BSNode<T>* & p, T key);
    void remove(BSNode<T>*  p, T key);
    void preOrder(BSNode<T>* p);
    void inOrder(BSNode<T>* p);
    void postOrder(BSNode<T>* p);
    //T search_minimun(BSNode<T>* p);
    //T search_maximum(BSNode<T>* p);
    void destory(BSNode<T>* &p);
};
/*默认构造函数*/
template<typename T>
BStree<T>::BStree():root(nullptr)
{}

/*析构函数*/
template<typename T>
BStree<T>::~BStree()
{
    //destory(root);
}

/*插入函数*/
template<typename T>
void BStree<T>::insert(T key)
{
    pointer pparent = nullptr;
    pointer pnode   = root;
    //这里写的太复杂了,有很大的改进空间
    //只讲原理和拾遗,不做过多说明
    while (pnode != nullptr)
    {//找到插入的母节点
        pparent = pnode;
        if (key>pnode->value)
            pnode = pnode->rchild;
        else if (key<pnode->value)
            pnode = pnode->lchild;
        else
            break;
    }
    pnode = new BSNode<T>(key);
    if(pparent == nullptr)
        root = pnode;
    else {//插入到母节点之后
        if (key>pparent->value)
            pparent->rchild = pnode;
        else
            pparent->lchild = pnode;
    }
    pnode->parent = pparent;//新节点指向母节点
}

/*前序遍历*/
template <typename T>
void BStree<T>::preOrder()
{
    preOrder(root);
}
template <typename T>
void BStree<T>::preOrder(BSNode<T>* p)
{
    if(p!=nullptr)
    {
        cout<<p->value<<endl;
        preOrder(p->lchild);
        preOrder(p->rchild);
    }
}

/*中序遍历*/
template <typename T>
void BStree<T>::inOrder()
{
    inOrder(root);
}
template<typename T>
void BStree<T>::inOrder(BSNode<T>* p)
{
    if(p!=nullptr)
    {
        inOrder(p->lchild);
        cout<<p->value<<endl;
        inOrder(p->rchild);
    }
}

/*后序遍历算法*/
template <typename T>
void BStree<T>::postOrder()
{
    postOrder(root);
}
template <typename T>
void BStree<T>::postOrder(BSNode<T>* p)
{
    if (p != nullptr)
    {
        postOrder(p->lchild);
        postOrder(p->rchild);
        cout << p->value<<endl;
    }
}

/*寻找中序遍历的前驱节点*/
/*
一个节点的前驱节点有3种情况:
1. 它有左子树,则左子树根节点为其前驱节点
2. 它没有左子树,且它本身为右子树,则其父节点为其前驱节点
3. 它没有左子树,且它本身为左子树,则它的前驱节点为“第一个拥有右子树的父节点”
*/
template <typename T>
BSNode<T>* BStree<T>::predecessor(BSNode<T>* pnode)
{
    if (pnode->lchild != nullptr)
    {//第一种情况
        pnode = pnode->lchild;
        while (pnode->rchild != nullptr)
        {
            pnode = pnode->rchild;
        }
        return pnode;
    }
    //第二/三种情况
    BSNode<T>* pparent = pnode->parent;
    while (pparent != nullptr && pparent->lchild == pnode)
    {
        pnode = pparent;
        pparent = pparent->parent;
    }
    return pparent;
}

/*
一个节点的后继节点有3种情况:
它有右子树;则其后继节点为其右子树的最左节点
它没有右子树,但它本身是一个左孩子,则后继节点为它的双亲
它没有右子树,但它本身是一个右孩子,则其后继节点为“具有左孩子的最近父节点”
*/
template <typename T>
BSNode<T>* BStree<T>::successor(BSNode<T>* pnode)
{
    if (pnode->rchild != nullptr)
    {//第一种情况
        pnode = pnode->rchild;
        while (pnode->lchild != nullptr)
        {
            pnode = pnode->lchild;
        }
        return pnode;
    }

    BSNode<T>* pparent = pnode->parent;
    while (pparent!=nullptr&& pparent->rchild == pnode)
    {//第二/三种情况
        pnode = pparent;
        pparent = pparent->parent;
    }
    return pparent;
}

/*销毁二叉树*/
template<typename T>
void BStree<T>::destory()
{
    destory(root);
}
template <typename T>
void BStree<T>::destory(BSNode<T>* &p)
{
    if (p != nullptr)
    {
        if (p->lchild != nullptr)
            destory(p->lchild);
        if (p->rchild != nullptr)
            destory(p->rchild);
        delete p;
        p = nullptr;
    }
}


/*删除指定节点*/
//---这里是最难理解的,文中会以图进行说明!
template <typename T>
void BStree<T>::remove(T key)
{
    remove(root, key);
}
template <typename T>
void BStree<T>::remove(BSNode<T>* pnode, T key)
{
    /*
    //获取当前节点位置和前继信息
    pointer new_temp = search(pnode,key);
    pointer pre_temp = this->predecessor(new_temp);
    if(new_temp->lchild==nullptr&&new_temp->rchild==nullptr)
    {//没有子节点
        bool flag =new_temp->parent->lchild==new_temp;
        if (flag)
            new_temp->parent->lchild = nullptr;
        else
            new_temp->parent->rchild = nullptr;
        delete new_temp;
    }
    else if (new_temp->lchild==nullptr||new_temp->rchild==nullptr)
    {
        pointer next_child = new_temp->lchild==nullptr?new_temp->rchild:new_temp->lchild;
        pointer pre_parent = new_temp->parent->lchild==new_temp?new_temp->rchild:new_temp->lchild;
        pre_parent = next_child;
        next_child->parent = pre_parent;
        delete new_temp;
    }
    else
    {

    }*/
    if (pnode != nullptr)
    {
        if (pnode->value == key)
        {
            BSNode<T>* pdel=nullptr;

            if (pnode->lchild == nullptr || pnode->rchild == nullptr)
                pdel = pnode;                   //情况二、三:被删节点只有左子树或右子树,或没有孩子
            else
                pdel = predecessor(pnode);      //情况一:被删节点同时有左右子树,则删除该节点的前驱

            //此时,被删节点只有一个孩子(或没有孩子).保存该孩子指针
            BSNode<T>* pchild=nullptr;
            if (pdel->lchild != nullptr)
                pchild = pdel->lchild;
            else
                pchild = pdel->rchild;

            //让孩子指向被删除节点的父节点
            if (pchild != nullptr)
                pchild->parent = pdel->parent;

            //如果要删除的节点是头节点,注意更改root的值
            if (pdel->parent == nullptr)
                root = pchild;

            //如果要删除的节点不是头节点,要注意更改它的双亲节点指向新的孩子节点
            else if (pdel->parent->lchild==pdel)
            {
                pdel->parent->lchild = pchild;
            }
            else
            {
                pdel->parent->rchild = pchild;
            }

            if (pnode->value != pdel->value)
                pnode->value = pdel->value;
            delete pdel;
        }
        //进行递归删除
        else if (key > pnode->value)
        {
            remove(pnode->rchild, key);
        }
        else remove(pnode->lchild, key);
    }
}

/*查找指定元素的节点(递归)*/
template <typename T>
BSNode<T>* BStree<T>::search_recursion(T key)
{
    return search(root,key);
}
/*private:search()*/
/*递归查找的类内部实现*/
template <typename T>
BSNode<T>* BStree<T>::search(BSNode<T>* & pnode, T key)
{
    pointer temp = pnode;
    if (temp == nullptr)
        return nullptr;
    if (key > temp->value)
        return search(temp->rchild,key);
    else if(key<pnode->value)
        return search(temp->lchild,key);
    else
        return temp;
}

#endif // TREE_H

注意C++的template只能在一个头文件,千万不要模块化编程一个在.h文件一个在.cpp文件!!!

参考资料:

文章主要参考,且图片均来自大神博文

我之前写的C++链表

吕鑫老师视频,很早之前看得

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 上一篇文章讲述了树的概念, 特征以及分类, 旨在让我们理解什么是树, 树的一些常用的概念是什么,树的分类有哪些等。...
    DevCW阅读 2,026评论 4 10
  • 目录 1、什么是树 2、相关术语 3、二叉树 3.1、二叉树的类型 3.2、二叉树的性质 3.3、二叉树的结构 3...
    我哈啊哈啊哈阅读 2,545评论 0 10
  • 数据结构和算法--二叉树的实现 几种二叉树 1、二叉树 和普通的树相比,二叉树有如下特点: 每个结点最多只有两棵子...
    sunhaiyu阅读 6,457评论 0 14
  • 1.继承有什么作用? 继承的作用就是让一个类能够重用另一个类的方法和属性,提高代码的复用性。这样的话就会有一个“基...
    candy252324阅读 514评论 0 0
  • 在此不论述克服演讲的紧张,只针对演讲需要的技巧和经验。 我们都知道,口语和书面的表达,差别是那么大,一个是严谨...
    余丹阅读 724评论 0 2