数据结构&算法学习笔记 二叉查找树

链表、堆栈和队列都是线性数据结构。树是非线性的、二维的数据结构。树的节点包含两个或更多的链接。二叉树是所有节点都包含两个链接的树。
同一个节点的子节点称为兄弟节点。

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。一颗二叉查找树(没有值相同的节点)有这样的特征:它的任何左子树上的值都小于其父节点的值,而它的任何右子树上的值都大于其父节点的值。注意对应同一组数据的二叉查找树的形状可以不同,这是由这些值被插入树中的顺序决定的。

平衡二叉树(Self-balancing binary search tree)是 一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,同时,平衡二叉树必定是二叉搜索树,反之则不一定。

这里写图片描述

限定先左后右,遍历一棵二叉查找树有三种方法:使用递归的先序遍历、中序遍历、后序遍历这三种算法。
因为每个节点被访问一次,所以时间复杂度O(n)
最坏空间复杂度O(n)(由树的深度决定);

先序遍历定义如下:
若二叉树为空,则空操作;否则
(1)访问根节点
(2)先序遍历左子树
(3)先序遍历右子树
上图的先序遍历结果为:47、25、11、43、31、44、77、65、68

中序遍历定义如下:
若二叉树为空,则空操作;否则
(1)中序遍历左子树
(2)访问根节点
(3)中序遍历右子树
上图的中序遍历结果为:11、25、31、43、44、47、65、68、77

后序序遍历定义如下:
若二叉树为空,则空操作;否则
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根节点
上图的后序遍历结果为:11、31、44、43、25、68、65、77、47

遍历实质上是对二叉树进行线性化的过程,即遍历的结果是将非线性结构的树中的节点排成一个线性序列。

在二叉查找树中寻找匹配的关键值也是很快速的。如果树是平衡的,那么每个分支上大概包含树上的一半节点。每次比较关键值就会排除一半的节点。所以有N个元素的二叉查找树最多需要log2 N次比较就可以确定匹配存不存在。1000个元素的平衡二叉树,最多不超过10次比较(2^10 > 1000); 1000,000个元素的平衡二叉树,最多不超过20次比较(2^20 > 1000,000);

类模板TreeNode
注意:因为TreeNode 的模板参数NODETYPE也是在友元声明中Tree中的模板参数,
所以使用一种特定类型特化的TreeNode只能被相同类型特化的Tree所处理(例如,一个int 类型的Tree树管理储存int值的TreeNode对象)

//TreeNode.h
#ifndef TREENODE_H_INCLUDED
#define TREENODE_H_INCLUDED
template<typename  NODETYPE>class Tree;//声明Tree类

template<typename NODETYPE>
class TreeNode
{
    friend class Tree<NODETYPE>;
public:
    //构造函数
    TreeNode(const NODETYPE& d)
        :leftPtr(NULL),
         data(d),
         rightPtr(NULL)//也可用c++11 nullptr关键字
    {
    }

    NODETYPE getData()const
    {
        return data;
    }

private:
    TreeNode<NODETYPE> *leftPtr;//指向左子树的指针
    NODETYPE data;
    TreeNode<NODETYPE> *rightPtr;//指向右子树的指针
};

#endif // TREENODE_H_INCLUDED

类模板Tree

#ifndef TREE_H_INCLUDED
#define TREE_H_INCLUDED

#include<iostream>
#include"TreeNode.h"

