最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java數據結構之線段樹的原理與實現

 更新時間:2022年06月15日 09:19:50   作者:Carol  
線段樹是一種二叉搜索樹,是用來維護區(qū)間信息的數據結構。本文將利用示例詳細講講Java數據結構中線段樹的原理與實現,需要的可以參考一下

簡介

線段樹是一種二叉搜索樹,是用來維護區(qū)間信息的數據結構??梢栽贠(logN)的時間復雜度內實現單點修改、區(qū)間修改、區(qū)間查詢(區(qū)間求和,求區(qū)間最大值,求區(qū)間最小值)等操作。接下來我以實現區(qū)間求和為例子來講解線段樹(最大值和最小值與求和實現方式幾乎無異),假設存在一個數組[1,4,6,3,9]。

實現思路

從線段樹的定義,我們首先需要定義一個樹節(jié)點,節(jié)點包含區(qū)間和(23),區(qū)間([1-5]),左節(jié)點,右節(jié)點等。(如果要實現求區(qū)間最大值,最小值,則還需包含這些)。然后需要提供構建線段樹,線段樹支持修改節(jié)點操作方法。

節(jié)點定義

@Data
public?static?class?Node?{
? ?//區(qū)間起始下標
? ?private?int?start;
? ?//區(qū)間結尾下標
? ?private?int?end;
? ?//當前區(qū)間和值
? ?private?int?value;
? ?private?Node?left;
? ?private?Node?right;
? ?Node(int?start,?int?end,?int?value) {
? ? ? ?this.start?=?start;
? ? ? ?this.end?=?end;
? ? ? ?this.value?=?value;
? }
}

構建線段樹

因為構建線段樹時候需要計算當前區(qū)間和,所以我們可以先初始化一個前綴和數組,在構建線段樹時候利用下標快速計算出區(qū)間和,同時為了保證每個節(jié)點有一致的操作,初始化一個頭節(jié)點,指向root(這是鏈表樹等常用的簡化操作的方法)

//head 指向線段樹root節(jié)點的指針,使得root節(jié)點與其余節(jié)點操作保持一致
? ?Node?head;
? ?int?size;
? ?List<Integer>?nums;

? ?//前綴和數組,便于構建線段樹時候計算區(qū)間值,用于初次構建輔助
? ?List<Integer>?prefixSum?;
? ?public?void?init(List<Integer>?nums) {
? ? ? ?//初始化一個頭節(jié)點,便于操作
? ? ? ?this.head?=?new?Node(-1,?-1,?-1);
? ? ? ?this.nums?=?nums;
? ??//初始化前綴和數組
? ? ? ?prefixSum?=?new?ArrayList<>(nums.size());
? ? ? ?prefixSum.add(0);
? ? ? ?for?(int?i?=?0;?i?<?nums.size() ;?i++) {
? ? ? ? ? ?prefixSum.add(prefixSum.get(prefixSum.size()?-?1)?+?nums.get(i));
? ? ? }
? ??//構建線段樹
? ? ? ?this.build(1,?nums.size());
? ? ? ?size?=?nums.size();
? }

? ?//構建線段樹
? ?public?void?build(int?start,?int?end) {
? ? ??Node?root?=?new?Node(start,?end,?prefixSum.get(end)?-?prefixSum.get(start?-?1));
? ? ?//將頭節(jié)點右子樹指向root
? ? ??head.right?=?root;
? ? ?//從root開始構建線段樹
? ? ??this.madeChild(root,?start,?end);
? }
private?void?madeChild(Node?node,?int?start,?int?end) {
? ? ? ?if?(start?>=?end) {
? ? ? ? ? ?return;
? ? ? }
? ? ? ?//分個左右子樹,左子樹取start~mid,右子樹取mid+1~end
? ? ? ?int?mid?=?start?+?((end?-?start)?>>?1);
? ? ? ?if?(start?<=?mid) {
? ? ? ? ? ?Node?left?=?new?Node(start,?mid,?prefixSum.get(mid)?-?prefixSum.get(start?-?1));
? ? ? ? ? ?node.left?=?left;
? ? ? ? ? ?madeChild(left,?start,?mid);
? ? ? }
? ? ? ?if?(mid?+?1?<=?end) {
? ? ? ? ? ?Node?right?=?new?Node(mid?+?1,?end,?prefixSum.get(end)?-?prefixSum.get(mid));
? ? ? ? ? ?node.right?=?right;
? ? ? ? ? ?madeChild(right,?mid?+?1,?end);
? ? ? }
? }

求解區(qū)間和

