基于C#實現線段樹

一、線段樹

線段樹又稱"區間樹”,在每個節點上保存一個區間,當然區間的劃分采用折半的思想,葉子節點只保存一個值,也叫單元節點,所以最終的構造就是一個平衡的二叉樹,擁有 CURD 的 O(lgN)的時間。
image.png
從圖中我們可以清楚的看到[0-10]被劃分成線段的在樹中的分布情況,針對區間[0-N],最多有 2N 個節點,由于是平衡二叉樹的形式也可以像堆那樣用數組來玩,不過更加耗費空間,為最多 4N 個節點,在針對 RMQ 的問題上,我們常常在每個節點上增加一些 sum,max,min 等變量來記錄求得的累加值,當然你可以理解成動態規劃的思想,由于擁有 logN 的時間,所以在 RMQ 問題上比數組更加優美。

二、代碼

1、在節點中定義一些附加值,方便我們處理 RMQ 問題。

 #region 線段樹的節點/// <summary>/// 線段樹的節點/// </summary>public class Node{/// <summary>/// 區間左端點/// </summary>public int left;/// <summary>/// 區間右端點/// </summary>public int right;/// <summary>/// 左孩子/// </summary>public Node leftchild;/// <summary>/// 右孩子/// </summary>public Node rightchild;/// <summary>/// 節點的sum值/// </summary>public int Sum;/// <summary>/// 節點的Min值/// </summary>public int Min;/// <summary>/// 節點的Max值/// </summary>public int Max;}#endregion

2、構建(Build)
前面我也說了,構建有兩種方法,數組的形式或者鏈的形式,各有特點,我就采用后者,時間為 O(N)。

  #region 根據數組構建“線段樹"/// <summary>/// 根據數組構建“線段樹"/// </summary>/// <param name="length"></param>public Node Build(int[] nums){this.nums = nums;return Build(nodeTree, 0, nums.Length - 1);}#endregion#region 根據數組構建“線段樹"/// <summary>/// 根據數組構建“線段樹"/// </summary>/// <param name="left"></param>/// <param name="right"></param>public Node Build(Node node, int left, int right){//說明已經到根了,當前當前節點的max,sum,min值(回溯時統計上一層節點區間的值)if (left == right){return new Node{left = left,right = right,Max = nums[left],Min = nums[left],Sum = nums[left]};}if (node == null)node = new Node();node.left = left;node.right = right;node.leftchild = Build(node.leftchild, left, (left + right) / 2);node.rightchild = Build(node.rightchild, (left + right) / 2 + 1, right);//統計左右子樹的值(min,max,sum)node.Min = Math.Min(node.leftchild.Min, node.rightchild.Min);node.Max = Math.Max(node.leftchild.Max, node.rightchild.Max);node.Sum = node.leftchild.Sum + node.rightchild.Sum;return node;}#endregion

3、區間查詢
在線段樹中,區間查詢還是有點小麻煩的,存在三種情況。
① 完全包含:也就是節點的線段范圍完全在查詢區間的范圍內,這說明我們要么到了“單元節點",要么到了一個子區間,這種情況就是我找到了查詢區間的某一個子區間,直接累積該區間值就可以了。
② 左交集: 這種情況我們需要到左子樹去遍歷。
③ 右交集: 這種情況我們需要到右子樹去遍歷。
比如說:我要查詢 Sum[4-8]的值,最終會成為:Sum 總=Sum[4-4]+Sum[5-5]+Sum[6-8],時間為 log(N)。

 #region 區間查詢/// <summary>/// 區間查詢(分解)/// </summary>/// <returns></returns>public int Query(int left, int right){int sum = 0;Query(nodeTree, left, right, ref sum);return sum;}/// <summary>/// 區間查詢/// </summary>/// <param name="left">查詢左邊界</param>/// <param name="right">查詢右邊界</param>/// <param name="node">查詢的節點</param>/// <returns></returns>public void Query(Node node, int left, int right, ref int sum){//說明當前節點完全包含在查詢范圍內,兩點:要么是單元節點,要么是子區間if (left <= node.left && right >= node.right){//獲取當前節點的sum值sum += node.Sum;return;}else{//如果當前的left和right 和node的left和right無交集,此時可返回if (node.left > right || node.right < left)return;//找到中間線var middle = (node.left + node.right) / 2;//左孩子有交集if (left <= middle){Query(node.leftchild, left, right, ref sum);}//右孩子有交集if (right >= middle){Query(node.rightchild, left, right, ref sum);}}}#endregion

