每日一题23:查找树基本操作

17 篇文章 0 订阅

距离上一篇博客已经过去了四天,“每日一题”这个标题给的压力还是挺大的,这四天一直在学习查找树,同时更进一步地接触STL,所以更新慢了。查找树的删除操作整整花了一天才学会,比较耗时,另外两个有点麻烦的地方是迭代器的前进与后退,需要画图再根据查找树的性质推理。这三个操作算得上两个题吧,整个查找树操作算得上一个题,这样想想,嗯,勉强对得起标题啦,O(∩_∩)O。好了,闲话少说,上代码,吸取了以前的教训,注释全部写在源代码中,直接复制过来就可以了,关于注释只写了比较麻烦的一些操作的,那些简单的就没写了,希望以后拿来看的时候还能看懂:

#ifndef _SEARCH_TREE_H_
#define _SEARCH_TREE_H_

#include "../Utilites/type_traits.h"
#include "Functor.h"

namespace MyDataStructure
{
    //将node结构分为两层,可以带来一定的弹性,节点
    //基类只负责节点连接的功能,而子类增加了实际的
    //数据成员,所谓的弹性在这里得到了体现,本例的
    //子类只需要拥有一个数据成员,而对于红黑树而言,
    //子类还需要表示颜色的成员,所有使用分层结构可以
    //复用
    struct BinaryNodeBase
    {
        typedef BinaryNodeBase* BasePtr;
        BasePtr parent; //为了支持迭代器,增设该指针
        BasePtr left;
        BasePtr right;

        //查找以本节点为根的树中的最小值,
        //如果有左子树,则一直左转到最左边
        //的节点,否则本身为最小
        BasePtr minimum()
        {
            BinaryNodeBase* res = this;
            while (res->left)
            {
                res = res->left;
            }
            return res;
        }

        //查找以本节点为根的树中的最大值,
        //如果有右子树,则一直右转到最右边
        //的节点,否则本身为最大
        BasePtr maximum()
        {
            BinaryNodeBase* res = this;
            while (res->right)
            {
                res = res->right;
            }
            return res;
        }
    };

    //具体的节点类
    template<typename ValueType>
    struct BinaryNode : public BinaryNodeBase
    {
        typedef BinaryNode<ValueType>* LinkType;
        ValueType value;
    };


    //查找树迭代器基类
    struct  SearchTreeIteratorBase
    {
        typedef BinaryNodeBase::BasePtr BasePtr;
        BasePtr node;

        //让迭代器移动到下一个节点
        void Increament()
        {
            //如果迭代器指示的节点有右子节点,
            //则以该右子节点为根的树的最小值节
            //点就是下一个迭代器该指示的节点
            if (node->right) node = node->right->minimum();
            else
            {
                //否则,如果当前指示的节点是右子节
                //点,就需要往上搜索第一个不是右子节点
                //的节点,如果当前指示的节点是左子节点,
                //那么父节点就是了。画图根据查找树的性质容易
                //的到算法
                BasePtr p = node->parent;
                //根节点的父节点为nullptr,需要注意
                while (p && p->right == node)
                {
                    node = p;
                    p = p->parent;
                }
                node = p;
            }
        }

        //让迭代器移动到上一个节点,和Increament
        //算法大同小异,对称地考虑
        void Decreament()
        {
            if (node->left) node = node->left->maximum();
            else
            {
                BasePtr p = node->parent;
                while (p && p->left == node)
                {
                    node = p;
                    p = p->parent;
                }
                node = p;
            }
        }

        //模拟指针行为,迭代器是一种智能指针
        bool operator ==(const SearchTreeIteratorBase& it)
        {
            return this->node == it.node;
        }

        bool operator != (const SearchTreeIteratorBase& it)
        {
            return this->node != it.node;
        }
    };

    //真正的查找树迭代器
    template<typename Value,typename Ref,typename Ptr>
    struct SearchTreeIterator : public SearchTreeIteratorBase
    {
        typedef Value ValueType;
        typedef Ref reference;
        typedef Ptr pointer;
        typedef SearchTreeIterator<Value, Value&, Value*> iterator;
        typedef SearchTreeIterator<Value, const Value&, Value*> const_iterator;
        typedef SearchTreeIterator<Value, Value&, Value*> self;
        typedef BinaryNodeBase* LinkType;
        typedef BinaryNode<Value>* NodePtr;

