二叉树的基本操作

百度百科:二叉树
刷题是学习数据结构,整理自己的思路最快的方法。所以推荐大家多思考,人人都会敲代码,但是思路不一定每个人都会有。

题目

主要问题

1.求一个结点的父节点
2.求结点的左兄弟结点或右兄弟结点
3.求一颗二叉树的高度
4.求结点i的深度
5.根据一颗二叉树的前序和中序重建一棵二叉树

1.求一个结点的父结点

方法一:我们知道,二叉树的相关操作无非就是,几种遍历方式,然后进相应的操作即可,那么首先想到的就是递归遍历。
如果我当前结点的左孩子或者右孩子等于遍历查找返回的结点,那么那么我们就返回结点。

    Node* _Parent(Node* root ,Node * cTest)
    {
        if (root == NULL)
        {
            return NULL;
        }

        if (root->_pLeft == NULL&&root->_pRight == NULL)
        {
            return NULL;
        }

        if (root->_pLeft == cTest||root->_pRight==cTest)
        {
            return root;
        }
        Node* parent= _Parent(root->_pLeft, cTest);
        if (parent)
        {
            return  parent;
        }
        return _Parent(root->_pRight,cTest);
    }

方法二:跟父节点有关系的,我们还可以想到层序遍历,如果左右孩子不为空,就将其左右孩子结点入队。在入之前我们可以进行判断,如果相等,我们就将其父结点返回.

    Node* ParentNode(Node * node)
    {
        if (node == NULL || _pRoot == node)
        {
            return NULL;
        }

        int max = 50;
        int front, rear;
        Node* current, *child;
        int count = 0;
        if (node != NULL)
        {
            Node*  queue[max];
            front = rear = 0;
            queue[rear++] = node;
            count++;
            while (count != 0)
            {
                current = queue[front];
                if (current->_pLeft)
                {
                    child = current->_pLeft;
                    if (node == child)
                    {
                        return current;
                    }
                    else
                    {
                        //入队
                        queue[rear] = child;
                        rear = (rear + 1)%max;//循环队列实现方式
                        count++;
                    }
                }

                if (current->_pRight)
                {
                    child = current->_pRight;
                    if (child == node)
                    {
                        return current;
                    }
                    else
                    {
                        queue[rear] = child;
                        rear = (rear + 1) % max;
                        count++;

                    }

                }

                front = (front + 1) % max;//出队
                count--;

            }
        }
        return NULL;
    }
2.求结点的左兄弟结点或右兄弟结点

思路:找到父节点后(第一题),其兄弟结点很容易知道,不再累赘。

3.求一颗二叉树的高度

**两种实现方式:
思路1.递归法**
思路:先求其左子树的高度,再求其右子树的高度,取其较高者,就是该树的高度

    size_t _Height(Node* root)
    {
        //左右子树最深的个数加1就是当前树的深度
        if (root == NULL)
        {
            return 0;
        }

        size_t left = _Height(root->_pLeft);
        size_t right = _Height(root->_pRight);
        return left > right ? left + 1 : right + 1;
    }

思路二:遍历法
一直走到根节点,更新最大长度,循环结束后,即可得该树的高度。

template<typename DataType>
size_t BinaryTree<DataType>::getDepth(int numberofNodes)
{
    BinaryTreeNode<DataType>* nodeStack = new BinaryTreeNode<DataType>[numberofNodes];//设置存储二叉树结点的栈
    int depthStak[numberofNodes];//声明深度栈
    int currentDepth, maxDepth = 0;
    int top = -1;
    BinaryTreeNode<DataType>*node = root;

    if (node != NULL)
    {
        currentDepth = 1;//高度从0开始计数
        do
        {
            while (node != NULL)
            {
                nodeStack[++top] = node;//M模拟栈这样的使用top,当取的时候非常方便
                node = node->lChild;
                depthStack[top] = currentDepth;
                currentDepth++;
            }

            node = nodeStack[top];
            currentDepth = depthStack[top--];//出栈,并且取出上一步的高度
            if (node->lChild == NULL&&node->rChild == NULL)
            {
                if (currentDepth > maxDepth)
                {
                    maxDepth = currentDepth;
                }
            }
            node = node->rChild;
            currentDepth++;

        } while (!(node == NULL&&top == -1));
    }       
    return maxDepth;
}
4.求结点i的深度