求解區(qū)間和過程就是遍歷線段樹,將求解區(qū)間與當前節(jié)點區(qū)間進行比較,如果全部存在于左子樹或者右子樹,則直接深度繼續(xù)在左子樹右子樹遍歷即可,但是如果求解區(qū)間在當前節(jié)點的左右子樹均有部分,則需要將當前區(qū)間分為兩個部分,然后分別深度遍歷左右子樹,最后將結果相加。

//求解區(qū)間和
? ?public?int?findSectionSum(int?start,?int?end) {
? ? ? ?//深度遍歷線段樹,找到對應區(qū)間
? ? ? ?if?(start?<?1?||?end?>?size?||?start?>?end) {
? ? ? ? ? ?return?-1;
? ? ? }
? ? ? ?return?dfsFindSectionSum(head.right,?start,?end);
? } ? ?
/**
? ??* 深度遍歷線段樹結構,分為三種情況
? ??* 1.區(qū)間在當前節(jié)點的左子樹中
? ??* 2.區(qū)間在當前節(jié)點的右子樹中
? ??* 3.左子樹中一部分,右子樹中一部分
? ??* @param node
? ??* @param start
? ??* @param end
? ??* @return
? ??*/
? ?private?int?dfsFindSectionSum(Node?node,?int?start,?int?end) {
? ? ? ?if?(node.start?==?start?&&?node.end?==?end) {
? ? ? ? ? ?//找到區(qū)間
? ? ? ? ? ?return?node.value;
? ? ? }
? ? ? ?if?(this.isContain(node.left.start,?node.left.end,?start,?end)) {
? ? ? ? ? ?//在左子樹中
? ? ? ? ? ?return?this.dfsFindSectionSum(node.left,?start,?end);
? ? ? }
? ? ? ?if?(this.isContain(node.right.start,?node.right.end,?start,?end)) {
? ? ? ? ? ?//包含在右子樹中
? ? ? ? ? ?return?this.dfsFindSectionSum(node.right,?start,?end);
? ? ? }
? ? ? ?//左邊一部分,右邊一部分
? ? ? ?return?this.dfsFindSectionSum(node.left,?start,?node.left.end)?+?this.dfsFindSectionSum(node.right,?node.right.start,?end);
? }
? ?/**
? ??* 判斷區(qū)間[start2, end2]是否包含在[start1, end1]中
? ??* @param start1
? ??* @param end1
? ??* @param start2
? ??* @param end2
? ??* @return
? ??*/
? ?private?boolean?isContain(int?start1,?int?end1,?int?start2,?int?end2){
? ? ? ?return?start2?>=?start1?&&?end2?<=?end1;
? }

更新線段樹

當更新指定位置元素的值的時候,我們需要將線段樹中區(qū)間包含該節(jié)點的區(qū)間和進行更新。我們可以從根節(jié)點開始深度遍歷線段樹,如果當前節(jié)點包含該位置,我們更新區(qū)間和,然后根據當前節(jié)點左右子節(jié)點的區(qū)間,判斷走左子樹還是右子樹,直至更新到葉子節(jié)點,則更新完成。

//更新線段樹,將index位置的值更新為value,需要更新沿路的值
? ?public?void?update(int?index,?int?value) {
? ? ? ?Node?root?=?head.right;
? ? ? ?while?(null?!=?root) {
? ? ? ? ? ?if?(index?>=?root.start?&&?index?<=?root.end) {
? ? ? ? ? ? ? ?root.value?+=?value?-?nums.get(index?-?1);
? ? ? ? ? }
? ? ? ? ? ?int?mid?=?root.start?+?((root.end?-?root.start)?>>?1);
? ? ? ? ? ?if?(index?<=?mid) {
? ? ? ? ? ? ? ?root?=?root.left;
? ? ? ? ? ? ? ?continue;
? ? ? ? ? }
? ? ? ? ? ?root?=?root.right;
? ? ? }
? ? ? ?nums.set(index?-?1,?value);
? }

以上就是Java數據結構之線段樹的原理與實現的詳細內容,更多關于Java 線段樹的資料請關注腳本之家其它相關文章!

相關文章

最新評論

望谟县| 玉林市| 如皋市| 莫力| 大化| 张家川| 定兴县| 莎车县| 根河市| 乾安县| 乌兰察布市| 桃源县| 武汉市| 梅河口市| 峡江县| 桦甸市| 泽普县| 新化县| 阜宁县| 鄂温| 陆丰市| 金坛市| 昌吉市| 许昌市| 汽车| 肇庆市| 赤城县| 兰考县| 民乐县| 东山县| 马关县| 崇礼县| 江陵县| 南华县| 阿拉善盟| 淳化县| 辉南县| 怀化市| 皋兰县| 凤台县| 柘城县|