        SearchTreeIterator() {}
        SearchTreeIterator(LinkType x) { node = x; }
        SearchTreeIterator(const iterator& it) { node = it.node; }
        self& operator =(const iterator& it) { node = it.node; return *this; }

        //模拟指针行为,迭代器是一种智能指针
        reference operator *(){ return NodePtr(node)->value; }
        pointer operator ->() { return &(operator*()); }

        self& operator ++()
        {
            Increament();
            return *this;
        }

        self operator ++(int)
        {
            self tmp = *this;
            Increament();
            return tmp;
        }

        self& operator --()
        {
            Decreament();
            return *this;
        }

        self operator --(int)
        {
            self tmp = *this;
            Decreament();
            return tmp;
        }
    };

    template < typename T, typename Compare = less<T>>
    class SearchTree
    {
    public:
        typedef typename ParameterTrait<T>::ParameterType ParameterType;
        typedef typename BinaryNode<T> NodeType;
        typedef typename BinaryNode<T>* NodePtr;
        typedef typename BinaryNode<T>::BasePtr BasePtr;
        typedef typename SearchTreeIterator<T, T&, T*>::iterator iterator;
    public:

        //有两种方式构造一棵查找树:
        //  1)构造一颗空树。
        //  2)复制一颗已经存在的树
        SearchTree() : size(0),head(nullptr){}
        SearchTree(const SearchTree& st)
        {
            copy(st);
        }
        SearchTree& operator = (const SearchTree& st)
        {
            if (&st != this)
            {
                Clear();
                copy(st);
            }
            return *this;
        }
        ~SearchTree(){ Clear(); }
        const int Size(){ return size; }
        void Clear()
        {
            _destroy(head);
            head = nullptr;
            size = 0;
        }

        //允许插入重复值的方法(批量版)
        void Insert(T values[], int count)
        {
            for (int i = 0; i < count; ++i)
            {
                Insert(values[i]);
            }
        }

        //允许插入重复值的方法
        void Insert(const ParameterType value)
        {
            //q表示插入后新节点的父节点
            BasePtr q = find_position_for_insert(value);
            //q为nullptr,树是空的,所以构造头结点
            if (q == nullptr)
            {
                create_head(value);
            }
            //否则,插入就好了
            else
            {
                NodePtr node = create_node(value);
                insert(&q, node);
            }
        }

        //不允许插入重复值的方法(批量版)
        void UniqueInsert(T values[], int count)
        {
            for (int i = 0; i < count; ++i)
            {
                UniqueInsert(values[i]);
            }
        }
        //不允许插入重复值的方法
        void UniqueInsert(const ParameterType value)
        {
            //q同Insert中,通过p可以知道树中是否
            //已经插入同样的值了
            BasePtr q = nullptr;
            BasePtr p = find_first(head,value, &q);
            if (q == nullptr)
            {
                create_head(value);
            }
            else
            {
                //p不为空,插入会变成重复值,所以什么也不做了
                if (p != nullptr) return;
                NodePtr node = create_node(value);
                insert(&q, node);
            }
        }

        iterator FindFirst(const ParameterType value)
        {
            BasePtr temp = nullptr;
            return iterator(find_first(head,value,&temp));
        }

        iterator FindLast(const ParameterType value)
        {
            BasePtr temp = nullptr;
            return iterator(find_last(head, value, &temp));
        }

        //从树中删除一个节点
        void Erase(iterator& it)
        {
            erase(it.node);
        }

        //从树中删除一个节点
        void Erase(const ParameterType value)
        {
            Erase(FindFirst(value));
        }

        //查找树中的元素是排序的,获得树中最左端的节点
        iterator First()
        {
            BasePtr first = nullptr;
            if (head) first = head->minimum();
            return iterator(first);
        }
        //查找树中的元素是排序的,获得树中最右端的节点
        iterator Last()
        {
            BasePtr last = nullptr;
            if (head) last = head->maximum();
            return iterator(last);
        }

        //nullptr指示到了尽头了
        iterator End()
        {
            return iterator(nullptr);
        }

    private:
        NodePtr create_node(const ParameterType value)
        {
            NodePtr node = new NodeType;
            node->parent = node->left = node->right = nullptr;
            node->value = value;
            return node;
        }
        void copy(const SearchTree& st)
        {
            __copy(&head, nullptr, st.head);
        }