思路:纵观这些题,我们可以知道无非就是就是遍历只是操作和返回的东西不一样,根据栈的性质进行前进或者回溯不断缩小问题规模。所以就会有两种方法:递归和非递归
这里为了更好的理解:后序遍历的非递归方式,这里我们用后序遍历来解决这个问题,注意我们的flagStack.就是来区分前中后序遍历的。

template<typename DataType>
int BinaryTree<DataType>::getDepthofNode(DataType i)
{
    int stackSize = 0;
    BinaryTreeNode<DataType>* nodeStack[stackSize];
    int flagStack[StackSize];//存储每个结点的状态
    int flag = 0;
    int top = -1;

    BinaryTreeNode<DataType>* node = root;

    do{
        while (node != NULL)
        {
            nodeStack[++top] = node;
            flagStack[top] = 0;
            node = node->_pLeft;
        }

        node = nodeStack[top];
        flag = flagStack[top--];

        if (flag = 0)
        {
            nodeStack[++top] = node;//将第一次访问的结点再次入栈
            flagStack[top] = 1;
            node = node->rChild;
        }
        else
        {
            if (node->_data == i)
            {
                return top + 2;
            }
            node = NULL;
        }
    } while (!(node==NULL&&top==-1))
}
5.根据一颗二叉树的前序和中序重建一棵二叉树

根据二叉树遍历的性质,我们可以知道前序的第一个结点是根结点,据此,我们就可以把中序遍历的结果分为两份,一份左半子树,一份右半子树。

void ReBuild(char*pPreOrder, char* pInorder, int nTreeLen, Node**pRoot)
{
    //检查边界
    if (pPreOrder == NULL || pInOrder == NULL)
    {
        return;
    }
    Node* temp = new Node;
    temp->data = *pPreOrder;

    if (pRoot == NULL)
    {
        *pRoot = temp;
    }

    if (nTreeLen == 1)
    {
        return;
    }
    char* pOrgInOrder = pInOrder;
    char* pLeftEnd = pInOrder;

    int nTempLen = 0;
    while (*pPreOrder != *pLeftEnd)
    {
        if (pPreOrder == NULL || pLeftEnd == NULL)
            return;
        nTempLen++;
        if (nTempLen > nTreeLen)break;
        pLeftEnd++;
    }

    int nLeftLen = (int)(pLeftEnd - pOrgInOrder);
    int nRightLen = nTreeLen - nLeftLen - 1;

    if (nLeftLen > 0)
    {
        ReBuild(pPreOrder + 1, pInOrder, nLeftLen, &((*pRoot)->left));
    }
    if(nRightLen > 0)
    {
        ReBuild(pPreOrder + nLeftLen + 1, pInOrder + nLeftLen + 1, nRightLen, &((*pRoot)->right));
    }
}

其余基本操作完整代码:

#include<iostream>
#include<queue>
#include<assert.h>
using namespace std;

//二叉树的构建、销毁、赋值,拷贝构造。、遍历、修改、查找、


template<typename T>
struct BinaryTreeNode
{
    BinaryTreeNode<T>(const T&data=T())
    : _data(data)
    , _pLeft(NULL)
    , _pRight(NULL)
    {}
    T _data;
    BinaryTreeNode<T>* _pLeft;
    BinaryTreeNode<T>*_pRight;
};

template<typename T>
class BinaryTree
{
    typedef BinaryTreeNode<T> Node;
public:
    BinaryTree()
        :_pRoot(NULL)
        , _invalid(T())
    {
    }
    BinaryTree(const T arr[], const size_t length,const T& invalid)
    {
        size_t index = 0;
        _pRoot = _CreateTree(_pRoot, arr, length, index,invalid);
    }
    //前序遍历
    void PreOrder()
    {
        _PreOrder(_pRoot);
    }
    void InOrder()
    {
        _InOrder(_pRoot);
    }
    void MidOrder()
    {
        _MidOrder(_pRoot);

    }
    void LevelOrder()
    {
    //层序遍历

        //利用队列实现因为需要先入的先访问,如果按照栈的特点来做,访问左孩子后,为了方便访问下一层,需要保存它的左右孩子,无法访问按层序访问右孩子,因为已经插上来了
        //所以我们使用队列,就可以满足这一点

        //只需要一个循环就能实现
        if (_pRoot != NULL)
        {
            queue<Node*>q;
            q.push(_pRoot);

            while (!q.empty())
            {
                Node*cur = q.front();
                q.pop();
                cout << cur->_data<<" ";
                if (cur->_pLeft)
                {
                    q.push(cur->_pLeft);
                }
                if (cur->_pRight)
                {
                    q.push(cur->_pRight);
                }
            }
        }
    }


