日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

经典算法题每日演练——第十题 树状数组

發(fā)布時(shí)間:2025/3/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 经典算法题每日演练——第十题 树状数组 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
原文:經(jīng)典算法題每日演練——第十題 樹狀數(shù)組

?? ? ? ?有一種數(shù)據(jù)結(jié)構(gòu)是神奇的,神秘的,它展現(xiàn)了位運(yùn)算與數(shù)組結(jié)合的神奇魅力,太牛逼的,它就是樹狀數(shù)組,這種數(shù)據(jù)結(jié)構(gòu)不是神人是發(fā)現(xiàn)不了的。

一:概序

? ? ?假如我現(xiàn)在有個(gè)需求,就是要頻繁的求數(shù)組的前n項(xiàng)和,并且存在著數(shù)組中某些數(shù)字的頻繁修改,那么我們?cè)撊绾螌?shí)現(xiàn)這樣的需求?當(dāng)然大家可以往

真實(shí)項(xiàng)目上靠一靠。

① 傳統(tǒng)方法:根據(jù)索引修改為O(1),但是求前n項(xiàng)和為O(n)。

②空間換時(shí)間方法:我開一個(gè)數(shù)組sum[],sum[i]=a[1]+....+a[i],那么有點(diǎn)意思,求n項(xiàng)和為O(1),但是修改卻成了O(N),這是因?yàn)槲业腟um[i]中牽

? ? ? ? ? ? ? ? ? ? ? ? ?涉的數(shù)據(jù)太多了,那么問題來了,我能不能在相應(yīng)的sum[i]中只保存某些a[i]的值呢?好吧,下面我們看張圖。

從圖中我們可以看到S[]的分布變成了一顆樹,有意思吧,下面我們看看S[i]中到底存放著哪些a[i]的值。

S[1]=a[1];

S[2]=a[1]+a[2];

S[3]=a[3];

S[4]=a[1]+a[2]+a[3]+a[4];

S[5]=a[5];

S[6]=a[5]+a[6];

S[7]=a[7];

S[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];

之所以采用這樣的分布方式,是因?yàn)槲覀兪褂玫氖沁@樣的一個(gè)公式:S[i]=a[i-2k+1]+....+a[i]。

其中:2k?中的k表示當(dāng)前S[i]在樹中的層數(shù),它的值就是i的二進(jìn)制中末尾連續(xù)0的個(gè)數(shù),2k也就是表示S[i]中包含了哪些a[],

舉個(gè)例子: ?i=610=01102?;可以發(fā)現(xiàn)末尾連續(xù)的0有一個(gè),即k=1,則說明S[6]是在樹中的第二層,并且S[6]中有21項(xiàng),隨后我們求出了起始項(xiàng):

? ? ? ? ? ? a[6-21+1]=a[5],但是在編碼中求出k的值還是有點(diǎn)麻煩的,所以我們采用更靈巧的Lowbit技術(shù),即:2k=i&-i 。

? ? ? ? ? ?則:S[6]=a[6-21+1]=a[6-(6&-6)+1]=a[5]+a[6]。

二:代碼

1:神奇的Lowbit函數(shù)

1 #region 當(dāng)前的sum數(shù)列的起始下標(biāo)2 /// <summary>3 /// 當(dāng)前的sum數(shù)列的起始下標(biāo)4 /// </summary>5 /// <param name="i"></param>6 /// <returns></returns>7 public static int Lowbit(int i)8 {9 return i & -i; 10 } 11 #endregion

?

2:求前n項(xiàng)和

? ? ?比如上圖中,如何求Sum(6),很顯然Sum(6)=S4+S6,那么如何尋找S4呢?即找到6以前的所有最大子樹,很顯然這個(gè)求和的復(fù)雜度為logN。

1 #region 求前n項(xiàng)和2 /// <summary>3 /// 求前n項(xiàng)和4 /// </summary>5 /// <param name="x"></param>6 /// <returns></returns>7 public static int Sum(int x)8 {9 int ans = 0; 10 11 var i = x; 12 13 while (i > 0) 14 { 15 ans += sumArray[i - 1]; 16 17 //當(dāng)前項(xiàng)的最大子樹 18 i -= Lowbit(i); 19 } 20 21 return ans; 22 } 23 #endregion

3:修改

如上圖中,如果我修改了a[5]的值,那么包含a[5]的S[5],S[6],S[8]的區(qū)間值都需要同步修改,我們看到只要沿著S[5]一直回溯到根即可,

同樣它的時(shí)間復(fù)雜度也為logN。

1 public static void Modify(int x, int newValue)2 {3 //拿出原數(shù)組的值4 var oldValue = arr[x];5 6 for (int i = x; i < arr.Length; i += Lowbit(i + 1))7 {8 //減去老值,換一個(gè)新值9 sumArray[i] = sumArray[i] - oldValue + newValue; 10 } 11 }

最后上總的代碼:

View Code 1 using System;2 using System.Collections.Generic;3 using System.Linq;4 using System.Text;5 using System.Diagnostics;6 using System.Threading;7 using System.IO;8 9 namespace ConsoleApplication210 {11 public class Program12 {13 static int[] sumArray = new int[8];14 15 static int[] arr = new int[8];16 17 public static void Main()18 {19 Init();20 21 Console.WriteLine("A數(shù)組的值:{0}", string.Join(",", arr));22 Console.WriteLine("S數(shù)組的值:{0}", string.Join(",", sumArray));23 24 Console.WriteLine("修改A[1]的值為3");25 Modify(1, 3);26 27 Console.WriteLine("A數(shù)組的值:{0}", string.Join(",", arr));28 Console.WriteLine("S數(shù)組的值:{0}", string.Join(",", sumArray));29 30 Console.Read();31 }32 33 #region 初始化兩個(gè)數(shù)組34 /// <summary>35 /// 初始化兩個(gè)數(shù)組36 /// </summary>37 public static void Init()38 {39 for (int i = 1; i <= 8; i++)40 {41 arr[i - 1] = i;42 43 //設(shè)置其實(shí)坐標(biāo):i=1開始44 int start = (i - Lowbit(i));45 46 var sum = 0;47 48 while (start < i)49 {50 sum += arr[start];51 52 start++;53 }54 55 sumArray[i - 1] = sum;56 }57 }58 #endregion59 60 public static void Modify(int x, int newValue)61 {62 //拿出原數(shù)組的值63 var oldValue = arr[x];64 65 arr[x] = newValue;66 67 for (int i = x; i < arr.Length; i += Lowbit(i + 1))68 {69 //減去老值,換一個(gè)新值70 sumArray[i] = sumArray[i] - oldValue + newValue;71 }72 }73 74 #region 求前n項(xiàng)和75 /// <summary>76 /// 求前n項(xiàng)和77 /// </summary>78 /// <param name="x"></param>79 /// <returns></returns>80 public static int Sum(int x)81 {82 int ans = 0;83 84 var i = x;85 86 while (i > 0)87 {88 ans += sumArray[i - 1];89 90 //當(dāng)前項(xiàng)的最大子樹91 i -= Lowbit(i);92 }93 94 return ans;95 }96 #endregion97 98 #region 當(dāng)前的sum數(shù)列的起始下標(biāo)99 /// <summary> 100 /// 當(dāng)前的sum數(shù)列的起始下標(biāo) 101 /// </summary> 102 /// <param name="i"></param> 103 /// <returns></returns> 104 public static int Lowbit(int i) 105 { 106 return i & -i; 107 } 108 #endregion 109 } 110 }

?

總結(jié)

以上是生活随笔為你收集整理的经典算法题每日演练——第十题 树状数组的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。