bst 计数

bst 计数

题目描述

相信大家对二叉查找树都很熟悉了,现在给你N个整数的序列,每个整数都在区间[1,N]内,且不重复。现在要你按照给定序列的顺序,建立一个二叉查找树,把第一整数作为根,然后依次插入后面的整数。


每个结点X的插入过程其实就是模拟下面的 insert(X, root)过程:
你要求的是:每次把序列的一个整数插入到二叉查找数后,到目前为止计数类加器C的值是多少?请把它输出。注意:第一次插入根,计数器C的值是0,你可以理解为插入根是不执行insert()操作的,其后每插入一个结点,C都累加,也就是每次进入过程insert( number X, node N ),都会执行increase the counter C by 1,使得C不断增大。

输入格式 1868.in

第一行:一个整数:N,表示序列有多少个整数。 1 <= N <=300000
接下来有N行,每行一个整数X, X在区间[1,N]内,且不重复,这N个整数就组成了一个有序的序列。

输出格式 1868.out

N行,每行一个整数,第一行表示你把序列的第一个数插入到二叉查找树后,当前计数器C的值是多少。 50%的数据:N <= 1000

输入样例 1868.in


   这题在考试的时候没怎么细想,索性就打了一个暴力就交了。所以直接说正解吧。

问题分析:

    从图上分析,2插入在3后面,1插在2后面,发现都是插在它大的最小的数的左边;而4插在3后面,5插在4后面,都是插在比它小的最大的数右边。

    如果还不能看出客观规律,就再画第三个样例看一看。

    :3 5 1 6 8 7 2 4


直接模拟一次看看:

    当5进来的时候,查到比5小的最大的数是3,而比5小的数没有;(连接到3)

    当1进来的时候,查找到当时比1大的最小的数是3,而比1小的数没有;(连接到3)

    当6进来时,查找到比6小的最大的数是5,而比6大的数没有。(连接到5)

    当8进来时,查找到比8小的最大的数是6,而比8大的数没有。(连接到6)

    当7进来的时候,查找到当前比7大的最小的数是8,比7小的最大的数是6。(连接到7)

    当2进来的时候,检测到当前比2大的最小的数是3,比2小的最大的数是1。(连接到1)

    当4进来的时候,检测到当前比4大的最小的数是5,比4小的最大的数是3。(连接到5)

    通过观察上面的规律,会发现,如果只有比它大的数或只有比它小的数,那就直接接上去;如果大与小都能找得到,则接的是深度大的。

    而且我们会发现,在一个数列里面,比数i大的最小数,与比数i小的最大数的坐标是紧紧相连的,它们之间不会有其余的数,是有祖先关系的,即插入数i时,要选择一个深度大的插入。

深度怎么求呢?此时的深度就等于父亲的深度+1,这是必然的,然后ans一直不断累加即可。

具体实现:

    其实求比i大的最小和比i小的最大就是在求i的前驱和后继,在treap的讲义里有提到。

    也是可以用线段树做的,进来一个,便查一下比当前大的最小,和比当前小的最大得到数;如果有一个的值不合法(即没有),那么便取另一个;如果都是合法的,比较两者的深度,取深度较大的连接。

    当然treap必然也是可以的,但我个人对线段树更熟练一些。


代码如下:

#include
     
     
      
      
#include
      
      
       
       
using namespace std;
const int maxn=300005,oo=10000000;
int n,a[maxn];
long long deep[maxn],ans;

struct tre1
{
	int ma;
}tree1[4*maxn];

struct tre2
{
	int mi;
}tree2[4*maxn];

void init()
{
	for(int i=0;i<=4*maxn;i++) 	tree2[i].mi=oo;
}

long long get1(int root,int l,int r,long long s,long long t)
{
	int ans1,ans2;
	if(s<=l&&r<=t) return tree1[root].ma;
	if(s>r||t
       
       
        
        r||t
        
        
         
         x||x>r) return;
	if(l==x&&x==r)
	{
		tree1[root].ma=y;
		return;
	}
	int mid=(l+r)/2;
	update1(root*2,l,mid,x,y);
	update1(root*2+1,mid+1,r,x,y);
	tree1[root].ma=max(tree1[root*2].ma,tree1[root*2+1].ma);
}//修改 