    //拷贝构造函数
    BinaryTree(const BinaryTree<T>& Bt)
    {
        //根据指针拷贝对象
        _pRoot = _Copy(Bt._pRoot);
    }


    //赋值函数
    BinaryTree<T>* operator =(BinaryTree<T>Bt)
    {
        //基本框架一样    或者采用新赋值方法,交换两者的空间。然后函数结束,销毁临时对象
        if (_pRoot != &Bt._pRoot)
        {
            //拷贝
            BinaryTree<T> temp(Bt);

            Destory(_pRoot);
            //释放新空间

            _pRoot = temp._pRoot;

        }
        return *this;

    }

    //查找

    Node* Find(const T& t)
    {
        return _Find(_pRoot, t);

    }


    //找出该节点的父亲结点
    //思路:

    //求叶子节点的个数


    Node* Parent(Node* cTest)
    {
        return _Parent(_pRoot,cTest);
    }
    Node* _Parent(Node* root ,Node * cTest)
    {
        if (root == NULL)
        {
            return NULL;
        }

        if (root->_pLeft == NULL&&root->_pRight == NULL)
        {
            return NULL;
        }

        if (root->_pLeft == cTest||root->_pRight==cTest)
        {
            return root;
        }
        Node* parent= _Parent(root->_pLeft, cTest);
        if (parent)
        {
            return  parent;
        }
        return _Parent(root->_pRight,cTest);
    }


    //求一个节点的父亲结点—方法二
    //利用层序遍历(这一次我们自己实现一个队列)的思路如果结点等于其左子树或者右子树,将其返回
    Node* ParentNode(Node * node)
    {
        if (node == NULL || _pRoot == node)
        {
            return NULL;
        }

        int max = 50;
        int front, rear;
        Node* current, *child;
        int count = 0;
        if (node != NULL)
        {
            Node  queue[max];
            front = rear = 0;
            queue[rear++] = node;
            count++;
            while (count != 0)
            {
                current = queue[front];
                if (current->_pLeft)
                {
                    child = current->_pLeft;
                    if (node == child)
                    {
                        return current;
                    }
                    else
                    {
                        //入队
                        queue[rear] = child;
                        rear = (rear + 1)%max;//循环队列实现方式
                        count++;
                    }
                }

                if (current->_pRight)
                {
                    child = current->_pRight;
                    if (child == node)
                    {
                        return current;
                    }
                    else
                    {
                        queue[rear] = child;
                        rear = (rear + 1) % max;
                        count++;

                    }

                }

                front = (front + 1) % max;
                count--;

            }
        }
        return NULL;
    }


    //求数的高度
    size_t Height()
    {
        return _Height(_pRoot);
    }



    //节点个数

    size_t size()
    {
     size_t count = 0;
     _size(_pRoot,count);
     return count;
    }

    //叶子节点个数
    size_t leaf()
    {
        size_t count = 0;
        _leaf(_pRoot,count);
        return count;
    }


    ~BinaryTree()
    {
    //销毁函数
        DestoryTree(_pRoot);
        _pRoot = NULL;

    }


protected:
    //递归创建是一个二叉树
    //Node*_CreateTree(Node* pRoot, const T arr[], const size_t length, size_t & index,const T &invalid)
    //{
    //  if (pRoot == NULL)
    //  {
    //      if (index < length&&arr[index] != invalid)
    //      {
    //          pRoot = new Node(arr[index]);
    //      }
    //  }
    //  if (pRoot)
    //  {
    //      _CreateTree(pRoot->_pLeft, arr, length, ++index, invalid);
    //      _CreateTree(pRoot->_pRight, arr, length, ++index, invalid);
    //  }
    //  return pRoot;
    //}


