题目概述
小明有一颗n个节点的二叉排序树,他想知道这棵树的最长的支的长度。一棵树的支表示为:
1)左支。选择某节点为起始,移动到其左儿子,再以左儿子为其起始移动到左儿子的左儿子。重复进行,当某个节点无左儿子时结束,其递归次数为左支的长度。
2)右支。定义类似于左支。
一棵二叉树中的最长支指树中长度最长的左支或右支。
给你一个长度为n的序列,请你按照这个序列的顺序插入初始为空的二叉排序树中。要求每次插入后仍满足二叉排序树的定义。
如有二叉排序树为:
3
/ \
1 4
向其插入2后变为:
3
/ \
1 4
\
2
请你告诉小明这棵二叉排序树最长的支有多长,并告诉他最长的支的个数。
输入描述:
第一行为一个n,表示二叉树的节点数。
第二行为n个不同的正整数:
。
示例一:
输入:
6
5 2 6 3 1 4
输出:
2 2
说明:
5
/ \
2 6
/ \
1 3
\
4
解题思路
方法一:建树法
我们按照题目的意思来,首先构造一棵二叉搜索树(Binary Search Tree)。建树的代价为O(n log n)。在建树的过程中,对每个节点,维护两个值L_len和R_len,分别表示以该节点为分支的最后一个节点时,该分支作为左支及右支的长度。同时,维护一个记录当前遇到的最长的分支长度cur_len以及对应数目cur_num。假设现在插入节点,其父节点为
,则
的各个变量的值为:
随后更新全局分支长度及对应的数目即可。
方法二:动态规划
分析一下最长的左右支的特点,发现:最长的左支,就是原数组中的最长递增子序列;最长的右支,就是最长递减子序列。因此这个问题就等价于回答最长递增/减子序列的数目和长度。这个问题的时间复杂度为O(n log n)。
时间复杂度O()的动态规划做法:
维护一维数组dp,dp[i]表示以A[i]元素结尾的最长递增子序列的长度。
递推公式为:
在更新数组长度时,同时维护一个count数组,用于统计以A[i]结尾的最长子序列的数目。
如何将时间复杂度降为O(n log n)呢?
这里可以参照最长递增子序列的解法。
示例代码
方法一:
#include<iostream>
#include<math.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class ListNode
{
public:
int val, L_len, R_len;
ListNode *L_node, *R_node;
ListNode(int x) { val = x; L_len = 1; R_len = 1; L_node = NULL; R_node = NULL; }
}*LN;
int main()
{
int N;
cin >> N;
int *arr = new int[N];
for (int i = 0; i < N; ++i)
cin >> arr[i];
ListNode *root, *p;
root = &ListNode(arr[0]);
vector<ListNode*> lists;
for (int i = 1; i < N; ++i)
{
ListNode* t = (ListNode*)malloc(sizeof(ListNode));
t->val = arr[i];
t->L_len = t->R_len = 1;
t->L_node = t->R_node = NULL;
lists.push_back(t);
}
int cur_len = 1, cur_num = 1;
for (int i = 1; i < N; ++i)
{
p = lists[i - 1];
ListNode *cur_p = root;
while (1)
{
if (cur_p->val > p->val)
{
if (cur_p->L_node == NULL)
{
cur_p->L_node = p;
p->L_len = cur_p->L_len + 1;
if (p->L_len > cur_len)
{
cur_len = p->L_len;
cur_num = 1;
}
else if (p->L_len == cur_len)
cur_num++;
break;
}
else
cur_p = cur_p->L_node;
}
else
{
if (cur_p->R_node == NULL)
{
cur_p->R_node = p;
p->R_len = cur_p->R_len + 1;
if (p->R_len > cur_len)
{
cur_len = p->R_len;
cur_num = 1;
}
else if (p->R_len == cur_len)
cur_num++;
break;
}
else
cur_p = cur_p->R_node;
}
}
}
cout << cur_len - 1 << ' ' << cur_num << endl;
return 0;
}
需要注意的是,建树的时候每个节点都需要提前申请空间。