4、更新操作
這個操作跟樹狀數組中的更新操作一樣,當遞歸的找到待修改的節點后,改完其值然后在當前節點一路回溯,并且在回溯的過程中一路修改父節點的附加值直到根節點,至此我們的操作就完成了,復雜度同樣為 logN。

 #region 更新操作/// <summary>/// 更新操作/// </summary>/// <param name="index"></param>/// <param name="key"></param>public void Update(int index, int key){Update(nodeTree, index, key);}/// <summary>/// 更新操作/// </summary>/// <param name="index"></param>/// <param name="key"></param>public void Update(Node node, int index, int key){if (node == null)return;//取中間值var middle = (node.left + node.right) / 2;//遍歷左子樹if (index >= node.left && index <= middle)Update(node.leftchild, index, key);//遍歷右子樹if (index <= node.right && index >= middle + 1)Update(node.rightchild, index, key);//在回溯的路上一路更改,復雜度為lgNif (index >= node.left && index <= node.right){//說明找到了節點if (node.left == node.right){nums[index] = key;node.Sum = node.Max = node.Min = key;}else{//回溯時統計左右子樹的值(min,max,sum)node.Min = Math.Min(node.leftchild.Min, node.rightchild.Min);node.Max = Math.Max(node.leftchild.Max, node.rightchild.Max);node.Sum = node.leftchild.Sum + node.rightchild.Sum;}}}#endregion

