很久沒有刷題了,這兩天在複習線段樹,順便就記錄一下本題的解題過程。
點擊這裏看題數組
暴力你們應該都想得出來,兩重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
注意每一個節點上面的數字是它的id,每一個節點框裏面的數字表明的是區間,也就是說tree[1]表明的就是區間[0,3]的和,每次的更新操做(這裏我直接把更新和建樹合併了,就是build_tree函數)其實本質上改變的是葉子節點的值,當讀入第一個點[1,1]時,它的x座標是1,咱們從樹根往下遞歸,發現tree[5]表明的就是x=1,因此tree[5] ++,此時tree[5]就是1,以後又會從葉子往上更新,找到tree[5]的父節點,即tree[2],也更新爲了1,再往上,tree[1]也更新爲1,更新完了以後查找在第一個點以前有沒有點的x座標是小於第一個點的x座標即1的,即查詢[0,1]區間的和,代碼仍是從樹根開始找,發現樹根表明的是[0,3]的區間和,二分後遞歸查找左子樹,發現左子樹表明的是[0,1],正好就是該區間,因此之間返回tree[2]節點的值便可,以後的更新查找操做就和第一次的同樣了,這裏就不贅述了。線段樹雖然能節省時間上的開支,可是也是創建在了開出不少數組的前提下,是一種以空間換時間的解決方案,其實涉及區間和,單點更新的題也能夠用樹狀數組來作,時間複雜度和線段樹同樣,可是空間上能省出不少來,有興趣的能夠本身去了解一下,本題也能夠用樹狀數組解決.net