經典演算法題每日演練 第十二題 線段樹

2021-09-23 21:43:53 字數 4712 閱讀 6912

這一篇我們來看樹狀陣列的加強版線段樹,樹狀陣列能玩的線段樹一樣可以玩,而且能玩的更好,他們在區間求和,最大,平均

等經典的rmq問題上有著對數時間的優越表現。

一:線段樹

線段樹又稱"區間樹」,在每個節點上儲存乙個區間,當然區間的劃分採用折半的思想,葉子節點只儲存乙個值,也叫單元節點,所

以最終的構造就是乙個平衡的二叉樹,擁有curd的o(lgn)的時間。

從圖中我們可以清楚的看到[0-10]被劃分成線段的在樹中的分布情況,針對區間[0-n],最多有2n個節點,由於是平衡二叉樹的形

式也可以像堆那樣用陣列來玩,不過更加耗費空間,為最多4n個節點,在針對rmq的問題上,我們常常在每個節點上增加一些sum,

max,min等變數來記錄求得的累加值,當然你可以理解成動態規劃的思想,由於擁有logn的時間,所以在rmq問題上比陣列更加優美。

二:**

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

#region 線段樹的節點

/// /// 線段樹的節點

///

public class node

#endregion

2:構建(build)

前面我也說了,構建有兩種方法,陣列的形式或者鏈的形式,各有特點,我就採用後者,時間為o(n)。

#region 根據陣列構建「線段樹"

/// /// 根據陣列構建「線段樹"

///

///

public node build(int nums)

#endregion

#region 根據陣列構建「線段樹"

/// /// 根據陣列構建「線段樹"

///

///

///

public node build(node node, int left, int right);}

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 區間查詢

/// /// 區間查詢(分解)

///

///

public int query(int left, int right)

/// /// 區間查詢

///

/// 查詢左邊界

/// 查詢右邊界

/// 查詢的節點

///

public void query(node node, int left, int right, ref int sum)

else

//右孩子有交集

if (right >= middle)}}

#endregion

4:更新操作

這個操作跟樹狀陣列中的更新操作一樣,當遞迴的找到待修改的節點後,改完其值然後在當前節點一路回溯,並且在回溯的過程中一

路修改父節點的附加值直到根節點,至此我們的操作就完成了,複雜度同樣為logn。

#region 更新操作

/// /// 更新操作

///

///

///

public void update(int index, int key)

/// /// 更新操作

///

///

///

public void update(node node, int index, int key)

else}}

#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;

tree tree = new tree();

//將當前陣列構建成 「線段樹」

tree.build(nums);

var watch = stopwatch.startnew();

var sum = tree.query(200, 3000);

watch.stop();

console.read();}}

public class tree

#endregion

node nodetree = new node();

int nums;

#region 根據陣列構建「線段樹"

/// /// 根據陣列構建「線段樹"

///

///

public node build(int nums)

#endregion

#region 根據陣列構建「線段樹"

/// /// 根據陣列構建「線段樹"

///

///

///

public node build(node node, int left, int right);}

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 區間查詢

/// /// 區間查詢(分解)

///

///

public int query(int left, int right)

/// /// 區間查詢

///

/// 查詢左邊界

/// 查詢右邊界

/// 查詢的節點

///

public void query(node node, int left, int right, ref int sum)

else

//右孩子有交集

if (right >= middle)}}

#endregion

#region 更新操作

/// /// 更新操作

///

///

///

public void update(int index, int key)

/// /// 更新操作

經典演算法題每日演練 第十九題 雙端佇列

話說大學的時候老師說妹子比工作重要 工作可以再換,妹子這個。所以。這兩個月也就一直忙著fall in love,嗨,慢慢調整心態吧,這篇就選乙個簡單的資料結構聊一聊,話說有很多資料結構都在玩組合拳,比如說 塊狀鍊錶,塊狀陣列,當然還有本篇的雙端佇列,是的,它就是 棧和佇列的組合體。一 概念 我們知道...

經典演算法題每日演練 第二十二題 奇偶排序

原文 經典演算法題每日演練 第二十二題 奇偶排序 這個專題因為各種原因好久沒有繼續下去了,mm吧。嘿嘿,不過還得繼續寫下去,好長時間不寫,有些東西有點生疏了,這篇就從簡單一點的乙個 奇偶排序 說起吧,不過這個排序還是蠻有意思的,嚴格來說複雜度是o n2 不過在多核的情況下,可以做到 n2 m 2 的...

經典演算法題每日演練 第七題 KMP演算法

在大學的時候,應該在資料結構裡面都看過kmp演算法吧,不知道有多少老師對該演算法是一筆帶過的,至少我們以前是的,確實kmp演算法還是有點饒人的,如果說紅黑樹是 級的,那麼kmp演算法比紅黑樹還要 很抱歉,每次打kmp的時候,輸 入法總是提示 看毛片 三個字,嘿嘿,就叫 看毛片演算法 吧。一 bf演算...