        //复制只能采取先序的方式进行,要让子
        //节点有归属,得先构造父节点
        void __copy(BasePtr* dst,BasePtr p, BasePtr src)
        {
            if (src == nullptr)
            {
                *dst = nullptr;
                return;
            }
            *dst = create_node(((NodePtr)src)->value);
            (*dst)->parent = p;
            ++size;
            __copy(&((*dst)->left),*dst, src->left);
            __copy(&((*dst)->right),*dst,src->right);
        }

        //摧毁最好采取后序的方式进行,
        //这样思考起来会自然更多
        void _destroy(BasePtr p)
        {
            if (p == nullptr) return;
            _destroy(p->left);
            _destroy(p->right);
            delete (NodePtr)p;
        }

        //删除一个节点是最麻烦的操作
        void erase(BasePtr node)
        {
            if (node == nullptr) return;


            //如果待删除的节点只有一个子节点
            //,直接让节点的父节点绕过它,指向
            //它的子节点就好了,代码之所以复杂,
            //是因为需要考虑父节点是否为空,以及
            //处理节点的连接时要考虑parent指针,
            //而维护parent指针则是为了支持迭代器


            //只有右节点
            if (node->left == nullptr)
            {
                BasePtr p = node;
                node = node->right;
                BasePtr q = parent(p);
                if(node != nullptr) node->parent = q;
                if (p == head) head = node;
                else
                {
                    if (q->left == p) q->left = node;
                    else q->right = node;
                }

                delete NodePtr(p);
            }
            //只有右节点
            else if (node->right == nullptr)
            {
                BasePtr p = node;
                node = node->left;
                BasePtr q = parent(p);
                if (node != nullptr) node->parent = q;
                if (p == head) head = node;
                else
                {
                    if (q->left == p) q->left = node;
                    else q->right = node;
                }
                delete NodePtr(p);
            }
            //上面两种情况还包含了没有子节点的情况

            //待删除节点具有两个节点,根据查找二叉树的性质,
            //使用其左子树中的最大值代替待删除节点,左子树最大值
            //节点要么某个节点的右子节点,要么就是待删除节点的
            //左子节点
            else
            {
                BasePtr p = (node->left)->maximum(); 
                BasePtr q = p->parent;     //因为node有两个子节点,所以p的parent不会为空

                //把待删除节点的值替换为其左子树中的
                //最大值,实际上直接将节点进行替换更合理,
                //在节点值为类并拥有许多笔数据时效率更高,
                //只是指针操作跟复杂一点
                ((NodePtr)node)->value = ((NodePtr)p)->value;

                //最大值是某个节点的右子节点,
                //该最小值节点最多拥有一个左子树
                //否则子树中就有一个值比它还大,
                //于是调整最大值节点的左子树
                if (q != node)
                { 
                    q->right = p->left;     
                }
                //最小值是左子节点,此时左子树最多有一个
                //左子树,于是调整最大值节点的左子树
                else 
                { 
                    q->left = p->left;
                }
                //连接时处理parent指针
                if (p->left != nullptr) p->left->parent = q;
                delete NodePtr(p);
            }
            --size;
        }
        void insert(BasePtr* parent, BasePtr node)
        {
            node->parent = *parent;
            if (*parent == nullptr)
                head = node;
            else if (compare((NodePtr(node))->value, ((NodePtr)(*parent))->value))
                (*parent)->left = node;
            else
                (*parent)->right = node;
            ++size;
        }

