很久没有刷题了,这两天在复习线段树,顺便就记录一下本题的解题过程。
点击这里看题数组
暴力你们应该都想得出来,两重for循环,既然y坐标是按照升序的,那么我只要考虑在当前点以前的点的x坐标是否不大于当前点的x坐标便可,第一重for里面表明是当前点,第二重for是当前点以前的点。bash
若是用暴力的话,对于数据量比较小的时候还能hold得住,可是数据量一大呢,两重for的时间复杂度但是O(n^2),若是我输入的是一万个数呢,那么就要跑10000 * 10000次啊,要是再多点呢?显然这是不行的。那么咱们再思考一下,对于某个点(tx, ty),它的y坐标咱们不用考虑了,对于x坐标,咱们只要找出在它以前出现的点中,哪些点的x坐标不就好了吗,统计出来假设有n个点,那么level[n]++ 便可。因此问题到这边就转换成了
的问题了。熟悉线段树的同窗应该知道线段树就是用来高效解决这类求区间和的问题的,还不了解线段树的点击这里函数
咱们用线段树去维护star的x区间,每次读入一个点,就去更新线段树(这里我把建树和更新放在一块儿操做了),更新完以后就去读取区间和,代码以下:ui
var maxn = 0; //x坐标的边界
var level = [];
var tree = []; //维护x坐标区间
function getStarLevel(stars) {
maxn = 0;
level = [];
for(let i = 0; i < stars.length; i ++)
level[i] = 0;
tree = [];
for(let i = 0; i < stars.length; i ++) {
if(stars[i][0] > maxn)
maxn = stars[i][0];
}
for(let i = 0; i < 4 * maxn; i ++) //线段树的缺点,空间消耗大
tree[i] = 0; //初始化线段树
for(let i = 0; i < stars.length; i ++) {
build_tree(1, 0, maxn, stars[i][0]); //在线建树
level[find(1, 0, maxn, 0, stars[i][0]) - 1] ++; //这边 -1 是不能把自身给算上去
}
return level;
}
/*
* 递归建树
* */
function build_tree(id, left, right, x) {
if(left === right) {
tree[id] ++;
return;
}
let mid = (left + right) >> 1;
if(mid >= x)
build_tree(id << 1, left, mid, x);
else
build_tree(id << 1 | 1, mid + 1, right, x);
tree[id] = tree[id << 1] + tree[id << 1 | 1];
}
function find(id, minX, maxX, left, right) {
if(minX === left && maxX === right)
return tree[id];
let mid = (minX + maxX) >> 1;
if(right <= mid)
return find(id << 1, minX, mid, left, right);
else if(left > mid)
return find(id << 1 | 1, mid + 1, maxX, left, right);
else
return find(id << 1, minX, mid, left, mid) + find(id << 1 | 1, mid + 1, maxX, mid + 1, right);
}
复制代码
这里用个简单的例子来说解一下这段代码,[[1,1], [2,2], [3,1]],首先我是遍历一遍点去拿到x的边界值即maxn=3,也就是说,咱们须要维护的就是一个[0,3]的区间,该树以下图所示spa
线段树虽然能节省时间上的开支,可是也是创建在了开出不少数组的前提下,是一种以空间换时间的解决方案,其实涉及区间和,单点更新的题也能够用树状数组来作,时间复杂度和线段树同样,可是空间上能省出不少来,有兴趣的能够本身去了解一下,本题也能够用树状数组解决.net