    Node* _CreateTree(Node* &pRoot,const T arr[],const size_t length,size_t&index,const T&invalid)
    {
        Node* temp = NULL;
        if (index<length&&arr[index]!=invalid)//pRoot为空不能作为遍历结束条件
        {
            //先对根进行操作
            temp = new Node(arr[index]);
            pRoot = temp;

            _CreateTree(pRoot->_pLeft, arr, length, ++index, invalid);//在遍历左子树
            _CreateTree(pRoot->_pRight, arr, length, ++index, invalid);//在遍历右子树
        }
        return temp;

    }

    //四种遍历:前中后。层
    void _PreOrder(Node* pRoot)
    {

        if (pRoot)
        {
            cout << pRoot->_data << " ";
            _PreOrder(pRoot->_pLeft);
            _PreOrder(pRoot->_pRight);
        }
    }
    void _InOrder(Node* pRoot)
    {
        if (pRoot)
        {
            _InOrder(pRoot->_pLeft);
            _InOrder(pRoot->_pRight);
            cout << pRoot->_data << " ";
        }
    }
    void _MidOrder(Node* pRoot)
    {
        if (pRoot)
        {
            _PreOrder(pRoot->_pLeft);
            cout << pRoot->_data << " " << endl;
            _PreOrder(pRoot->_pRight);
        }       
    }

    //拷贝构造函数
    //Node* _Copy(Node* node)
    //{
    //  Node* cur = node;
    //  Node* root = NULL;
    //  if (cur)
    //  {
    //      root = new Node(cur->_data);
    //      root->_pLeft = _Copy(cur->_pLeft);
    //      root->_pRight = _Copy(cur->_pRight);
    //  }
    //  return root;
    //
    //}


    Node* _Copy(Node* node)
    {
        Node* root = NULL;
        if (node)
        {
            root = new Node(node->_data);
            root->_pLeft = _Copy(node->_pLeft);
            root->_pRight = _Copy(node->_pRight);

        }
        return root;
    }

    Node* _Find(Node* root, const T & t)
    {
        if (root)
        {
            if (root->_data == t)
            {
                return root;
            }

            Node* ret = _Find(root->_pLeft, t);      //此处不应该如此,因为一直递归到最深处,假如没有找到,肯定函数返回为NULL,整个函数结束
            if (ret)
            {
                return ret;
            }

            return _Find(root->_pRight, t);

        }
        return root;
    }

    size_t _Height(Node* root)
    {
        //左右子树最深的个数加1就是当前树的深度
        if (root == NULL)
        {
            return 0;
        }

        size_t left = _Height(root->_pLeft);
        size_t right = _Height(root->_pRight);
        return left > right ? left + 1 : right + 1;
    }

    void _size(Node* root,size_t & count)
    {
        if (root)
        {
            if (root->_pLeft == NULL&&root->_pRight == NULL)
            {
                return;
            }
            _size(root->_pLeft, ++count);
            _size(root->_pRight, ++count);
        }
    }

    size_t _leaf(Node* root,size_t &count)
    {
        if (root == NULL)
        {
            return 0;
        }

        if (root->_pLeft == NULL&&root->_pRight == NULL)
        {
            count++;
        }
        _leaf(root->_pLeft, count);
        _leaf(root->_pRight, count);
    }

    //销毁函数
    void DestoryTree(Node*pRoot)
    {
        if (pRoot)
        {
            DestoryTree(pRoot->_pLeft);
            DestoryTree(pRoot->_pRight);
            delete pRoot;
            pRoot = NULL;
        }

    }

private:
    Node* _pRoot;
    T _invalid;

};


void Test()
{
    int arr[] = { 1,2, 4, '#', '#', '#', 3, 5, '#', '#', 6 };

    BinaryTree<int> Bt(arr,sizeof(arr)/sizeof(arr[0]),'#');
    //Bt.MidOrder();
    //Bt.InOrder();
    //Bt.PreOrder();
    //Bt.LevelOrder();

    /*BinaryTree<int> Bt1(Bt);
    Bt1.PreOrder();
    cout << endl;
    BinaryTreeNode<int>* cur = Bt1.Find(6);
    cout << cur->_data << " ";*/


    BinaryTreeNode<int>*  cur = Bt.Find(6);
    BinaryTreeNode<int>*parent = Bt.Parent(cur);
    cout << parent->_data << endl;

    cout << "size::" << Bt.size() << " " << "Height::" << Bt.Height() << " Leaf::" << Bt.leaf() << endl;


}

int main()
{

    Test();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值