        void create_head(const ParameterType value)
        {
            head = create_node(value);
            ++size;
        }
        BasePtr left(const BasePtr node){ if (node) return node->left; else return nullptr; }
        BasePtr right(const BasePtr node){ if (node) return node->right; else return nullptr; }
        BasePtr parent(const BasePtr node){ if (node) return node->parent; else return nullptr; }
        BasePtr find_first(BasePtr h,const ParameterType value,BasePtr* last_visited_valid_node)
        {
            BasePtr res = nullptr;
            BasePtr p = h;
            *last_visited_valid_node = nullptr;
            while (p)
            {
                *last_visited_valid_node = p;
                if (compare(value, ((NodePtr)p)->value)) p = p->left;
                else if (value == ((NodePtr)p)->value){ res = p; break; }
                else p = p->right;
            }
            return res;
        }
        //假设有重复,那么根据插入算法,后来的重复值都被插到第一个值
        //的右子树中,但未必就是它的右子节点,所以查找到一个节点后,
        //还要在该节点的右子树中继续查找
        BasePtr find_last(BasePtr h,const ParameterType value, BasePtr* last_visited_valid_node)
        {
            BasePtr res = find_first(h,value, last_visited_valid_node);
            if (res != nullptr)
            {
                BasePtr last_visited_valid_node_1 = nullptr;
                BasePtr res_1 = find_first(res->right, value, &last_visited_valid_node_1);
                if (res_1 != nullptr)
                {
                    res = res_1;
                    *last_visited_valid_node = last_visited_valid_node_1;
                }
            }
            return res;
        }
        //找到位置一定是插入节点的父节点
        BasePtr find_position_for_insert(const ParameterType value)
        {
            BasePtr res = nullptr;
            BasePtr p = head;
            while (p)
            {
                res = p;
                if (compare(value, ((NodePtr)p)->value)) p = p->left;
                else p = p->right;
            }
            return res;
        }

    private:
        BasePtr head;
        int size;
        Compare compare;
    };
}

#endif

上面连同注释一共使用了500行代码,应该还有可以精简的地方,代码行数越少越好,想起刚学编程的时候只想着怎样把代码写长,不禁好笑,现在呢觉得对于完成某个功能的代码,不丢失可读性的情况下越短越好,个人写过的总代码行呢,则是越多越好,这一多一少应该就是作为一个学习者应该追求的吧。

Functor.h文件的代码:

#ifndef _FUNCTOR_H_
#define _FUNCTOR_H_
#include "../Utilites/type_traits.h"
namespace MyDataStructure
{

    template<typename T>
    struct less
    {
        typedef typename ParameterTrait<T>::ParameterType ParameterType;
        bool operator ()(const ParameterType op1, const ParameterType op2)
        {
            return op1 < op2;
        }
    };
}

#endif

type_traits.h文件前面的博文已经展示过,不在列出。测试代码(有重复插入):

// SearchTreeTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "../../include/SearchTree.h"


using namespace MyDataStructure;
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    int v[] = { 1, 3, 3, 9, 7, 9, 4, 2, 8, 10 };
    SearchTree<int> st;
    st.Insert(v, 10);
    for (SearchTree<int>::iterator it = st.Last(); it != st.End(); --it)
        cout << *it << " ";
    cout << endl;
    SearchTree<int> st1(st);
    st = st;
    SearchTree<int>::iterator it1 = st.FindFirst(9), it2 = st.FindLast(9);
    cout << "they are " << (it1 == it2) <<" : "<<*it1<<" "<<*it2<<endl;
    for (int i = 0; i < 10; ++i)
    {
        st.Erase(v[i]);
    }
    for (SearchTree<int>::iterator it = st.Last(); it != st.End(); --it)
        cout << *it << " ";
    cout << endl;

    for (SearchTree<int>::iterator it = st1.First(); it != st1.End(); ++it)
        cout << *it << " ";
    cout << endl;

    const int n = st.Size();
    cout << n << endl;
    const int m = st1.Size();
    cout << m << endl;
    st.Clear();
    return 0;
}

运行结果:
这里写图片描述
把插入操作改为不允许插入重复值的版本:

st.Insert(v, 10);

改为:

st.UniqueInsert(v, 10);

在运行,得到结果:
这里写图片描述
可以看到,程序通过了基本的测试,而对于一些比较复杂的测试则不再构造了,本序列博客与程序只为学习算法和数据结构,程序的健壮性还暂不考虑。写这个数据结构最复杂的就是删除操作,参看了四本书:《STL源码剖析》、《算法导论》、《数据结构与算法分析(第二版)》以及严蔚敏版的《数据结构》,个人感觉最后一本讲得最清楚,倒数第二本是用递归实现的,容易懂,但是不是太喜欢。写完了这个结构就可以做许多关于二叉树的题目了,如果不以查找树这种方式构建二叉树,输入就太麻烦了,现在有了这一个类,后面的题目全部自主产权,还能继续测试这个类,真是一举两得。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值