C和C++程序员面试秘笈:34---如何判断一个二叉树为二叉排序树呢?

一、什么是二叉排序树

  • 详情请参阅:https://blog.csdn.net/qq_41453285/article/details/103963343
  • 二叉排序树,又称二叉搜索树,又称二叉查找树
  • 二叉排序树的定义为:对于给定一棵二叉树,给定一个节点,该节点的左子节点都小于该节点,该节点的所有右子节点都大于该节点,那么这就是一棵二叉排序树
  • 例如,下面就是一棵二叉排序树

C和C++程序员面试秘笈:34---如何判断一个二叉树为二叉排序树呢?_第1张图片

二、如何判断一棵二叉树为二叉排序树

  • 判断一个二叉树为二叉排序树需要借助中序遍历的方法
  • 在前面一篇文章中我们介绍了二叉排序树的中序遍历的非递归方法,请参阅:https://blog.csdn.net/qq_41453285/article/details/107677599
  • 大致思路是:
    • 二叉排序树的中序遍历时,其结果就是所有节点从小到大排序的结果
    • 我们可以借助二叉排序树的非递归中序遍历,使用一个lastValue来记录上一次出队的节点数据
    • 如果lastValue大于或等于当前出队的节点值,则返回false;否则一直入队和出队
    • 如果lastValue始终小于当前出队的节点值,则是升序排列,返回true
  • 代码如下:
// 将二叉排序树的非递归中序遍历代码拿来稍微改一下就行了
template
bool Tree::isSortedTree()
{
    int lastValue = 0;

    std::stack<_TreeNode*> _st;
    
    _TreeNode *p = root;

    // 借助二叉树的非递归的思想来判断二叉树是否为二叉搜索树
    while(p != nullptr || !_st.empty())
    {
        // 将左子树加入到栈中
        while(p != nullptr)
        {
            _st.push(p);
            p = p->left;
        }

        if(!_st.empty())
        {
            p = _st.top(); // 得到栈顶元素
            _st.pop();     // 出栈
 
            // 如果第一次弹出或者lastValue小于当前节点, 给lastValue重新赋值
            if(lastValue == 0 || lastValue < p->data)
                lastValue = p->data;
            // 如果lastValue大于当前节点值, 则返回false
            if(lastValue > p->data)
                return false;

            p = p->right; //指向右子节点, 下一次循环时就会中序遍历右子树
        }
    }

    // 走到这里说明二叉树为二叉搜索树, 返回false
    return true;
}

三、测试代码

  • 下面是一个二叉搜索树的实现代码,其中包含树的中序遍历,和我们的isSortedTree()方法
  • 代码如下:
#include 
#include 

using namespace std;

// 二叉搜索树
template
class Tree
{
private:
    // 树节点
    typedef struct treeNode{
        treeNode(T _data, struct treeNode *_left, struct treeNode *right) : data(_data), left(_left), right(right) { }
        T data;
        struct treeNode *left;
        struct treeNode *right;
    }_TreeNode;
private:
    // 根节点
    _TreeNode *root;
public:
    // 构造函数, 使用数组构造一棵二叉搜索树
    Tree(T arr[], size_t len);

    // 判断该树是否为二叉搜索树
    bool isSortedTree();

    // 非递归中序遍历
    void InOrderTreeUnRec()
    {
        if(root == nullptr)
            return;
        InOrderTreeUnRec(root);
        std::cout << std::endl;
    }
    
private:
    void InOrderTreeUnRec(_TreeNode *node);
private:
    // 将一个节点插入刀二叉搜索树中
    void insertNode(T elem);
};

template
Tree::Tree(T arr[], size_t len)
{
    root = nullptr;

    // 循环将节点插入到二叉搜索树中
    for(int i = 0; i < len; ++i)
        insertNode(arr[i]);
}

template
void Tree::insertNode(T elem)
{
    // 创建一个新的节点
    _TreeNode *newNode = new _TreeNode(elem, nullptr, nullptr);

    // 如果根节点为空, 将其作为根节点
    if(root == nullptr)
    {
        root = newNode;
        return;
    }

    // 指向于根节点
    _TreeNode *step = root, *temp;
    while(step != nullptr)
    {
        // 先记录下这个节点
        temp = step;

        // 如果新节点大于该节点, 向右偏移
        if(newNode->data > step->data)
            step = step->right;
        // 如果新节点小于该节点, 向左偏移
        else if(newNode->data < step->data)
            step = step->left;
    }

    // 偏移完成之后, temp一定指向于尾节点, 然后将新节点与尾节点比较, 将其插入左边还是右边
    if(temp->data > newNode->data)
        temp->left = newNode;
    else
        temp->right = newNode;
}

template
void Tree::InOrderTreeUnRec(_TreeNode *node)
{
    std::stack<_TreeNode*> _st;
    _TreeNode *p = node;
 
    // 如果节点不为空: 说明还有节点可以判断
    // 如果栈不为空: 如果节点为空, 但是栈不为空, 说明要回退了
    while(p != nullptr || !_st.empty())
    {
        // 逐渐将左节点加入到栈中
        while(p != nullptr)
        {
            _st.push(p); //把遍历的节点全部压栈
            p = p->left;
        }
 
        // 栈不为空, 将节点出栈, 然后右节点压入栈
        if(!_st.empty())
        {
            p = _st.top();  // 得到栈顶内容
            _st.pop();      // 出栈
            std::cout << p->data << " ";
            p = p->right;   // 指定右子节点, 下一次循环时就会中序遍历右子树
        }
    }
}


template
bool Tree::isSortedTree()
{
    int lastValue = 0;

    std::stack<_TreeNode*> _st;
    
    _TreeNode *p = root;

    // 借助二叉树的非递归的思想来判断二叉树是否为二叉搜索树
    while(p != nullptr || !_st.empty())
    {
        // 将左子树加入到栈中
        while(p != nullptr)
        {
            _st.push(p);
            p = p->left;
        }

        if(!_st.empty())
        {
            p = _st.top(); // 得到栈顶元素
            _st.pop();     // 出栈
 
            // 如果第一次弹出或者lastValue小于当前节点, 给lastValue重新赋值
            if(lastValue == 0 || lastValue < p->data)
                lastValue = p->data;
            // 如果lastValue大于当前节点值, 则返回false
            if(lastValue > p->data)
                return false;

            p = p->right; //指向右子节点, 下一次循环时就会中序遍历右子树
        }
    }

    // 走到这里说明二叉树为二叉搜索树, 返回false
    return true;
}

int main()
{
    int arr[] = {5, 3, 7, 2, 4, 6, 8, 1};
    Tree *myTree = new Tree(arr, sizeof(arr) / sizeof(int));

    // 非递归中序遍历
    std::cout << "InOrderTreeUnRec: ";
    myTree->InOrderTreeUnRec();
    std::cout << std::endl;

    // 打印判断是否为二叉排序树的结果
    std::cout << boolalpha << myTree->isSortedTree() << std::endl;

    return 0;
}
  • 我们本次测试的代码二叉搜索树如下所示:

C和C++程序员面试秘笈:34---如何判断一个二叉树为二叉排序树呢?_第2张图片

  • 代码运行的效果如下,全部正确:

你可能感兴趣的:(C和C++程序员面试秘笈)