template<typename NODETYPE>class Tree
{
public:
    Tree()
    :rootPtr(NULL)
    {
    }

    void insertNode(const NODETYPE &value)
    {//插入节点到树中
        insertNodeHelper(&rootPtr, value);
        //一个节点只能以叶节点的形式插入到二叉查找树中
    }

    void preOrderTraversal()const
    {//先序遍历
        preOrderHelper(rootPtr);
    }

    void inOrderTraversal()const
    {//中序遍历
        inOrderHelper(rootPtr);
    }

    void postOrderTraversal()const
    {//后序遍历
        postOrderHelper(rootPtr);
    }

private:
    TreeNode<NODETYPE> *rootPtr;//指向根节点指针

    void insertNodeHelper(TreeNode<NODETYPE>**ptr, const NODETYPE &value)
    {//使用二级指针以便于函数修改指针的值
        if(*ptr == NULL)
            *ptr = new TreeNode<NODETYPE>(value); 
            //如果子树是空的,创建一个包含值的新的节点
        else{//子树非空
            if(value < (*ptr) -> data)
                insertNodeHelper(&((*ptr) -> leftPtr), value);
            else
            {
                if(value > (*ptr) -> data)
                    insertNodeHelper(&((*ptr) -> rightPtr), value);
                else
                    std::cout<<value<<" dup"<<std::endl; 
                   /*如果插入的值和根节点的值一样大,那么程序打
                    印"dup"并且返回而没有把这个相同的值插入*/
            }
        }
    }

    //记住,递归要求传递一个指针,表示下一棵要处理的子树
    void preOrderHelper(TreeNode<NODETYPE>*ptr) const
    {//先序遍历
        if(ptr != NULL)
        {
            std::cout<<ptr->data<<' ';//访问根节点
            preOrderHelper(ptr -> leftPtr);//先序遍历左子树
            preOrderHelper(ptr -> rightPtr);//先序遍历右子树
        }
    }

    void inOrderHelper(TreeNode<NODETYPE>*ptr) const
    {//中序遍历
        if(ptr != NULL)
        {
            inOrderHelper(ptr -> leftPtr);//中序遍历左子树
            std::cout<<ptr->data<<' ';//访问根节点
            inOrderHelper(ptr -> rightPtr);//中序遍历右子树
        }
    }

    void postOrderHelper(TreeNode<NODETYPE>*ptr) const
    {//后序遍历
        if(ptr != NULL)
        {
            postOrderHelper(ptr -> leftPtr);//后序序遍历左子树
            postOrderHelper(ptr -> rightPtr);//后序遍历右子树
            std::cout<<ptr->data<<' ';//访问根节点
        }
    }
};
#endif // TREE_H_INCLUDED

测试Tree类模板

#include<iostream>
#include<iomanip>
#include"Tree.h"
using namespace std;
int main()
{
    Tree<int> intTree;//整型树

    cout<<"请输入10个整数"<<endl;

    for(int i = 0; i < 10; i++)
    {
        int intValue= 0;
        cin>>intValue;
        intTree.insertNode(intValue);
    }

    cout<<"\n先序遍历的结果为\n";
    intTree.preOrderTraversal();

    cout<<"\n中序遍历的结果为\n";
    intTree.inOrderTraversal();

    cout<<"\n后序遍历的结果为\n";
    intTree.postOrderTraversal();

    Tree<double> doubleTree;//double型树

    cout<<"\n\n请输入10个浮点数\n"<<endl;

    for(int i = 0; i < 10; i++)
    {
        double doubleValue= 0.0;
        cin>>doubleValue;
        doubleTree.insertNode(doubleValue);
    }

    cout<<"\n先序遍历的结果为\n";
    doubleTree.preOrderTraversal();

    cout<<"\n中序遍历的结果为\n";
    doubleTree.inOrderTraversal();

    cout<<"\n后序遍历的结果为\n";
    doubleTree.postOrderTraversal();
    cout<<endl;
    return 0;
}

运行结果:
这里写图片描述

更新日记:
2018/2/2
二叉树的性质(1):对于任何一棵二叉树,如果其终端节点数为n0, 度为2的节点数为n2,则n0 = n2 + 1.
Proof:
设n1为二叉树T中节点度为1的节点数。
则节点总数n = n0 + n1 + n2; (1)
除根节点外,其余节点都有一个分支进入,设B为分支总数,
则n = B + 1 = n1 + 2*n2 + 1; (2)
由(1)(2)得 n0 = n2 + 1

2018/2/23
由二叉树的先序序列和中序序列,或由其后序序列和中序序列均能唯一地确定一棵二叉树。但是一棵二叉树的先序序列和后序序列不能唯一确定一棵二叉树,因为无法确定左右子树两部分。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值