二叉搜索树的后序遍历序列

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

在这里小鸿为大家科普一下什么是二叉搜索树;
二叉搜索树,也叫二叉排序树、二叉查找树或BST(Binary Search Tree)。二叉搜索树或是一棵空疏,或者是一棵具有下列特性的非空二叉树:
1. 若左子树非空,则左子树上的所有节点的关键字值均小于根节点的关键字的值。 二叉搜索树;
2. 若右子树非空,则右子树上的所有节点的关键字值均大于根节点的关键字的值;
3. 左右子树,本身也是一棵二叉搜索树。
如下图二叉树,就是一个典型的二叉搜索树:
二叉搜索树

例如:输入数组{5,7,6,9,11,10,8},则返回true,因为这个整数序列是下图二叉搜索树的后序遍历序列。如果输入的数组是{7,4,6,5},则由于没有哪棵二叉搜索树的后序遍历结果是这个序列,因此返回false。
二叉搜索树中的完全二叉搜索树
在后序遍历得到的序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:一部分是左子树节点的值,它们都比根节点的值小;一部分是右子树节点的值,它们都比根节点的值大。
以数组{5,7,6,9,11,10,8}为例,后序遍历结果的最后一个数字8就是根节点的值。在这个数组中,前面三个数字,5、6、7都比8小,所以是值为8的节点的左子树节点;后三个数字9、10、11都比8大,是值为8的节点的右子树节点。
接下来用同样的方法确定与数组每一部分对应的子树的结构。这其实就是一个递归过程。对于序列{5,7,6},最后一个数字6是左子树节点根节点的值。数字5比数字6小,所以5是值为6的节点的左子节点,而7则是它的右子节点。同理,对于序列{9,11,10},数字9比10小,所以值为10的节点的左子节点为9,而11则是它的右子节点。
那么再来分析上面提到的第二个数组{7,4,6,5}。后序遍历的最后一个数字是根节点,因此根节点的值是5 。由于第一个数字7大于5,因此在对应的二叉搜索树中,根节点上是没有左子树的,但是可以发现,在7后面有一个数字4小于根节点5,这是不符合二叉搜索树的性质的,所以不存在这样二叉搜索树使它的后序遍历序列为{7,4,6,5}。
得到以上规律之后,有以下参考代码:

using System;

namespace 二叉搜索树的后序遍历序列
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] arr1 = new int[] { 5, 7, 6, 9, 11, 10, 8 };
            int[] arr2 = new int[] { 7, 4, 6, 5 };
            Solution s = new Solution();
            //Test1:数组为某个二叉树的后续遍历序列
            Console.WriteLine("Test1:\n" + s.VerifySquenceOfBST(arr1));
            //Test2:数组不是二叉树的后续遍历序列
            Console.WriteLine("Test2:\n" + s.VerifySquenceOfBST(arr2));
            //Test3:数组为空
            Console.WriteLine("Test3:\n" + s.VerifySquenceOfBST(null));
        }
    }

    class Solution
    {
        public bool VerifySquenceOfBST(int[] sequence)
        {
            //如果数组是空指针或者数组无元素,返回false
            if (sequence == null || sequence.Length < 1)
                return false;
            //数组有元素,检查是否满足后序遍历序列的条件
            return IsPostorder(sequence, 0, sequence.Length - 1);
        }

        /// <summary>
        /// 用于检测数组是否为某个二叉树的后序遍历序列
        /// </summary>
        /// <param name="sequence">检测的数组</param>
        /// <param name="start">数组开始的下标</param>
        /// <param name="end">数组结束的下标</param>
        /// <returns>如果是某个二叉树的后序遍历序列,返回true,否则返回false</returns>
        private bool IsPostorder(int[] sequence, int start, int end)
        {
            //用于储存当前的根节点
            int root = sequence[end];
            //从start位置开始检索,直到遇到比根节点大的节点,在此节点之前的所有节点为该根节点的左子树
            int i = start;
            for (; i < end - 1; i++)
            {
                if (sequence[i] > root)
                    break;
            }
            //从i(比根节点大的第一个数字)开始检索,是否存在小于根节点的节点,若果有,说明不是搜索二叉树
            int j = i;
            for (; j < end - 1; j++)
            {
                if (sequence[j] < root)
                    return false;
            }

            //检查该节点的左子树是否为搜索二叉树
            bool left = true;
            if (i > 0)
                left = IsPostorder(sequence, 0, i - 1);
            //检查该节点的右子树是否为搜索二叉树
            bool right = true;
            if (i < end - 1)
                right = IsPostorder(sequence, i, end - 1);

            //如果该节点的左右子树都是搜索二叉树,返回true,否则返回false
            return left && right;
        }
    }
}

总结:如果面试题要求处理一颗二叉树的遍历序列,则可以先找到二叉树的根节点,再基于根节点把整棵树的遍历序列拆分成左右子树对应的序列,接下来再递归处理这两个子序列。本题和另一个二叉树的题目十分相像“重建二叉树”,都是运用的这种思路。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值