Google 2016 面试题6 | Count of Smaller Numbers After Self(数组计数)

Google 2016 面试题6 | Count of Smaller Numbers After Self(数组计数)


题目描述 

给定一个数组nums,返回一个计数数组count,count[i]表示nums中第i个右边有多少个数小于nums[i]

Example:
nums = [5, 2, 6, 1]
输出[2,1,1,0]


分析解答

此题不难给出O(N^2)的算法,先穷举nums中每个位置i,再穷举右边的数计算有多少个小于nums[i]。难点在于利用数据结构进行优化从而降低时间复杂度。线段树(segment tree)和平衡树(Balanced Binary Tree)是两种可以使用的数据结构。


线段树的每个节点表示一段区间,记录这个区间的某些信息,其基本思想是把区间一分为二,二分为四。。。直到不可再分(因此叶子节点的区间只包含一个数),如此可以把任意区间表示成log(区间大小)个子区间的拼接,以降低查询时间复杂度。在本题中,假设nums中的数字范围在0到maxnum之间,那么建树的区间为[0,maxnum](也就是根节点所表示的区间)。每个节点记录其表示区间内的数字个数。本题涉及两种线段树基本操作:插入和查询。插入操作把nums[i]插入到线段树相应位置,同时对所有经过的区间的sum值进行累加;查询操作需要查询区间[0,nums[i]-1]所包含的数字个数,利用已经建好的线段树把查询区间分割为若干个节点所表示的区间,统计并返回这些节点的sum值之和。


平衡树用途更广,代码复杂度也更高,是一种保持叶子节点深度平衡的二叉搜索树,有多种方法实现,大家可以自行在网上搜索学习。


参考程序:

public class Solution{
	
    class SegmentTreeNode{
	  public int start,end;
	  public int count;
	  public SegmentTreeNode left,right;
	  public SegmentTreeNode(int short,int end,int count){
	   this.start =start;
	   this.end =end;
	   this.count =count;
	   this.left =this.right =null;
	  }
}
SegmentTreeNode root;
public SegmentTreeNode build(int start,int end){
	if(start>end){
		return null;
	}

  SegmentTreeNode root =new SegmentTreeNode(start,end,0);
	if(start!=end){
		int mid =(start+end)/2;
		root.left =build(start,mid);
		root.right =build(mid+1,end);
	} else{
		root.count =0;
	}
	return root;
}
public int querySegmentTree(SegmentTreeNode root,int start,int end){
	if(start==root.start&&root.end==end){
		return root.count;
	}
	int mid =(count.start+root.end)/2;
	int leftcount =0,rightcount =0
	//左子区
	if(start<=mid){
		if(mid<end){
			leftcount =querySegmentTree(root.left,start,mid);
		}else{
			leftcount =querySegmentTree(root.left,start,end);
		}
	}
	//右子区
	if(mid<end){
		if(start<=mid){
			rightcount =querySegmentTree(root.right,mid+1,end);
		}else{
			rightcount =querySegmentTree(root.right,start,end);
		}
	}
	//else就是不相交
	return leftcount+rightcount; 
	
}
public void modifySegmeniTree(SegmentTreeNode root,int index,int value){
	if(root.start ==index&&root.end==index){
		root.count+=value;
		return;
	}
	//查询
	int mid =(root.start+root.end)/2;
	if(root.start<=index&&index<=mid){
		modifySegmeniTree(root.left,index,value);
	}
    if(mid<index&&index<=root.end){
		modifySegmeniTree(root.right,index,value);
	}
	//更新
	root.count==root.left.count+root.right.count;
}
public ArrayList<Integer> countOfSmallerNumberII(int []A){
	root =build(0,10000);
	ArrayList<Integer>ans =new ArrayList<Integer>();
	int res;
	for(int i=0;i<A,length;i++){
		res=0;
	if(A[i]>0){
		res=querySegmentTree(root,0,A[i]-1);
	}
	modifySegmeniTree(root,A[i],1);
	ans.add(res);
	}
   return ans;
  }
}


面试官角度分析

可以先答出O(n^2)的时间复杂度然后面试官沟通后给出小于O(N^2)的算法比如线段树解决的方法可以达到hire的程度。




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值