本文共 3614 字,大约阅读时间需要 12 分钟。
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
例如,输入数组{ 5,7,6,9,11,10,8},则返回true,因为这个整数序列是图 4.6二叉搜索树的后续遍历结果。如果输入的数组是{7,4,6,5},由于没有那颗二叉搜索树的后序遍历的结果是这个序列,因此返回false。
在后序遍历得到的序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:第一部分是左子树结点的值,它们都比根节点的值小;第二部分是右子树结点的值,它们都比根节点的值大。
以数组{5,7,6,9,11,10,8 } 为例,后序遍历结果的最后一个数字8就是根节点的值。在这个数组中,前3个数字5、7和6都比8小,是值为8的结点的左子树结点:后3个数字9、11和10 都比8大,是值为8的结点的右子树结点。
接下来用同样的方法确定与数组每一部分对应的子树的结构。这其实就是一个递归的过程。对于序列5、7、6,最后一个数字6是左子树的根节点的值。数字5比6小,是值为6的结点的左子结点,而7则是它的右子结点。同样,在序列9、11、10中,最后一个数字10是右子树的根节点。
再来分析另一个整数数组{ 7,4,6,5}。后序遍历的最后一个数是根节点,因此根节点的值是5.由于第一个数字7大于5,因此在对应的二叉搜索树中,根节点上是没有左子树的,数字7、4和6都是右子树结点的值,但我们发现在右子树中有一个结点的值是4,比根节点的值5小,这违背了二叉搜索树的定义。因此不存在一颗二叉搜索树,它的后序遍历的结果是7、4、6、5.
(1)功能测试(输入的后序遍历的序列对应一棵二叉树,包括完全二叉树、所有结点都没有左/右子树的二叉树、只有一个结点的二叉树:输入的后序遍历的序列没有对应一棵二叉树)
(2)特殊输入测试(指向后序遍历序列的指针为NULL指针)
// SquenceOfBST.cpp : Defines the entry point for the console application.//// 《剑指Offer——名企面试官精讲典型编程题》代码// 著作权所有者:何海涛#include "stdafx.h"// BST:Binary Search Tree,二叉搜索树bool VerifySquenceOfBST(int sequence[], int length){ if(sequence == NULL || length <= 0) return false; int root = sequence[length - 1]; // 在二叉搜索树中左子树的结点小于根结点 int i = 0; for(; i < length - 1; ++ i) { if(sequence[i] > root) break; } // 在二叉搜索树中右子树的结点大于根结点 int j = i; for(; j < length - 1; ++ j) { if(sequence[j] < root) return false; } // 判断左子树是不是二叉搜索树 bool left = true; if(i > 0) left = VerifySquenceOfBST(sequence, i); // 判断右子树是不是二叉搜索树 bool right = true; if(i < length - 1) right = VerifySquenceOfBST(sequence + i, length - i - 1); return (left && right);}// ====================测试代码====================void Test(char* testName, int sequence[], int length, bool expected){ if(testName != NULL) printf("%s begins: ", testName); if(VerifySquenceOfBST(sequence, length) == expected) printf("passed.\n"); else printf("failed.\n");}// 10// / \// 6 14// /\ /\// 4 8 12 16void Test1(){ int data[] = { 4, 8, 6, 12, 16, 14, 10}; Test("Test1", data, sizeof(data)/sizeof(int), true);}// 5// / \// 4 7// /// 6void Test2(){ int data[] = { 4, 6, 7, 5}; Test("Test2", data, sizeof(data)/sizeof(int), true);}// 5// /// 4// /// 3// /// 2// /// 1void Test3(){ int data[] = { 1, 2, 3, 4, 5}; Test("Test3", data, sizeof(data)/sizeof(int), true);}// 1// \// 2// \// 3// \// 4// \// 5void Test4(){ int data[] = { 5, 4, 3, 2, 1}; Test("Test4", data, sizeof(data)/sizeof(int), true);}// 树中只有1个结点void Test5(){ int data[] = { 5}; Test("Test5", data, sizeof(data)/sizeof(int), true);}void Test6(){ int data[] = { 7, 4, 6, 5}; Test("Test6", data, sizeof(data)/sizeof(int), false);}void Test7(){ int data[] = { 4, 6, 12, 8, 16, 14, 10}; Test("Test7", data, sizeof(data)/sizeof(int), false);}void Test8(){ Test("Test8", NULL, 0, false);}int _tmain(int argc, _TCHAR* argv[]){ Test1(); Test2(); Test3(); Test4(); Test5(); Test6(); Test7(); Test8(); return 0;}
(1)考查分析复杂问题的思维能力。能否解决这道题的关键在于应聘者能否找出后序遍历的规律。一旦找到了规律,用递归的代码编码相对而言就简单了。在面试的时候,应聘者可以从一两个例子入手,通过分析具体的例子寻找规律
(2)考查对二叉树后序遍历的理解
输入一个整数数组,判断该数组是不是某二叉搜索树的前序遍历的结果。这和前面问题的后序遍历很类似,只是在前序遍历得到的序列中,第一个数字是根节点的值
如果面试题是要求处理一棵二叉树的遍历序列,我们可以先找到二叉树的根节点,再基于根节点把整棵树的遍历序列拆分成左子树对应的子序列和右子树对应的子序列,接下来再递归地处理这两个子序列。本面试题是应用这个思路,也是应用这个思路。