最后我們做個例子,在 2000000 的數組空間中,尋找 200-3000 區間段的 sum 值,看看他的表現如何。

 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;using System.Threading;using System.IO;namespace ConsoleApplication2{public class Program{public static void Main(){int[] nums = new int[200 * 10000];for (int i = 0; i < 10000 * 200; i++){nums[i] = i;}Tree tree = new Tree();//將當前數組構建成 “線段樹”tree.Build(nums);var watch = Stopwatch.StartNew();var sum = tree.Query(200, 3000);watch.Stop();Console.WriteLine("耗費時間:{0}ms,  當前數組有:{1}個數字, 求出Sum=:{2}", watch.ElapsedMilliseconds, nums.Length, sum);Console.Read();}}public class Tree{#region 線段樹的節點/// <summary>/// 線段樹的節點/// </summary>public class Node{/// <summary>/// 區間左端點/// </summary>public int left;/// <summary>/// 區間右端點/// </summary>public int right;/// <summary>/// 左孩子/// </summary>public Node leftchild;/// <summary>/// 右孩子/// </summary>public Node rightchild;/// <summary>/// 節點的sum值/// </summary>public int Sum;/// <summary>/// 節點的Min值/// </summary>public int Min;/// <summary>/// 節點的Max值/// </summary>public int Max;}#endregionNode nodeTree = new Node();int[] nums;#region 根據數組構建“線段樹"/// <summary>/// 根據數組構建“線段樹"/// </summary>/// <param name="length"></param>public Node Build(int[] nums){this.nums = nums;return Build(nodeTree, 0, nums.Length - 1);}#endregion#region 根據數組構建“線段樹"/// <summary>/// 根據數組構建“線段樹"/// </summary>/// <param name="left"></param>/// <param name="right"></param>public Node Build(Node node, int left, int right){//說明已經到根了,當前當前節點的max,sum,min值(回溯時統計上一層節點區間的值)if (left == right){return new Node{left = left,right = right,Max = nums[left],Min = nums[left],Sum = nums[left]};}if (node == null)node = new Node();node.left = left;node.right = right;node.leftchild = Build(node.leftchild, left, (left + right) / 2);node.rightchild = Build(node.rightchild, (left + right) / 2 + 1, right);//統計左右子樹的值(min,max,sum)node.Min = Math.Min(node.leftchild.Min, node.rightchild.Min);node.Max = Math.Max(node.leftchild.Max, node.rightchild.Max);node.Sum = node.leftchild.Sum + node.rightchild.Sum;return node;}#endregion#region 區間查詢/// <summary>/// 區間查詢(分解)/// </summary>/// <returns></returns>public int Query(int left, int right){int sum = 0;Query(nodeTree, left, right, ref sum);return sum;}/// <summary>/// 區間查詢/// </summary>/// <param name="left">查詢左邊界</param>/// <param name="right">查詢右邊界</param>/// <param name="node">查詢的節點</param>/// <returns></returns>public void Query(Node node, int left, int right, ref int sum){//說明當前節點完全包含在查詢范圍內,兩點:要么是單元節點,要么是子區間if (left <= node.left && right >= node.right){//獲取當前節點的sum值sum += node.Sum;return;}else{//如果當前的left和right 和node的left和right無交集,此時可返回if (node.left > right || node.right < left)return;//找到中間線var middle = (node.left + node.right) / 2;//左孩子有交集if (left <= middle){Query(node.leftchild, left, right, ref sum);}//右孩子有交集if (right >= middle){Query(node.rightchild, left, right, ref sum);}}}#endregion#region 更新操作/// <summary>/// 更新操作/// </summary>/// <param name="index"></param>/// <param name="key"></param>public void Update(int index, int key){Update(nodeTree, index, key);}/// <summary>/// 更新操作/// </summary>/// <param name="index"></param>/// <param name="key"></param>public void Update(Node node, int index, int key){if (node == null)return;//取中間值var middle = (node.left + node.right) / 2;//遍歷左子樹if (index >= node.left && index <= middle)Update(node.leftchild, index, key);//遍歷右子樹if (index <= node.right && index >= middle + 1)Update(node.rightchild, index, key);//在回溯的路上一路更改,復雜度為lgNif (index >= node.left && index <= node.right){//說明找到了節點if (node.left == node.right){nums[index] = key;node.Sum = node.Max = node.Min = key;}else{//回溯時統計左右子樹的值(min,max,sum)node.Min = Math.Min(node.leftchild.Min, node.rightchild.Min);node.Max = Math.Max(node.leftchild.Max, node.rightchild.Max);node.Sum = node.leftchild.Sum + node.rightchild.Sum;}}}#endregion}}

image.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/166533.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166533.shtml
英文地址,請注明出處:http://en.pswp.cn/news/166533.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

關于同一接口有多個不同實現的設計方案

關于同一接口有多個不同實現的設計方案 前言 最近公司做了一個銀行相關的項目&#xff0c;告訴我公司對接了多個銀行的支付&#xff0c;每個銀行都有對應的接口要去對接&#xff0c;比如&#xff1a;交易申請&#xff0c;交易取消&#xff0c;支付&#xff0c;回單&#xff0…

rabbitMQ發布確認-交換機不存在或者無法抵達隊列的緩存處理

rabbitMQ在發送消息時&#xff0c;會出現交換機不存在&#xff08;交換機名字寫錯等消息&#xff09;&#xff0c;這種情況如何會退給生產者重新處理&#xff1f;【交換機層】 生產者發送消息時&#xff0c;消息未送達到指定的隊列&#xff0c;如何消息回退&#xff1f; 核心&…

麒麟KYSEC使用方法05-命令設置密碼強度

原文鏈接&#xff1a;麒麟KYSEC使用方法05-命令設置密碼強度 hello&#xff0c;大家好啊&#xff0c;今天給大家帶來麒麟KYLINOS的kysec使用方法系列文章第五篇內容----使用命令設置密碼強度&#xff0c;密碼強度策略有兩個文件需要修改&#xff0c;pwquality.conf/login.defs&…

命令執行總結

之前做了一大堆的題目 都沒有進行總結 現在來總結一下命令執行 我遇到的內容 這里我打算按照過濾進行總結 依據我做過的題目 過濾system 下面是一些常見的命令執行內容 system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec() 反引號 同shell_exec() …

大語言模型概述(三):基于亞馬遜云科技的研究分析與實踐

上期介紹了基于亞馬遜云科技的大語言模型相關研究方向&#xff0c;以及大語言模型的訓練和構建優化。本期將介紹大語言模型訓練在亞馬遜云科技上的最佳實踐。 大語言模型訓練在亞馬遜云科技上的最佳實踐 本章節內容&#xff0c;將重點關注大語言模型在亞馬遜云科技上的最佳訓…

解決Chrome瀏覽器無法啟動,因為應用程序的并行配置不正確

目錄 現象 方法1 方法2 附帶&#xff1a;書簽路徑 一次比較奇怪的問題&#xff0c;花了一些時間&#xff0c;記錄下來。 現象 進到本機默認安裝路徑&#xff1a; C:\Users\你的用戶名\AppData\Local\Google\Chrome\Application 下面會有個版本號的目錄&#xff0c;如我的…

跨地區企業組網方案對比與推薦

跨地區的企業&#xff0c;需要在不同的辦公室之間實現內部通信來進行業務協作。然而&#xff0c;在不同的地方建立局域網并將它們連接起來是一個棘手的問題。傳統的企業組網方案可能會面臨各種挑戰&#xff0c;包括網絡延遲、數據安全性、維護困難等等。 常見的組網方案有&…

快手ConnectionError

因為運行的程序被中斷導致 top然后查看站用處內存高的accelerate kill進程號 9回車

linux基礎5:linux進程1(馮諾依曼體系結構+os管理+進程狀態1)

馮諾依曼體系結構os管理 一.馮諾依曼體系結構&#xff1a;1.簡單介紹&#xff08;準備一&#xff09;2.場景&#xff1a;1.程序的運行&#xff1a;2.登錄qq發送消息&#xff1a; 3.為什么需要內存&#xff1a;1.簡單的引入&#xff1a;2.計算機存儲體系&#xff1a;3.內存的意義…

微服務知識小結

1. SOA、分布式、微服務之間有什么關系和區別&#xff1f; 1.分布式架構指將單體架構中的各個部分拆分&#xff0c;然后部署到不同的機器或進程中去&#xff0c;SOA和微服務基本上都是分布式架構的 2. SOA是一種面向服務的架構&#xff0c;系統的所有服務都注冊在總線上&#…

讓工作效率提升10倍:十大AIGC工具評測【建議收藏】

AI技術的普及已經在近年來不斷增長。這種技術已經改變了我們與電腦的互動方式&#xff0c;讓我們能夠更高效、更自然地完成任務。本文將展示10個基于ChatGPT、GPT-3.5和 GPT-4.0 AI模型構建的最強大的資源&#xff0c;使您更容易充分利用它們的潛力。因此&#xff0c;如果您想利…

詳解深度學習中的圖神經網絡GNN

引言 圖神經網絡GNN是深度學習的一個分支。 深度學習的四個分支對應了四種常見的數據格式&#xff0c;前饋神經網絡FNN處理表格數據&#xff0c;表格數據可以是特征向量&#xff0c;卷積神經網絡CNN處理圖像數據&#xff0c;循環神經網絡RNN處理時序數據&#xff0c;圖神經網…

android的canvas的clipRegion廢棄替代代碼

由于clipRegion的一些問題&#xff0c;導致他被廢棄了&#xff0c;但又有時候會用到&#xff0c;所以寫了一個工具類來替代它 代碼如下 package com.example;import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.g…

c++|類和對象(上)

目錄 一、面向過程和面向對象初步認識 二、類的引入和定義 2.1類的引入 2.2類的定義 三、類的訪問限定符及封裝 3.1訪問限定符 3.2封裝 四、類的作用域 五、類的實例化 六、類的對象大小的計算 6.1如何計算對象的大小 6.2類對象的存儲方式 七、類成員函數的thi…

【Docker】從零開始:7.Docker命令:容器命令及參數詳解

【Docker】從零開始&#xff1a;7.幫助啟動類命令 一、幫助啟動類命令啟動Docker停止Docker重啟Docker查看Docker狀態開機啟動查看docker概要信息查看docker總體幫助文檔查看docker命令幫助文檔 二、鏡像命令列出本地主機上的鏡像運行示例返回說明操作參數 搜索倉庫里的某個鏡像…

Python-Django的“日志功能-日志模塊(logging模塊)-日志輸出”的功能詳解

01-綜述 可以使用Python內置的logging模塊來實現Django項目的日志記錄。 所以與其說這篇文章在講Django的“日志功能-日志模塊-日志輸出”&#xff0c;不如說是在講Pthon的“日志功能-日志模塊-日志輸出”&#xff0c;即Python的logging模塊。 下面用一個實例來進行講解。 …

2023年亞太杯數學建模A題水果采摘機器人的圖像識別功能(免費思路)

中國是世界上最大的蘋果生產國&#xff0c;年產量約為 3500 萬噸。同時&#xff0c;中國也是世界上最大的蘋果出口國&#xff0c;世界上每兩個蘋果中就有一個出口到國。世界上每兩個蘋果中就有一個來自中國&#xff0c;中國出口的蘋果占全球出口量的六分之一以上。來自中國。中…

保護服務器免受攻擊:解析攻擊情境與解決之道

在數字化時代&#xff0c;服務器安全問題日益突出&#xff0c;因為它們是企業和個人網絡活動的核心。服務器被攻擊可能引發一系列問題&#xff0c;理解攻擊的不同情境以及采取相應的解決方法變得至關重要。 DDoS 攻擊&#xff08;分布式拒絕服務攻擊&#xff09; 情境&#xff…

基于51單片機超聲波測距汽車避障系統

**單片機設計介紹&#xff0c; 基于51單片機超聲波測距汽車避障系統 文章目錄 一 概要二、功能設計設計思路 三、 軟件設計原理圖 五、 程序六、 文章目錄 一 概要 基于51單片機的超聲波測距汽車避障系統是一種用于幫助汽車避免碰撞和發生事故的設備&#xff0c;以下是一個基本…