void update2(int root,int l,int r,long long x,long long y)
{
	if(l>x||x>r) return;
	if(l==x&&x==r)
	{
		tree2[root].mi=y;
		return;
	}
	int mid=(l+r)/2;
	update2(root*2,l,mid,x,y);
	update2(root*2+1,mid+1,r,x,y);
	tree2[root].mi=min(tree2[root*2].mi,tree2[root*2+1].mi);
}//修改 

int main()
{
	freopen("1868.in","r",stdin);
	freopen("1868.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n;
	init();
	cin>>a[1];
	update1(1,1,n,a[1],a[1]);
	update2(1,1,n,a[1],a[1]);
	cout<<0<
         
         
           >a[i]; int v=0; int small=get1(1,1,n,1,a[i]-1);//最小的最大 int big=get2(1,1,n,a[i]+1,n);//最大的最小 int now=0; if(small==0) now=deep[big];//deep为深度,如果当没有比它小的数时 else if(big==oo) now=deep[small];//如果没有比它大的数时 else now=max(deep[big],deep[small]);//如果都有则取深度较大的 deep[a[i]]=now+1; update1(1,1,n,a[i],a[i]); update2(1,1,n,a[i],a[i]); ans+=deep[a[i]];//ans不断累加 printf("%lld\n",ans); } return 0; } 
         
        
        
       
       
      
      
     
     


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这道题目需要在二叉搜索树中查找第k大的元素。可以采用中序遍历的方式,将节点的值从小到大添加到数组中,然后返回数组中的倒数第k个元素即为答案。时间复杂度为O(N),空间复杂度为O(N)。也可以利用二叉搜索树的性质,从根节点开始遍历,每个节点记录其右子树的大小,根据大小可以找到第K大的元素。时间复杂度为O(log N),空间复杂度为O(1)。 ### 回答2: BST 是二叉搜索树,它是一种有序的二叉树,其中每个节点都存储一个键值,且左子树的值小于等于当前节点的值,右子树的值大于等于当前节点的值。因此,如果我们要找到 BST 中的第 k 大元素,我们可以利用 BST 的这种有序性质来帮助我们寻找。 一种有效的解决办法是使用中序遍历算法,在遍历的过程中维护一个计数器 count,记录当前已经遍历的节点数,如果 count 等于 k,直接返回当前节点的值即可。因为中序遍历算法遍历的顺序是左-中-右,所以返回的节点值就是第 k 大元素。 以下是实现该算法的例子,我们假设 BST 中不存在相同的值。 ``` class TreeNode: def __init__(self, val=None, left=None, right=None): self.val = val self.left = left self.right = right class Solution: def kthLargest(self, root: TreeNode, k: int) -> int: # 栈用于模拟中序遍历 stack = [] count = 0 # 记录当前已经遍历过的节点数 node = root while node or stack: while node: stack.append(node) node = node.right node = stack.pop() count += 1 if count == k: return node.val node = node.left ``` 该算法时间复杂度为 O(H+k),其中 H 为树的高度,k 为要查找的元素的下标。由于 BST 的性质,树的高度 H 最多为树中节点数 N(最坏情况下,BST 变成了一个链表),因此时间复杂度为 O(N+k)。 ### 回答3: 题目描述: 给定一棵二叉搜索树(BST)和一个整数k,找到其中第k大的元素。 解题思路: 首先,我们可以利用BST的性质,即左子树所有节点的值小于根节点的值,根节点的值小于右子树所有节点的值,来确定BST中某个节点的排名。 具体地,我们可以首先通过BST的中序遍历得到一个按照升序排列的节点值列表,然后按照降序遍历该列表并记录已经遍历的节点个数,当遍历到第k个节点时,就得到了所求的第k大节点的值。 但是,这种方法需要遍历整棵BST,时间复杂度为O(n),其中n是BST中节点的个数。而这题我们要求的是第k大节点,因此我们可以不必遍历整棵BST,可以在遍历BST时维护一个计数变量和一个全局变量,分别记录已经遍历的节点个数和第k大节点的值。 具体地,我们可以利用BST中序遍历得到一个按照升序排列的节点值列表。在遍历列表时,我们从最大的值开始,每次访问一个值,计数器就加1,当计数器达到k时,就求出了第k大节点的值。 代码实现: class Solution: def kthLargest(self, root: TreeNode, k: int) -> int: def inorder(node): if not node: return [] return inorder(node.left) + [node.val] + inorder(node.right) nums = inorder(root) return nums[-k]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值