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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

真是O(1)吗?想清楚了没?

發布時間:2025/3/21 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 真是O(1)吗?想清楚了没? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

當然標題里這個O(1)可以換成任何復雜度。

話說寫程序的時候我們會用到各種數據結構,但十有八九不會由我們自己從頭寫起,都會直接拿來用。于是很多人就會記住,譬如HashMap或Dictionary的存取是O(1)的操作,二分查找什么的則是O(log(N))。不過,我們在實踐中直接把這些類拿來用的時候,最好也留個心眼,知道這些類內部到底做了些什么,為什么它們能夠達到O(1)之類的時間復雜度。

例如,我們都知道List<T>的Add是O(1)的操作,但之所以它是O(1),是因為它的“擴容”操作被均攤了(amortized),但每次擴容時其實還是需要復制所有元素,次數越少越好,于是實踐中在可行的情況下我們往往應該給它指定一個初始容量——用StringBuilder的時候也是一樣。

我這里還可以再舉一個更為復雜的例子,例如HashSet和SortedSet,我們要向其中添加N個元素(如字符串),哪個會更快一些?從文檔上可以知道,HashSet的Add方法是O(1)的操作,而SortedSet內部是用了紅黑樹,它的Add方法是O(log(N))的操作(但它能順序輸出元素)。顯然,從時間復雜度上來講,SortedSet的性能要落后于HashSet,不過我們能否設計一個用例,讓HashSet慢于SortedSet呢?

當然可以,例如以前那個由哈希碰撞引起的DoS安全漏洞,其實就是設計了一些Hash Code相同,但具體內容不同的字符串,讓Dictionary(原理與HashSet相同)Add/Remove操作的時間復雜度從O(1)退化為O(N),這顯然低于O(log(N))。不過如今的BCL中的實現已經對碰撞次數設置了閾值,超過這個閾值則會對哈希函數進行隨機化,因此這種做法已經很難生效了。所以這里我們可以設計出另一個案例,且看代碼:

static string[] GetRandomStrings(int number, int length) {var random = new Random(DateTime.Now.Millisecond);var array = new string[number];var buffer = new char[length];for (var i = 0; i < array.Length; i++) {for (var j = 0; j < buffer.Length; j++) {buffer[j] = (char)(random.Next(Char.MinValue, Char.MaxValue) + 1);}array[i] = new string(buffer);}Console.WriteLine("Generated");return array; }static void CollectGarbage() {for (var i = 0; i < 10; i++) {GC.Collect();GC.WaitForPendingFinalizers();} }static void AddToSetTime(string name, ISet<string> set, string[] array) {CollectGarbage();var watch = new Stopwatch();watch.Start();foreach (var s in array) {set.Add(s);}Console.WriteLine(name + ": " + watch.ElapsedMilliseconds); }static void Main() {var array = GetRandomStrings(5000, 50000);AddToSetTime("HashSet", new HashSet<string>(), array);AddToSetTime("SortedSet", new SortedSet<string>(), array); }

GetRandomStrings方法用于生成一系列的隨機字符串,我們會將這些字符串使用AddToSetTime方法放入一個集合類中,并輸出耗時。這段程序在我的機器上輸出:

Generated HashSet: 165 SortedSet: 17

您可以自己嘗試一下,具體數值可能不同,但HashSet顯著慢于SortedSet則基本是確定的。為什么會這樣?O(1)為什么完敗于O(log(N))?難道HashSet的常數就那么大么?其實原因很簡單,我們只需想想HashSet和SortedSet分別是怎么實現的就行。

  • HashSet:調用元素的GetHashCode方法獲得Hash Code,算出該元素放在哪個Bucket中,然后順著鏈表使用Equals方法依次比較Hash Code的相同元素。由于Hash Code散列程度較高,相同Bucket中重復元素極少,因此時間復雜度近似為O(1)。
  • SortedSet:使用CompareTo方法比較新元素與紅黑樹里的元素,以此決定元素的樹中的“走向”,需要時再進行“平衡”操作。由于一顆平衡二叉樹的高度為log(N),因此添加一個元素要進行大約log(N)次比較(以及最多log(N)次O(1)的平衡操作),其時間復雜度大約為O(log(N))。

看起來很正常嘛,但是其中還隱含一些“假設”,那就是GetHashCode、Equals還有CompareTo方法都是O(1)的操作,但事實真是如此嗎?針對以上代碼生成的隨機字符串來說,Equals和CompareTo方法都可以幾乎瞬間返回(比較第一個字符即可)。不過GetHashCode便麻煩一些了,便順手從BCL的代碼里摘抄出來吧:

namespace System {public sealed class String {public override int GetHashCode() {#if FEATURE_RANDOMIZED_STRING_HASHINGif (HashHelpers.s_UseRandomizedStringHashing) {return InternalMarvin32HashString(this, this.Length, 0);} #endif // FEATURE_RANDOMIZED_STRING_HASHINGunsafe {fixed (char* src = this) {Contract.Assert(src[Length] == '\0', "src[this.Length] == '\\0'");Contract.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary");#if WIN32int hash1 = (5381 << 16) + 5381; #elseint hash1 = 5381; #endifint hash2 = hash1;#if WIN32// 32 bit machines. int* pint = (int*)src;int len = Length;while (len > 2) {hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];pint += 2;len -= 4;}if (len > 0) {hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];} #elseint c;char* s = src;while ((c = s[0]) != 0) {hash1 = ((hash1 << 5) + hash1) ^ c;c = s[1];if (c == 0)break;hash2 = ((hash2 << 5) + hash2) ^ c;s += 2;} #endif#if DEBUG// We want to ensure we can change our hash function daily.// This is perfectly fine as long as you don't persist the // value from GetHashCode to disk or count on String A // hashing before string B. Those are bugs in your code.hash1 ^= ThisAssembly.DailyBuildNumber; #endifreturn hash1 + (hash2 * 1566083941);}}}} }

從代碼里可以看出,撇開最先的InternalMarvin32HashString這個不談,其他分支下的哈希算法都是與字符串的長度呈線性關系。至于Marvin32這個神秘的哈希算法,我只知道可用于避免哈希碰撞攻擊,但搜索了半天都找不到它的具體信息,只有一個“疑似”的簡化實現。目前,我們還是用簡單的測試來驗證字符串長度與GetHashCode方法耗時的關系:

static void GetHashCodeTime(string name, string str, int iteration) {CollectGarbage();str.GetHashCode(); // warm upvar watch = new Stopwatch();watch.Start();for (var i = 0; i < iteration; i++) {str.GetHashCode();}Console.WriteLine(name + ": " + watch.ElapsedMilliseconds); }static void Main() {var shortStr = new string('a', 100);var longStr = new string('a', 10000);var iteration = 1000000;GetHashCodeTime("Short", shortStr, iteration);GetHashCodeTime("Long", longStr, iteration); }

我們創建了長度相差百倍的字符串,并比較其GetHashCode方法的耗時。我們可以開啟隨機化的字符串哈希算法(即使用Marvin32哈希算法),例如打開之后在我的機器上執行結果是:

Short: 114 Long: 9142

耗時與長度基本呈線性關系。關閉“隨機化哈希”之后執行速度略有提高,但耗時與長度的關系依然不變,其實從代碼上也已經能夠看出這點。

再回到最早針對HashSet和SortedSet的實驗,由于我故意生成了長度高達5w的字符串,因此HashSet時間復雜度為O(1)又如何?單次GetHashCode方法調用的耗時,就已經遠遠超過許多次CompareTo方法了。從中我們也可以看出,假如我們用字符串作為字典的鍵,其效率會較int或是普通未重載過GetHashCode和Equals方法的類型為低。話說回來,其實用一個最普通的類作為字典的鍵效率很高,因為它的GetHashCode可以直接返回它的初始地址,而Equals方法則直接比較兩個對象的引用。

在實踐中,我遇到各種需要以字符串作為鍵的場景,我都會思考下有沒有簡單的替代方法,例如使用int做鍵,甚至直接使用數組。例如,前段時間@左耳朵耗子提到對一個csv文件里的數據進行排序,我們可以使用一個字典來保存一行數據,其中鍵為字段名:

List<Dictionary<string, string>> data = ...; var ordered = data.OrderBy(row => row["column0"]) // 先以column0排序.ThenBy(row => row["column1"]); // 再以column1排序

但更有效率(內存使用也更緊湊)的做法是以一個數組來保存一行數據。我們先找出需要排序的列的下標,然后再從數組中找出排序用的字段值:

string[] columns = ...; var index0 = columns.IndexOf("column0"); var index1 = columns.IndexOf("column1");List<string[]> data = ...; var ordered = data.OrderBy(row => row[index0]).ThenBy(row => row[index1]);

從理論上講,兩種做法的時間復雜度一致,但實際上后者比前者會有不少提高。我們在了解“理論”的同時也需要注意實踐上的細節,例如,其實在實踐中O(log(N))其實也是個不比O(1)大多少的時間復雜度,此時可能也需要考慮下“常數”會對性能造成多大影響。

from:?http://blog.zhaojie.me/2013/01/think-in-detail-why-its-o-1.html

總結

以上是生活随笔為你收集整理的真是O(1)吗?想清楚了没?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美日韩午夜精品 | 久久窝窝 | 精品成人无码一区二区三区 | 国产欧美视频一区 | 国产青草视频 | 天天摸夜夜添 | 性高潮久久久久久久 | 高级家教课程在线观看 | aaa级片| 精品一区在线观看视频 | 久久精品黄色片 | 伊人网综合在线 | 国产精品免费视频一区二区三区 | 黄色自拍网站 | 亚洲AV无码一区二区伊人久久 | 超碰网址| av资源库 | 亚洲男人天堂2018 | 舌奴调教日记 | 美女啪啪动态图 | 国产精品久久av无码一区二区 | 久久97人妻无码一区二区三区 | 免费在线播放视频 | 欧美亚洲综合久久 | 美女污软件 | 久久久久极品 | 日韩乱码人妻无码系列中文字幕 | 久插网| 国产专区在线 | 老熟女一区二区三区 | 99视频在线看 | 男男黄网站 | 欧美精品在线第一页 | 男生操女生免费网站 | 欧美丰满一区二区免费视频 | 国产高清视频网站 | 中文字幕欧美在线观看 | 午夜精品久久久久久毛片 | 国内精品国产成人国产三级 | 国产a级免费视频 | 国产精品极品白嫩在线 | 强行糟蹋人妻hd中文字幕 | 4438五月天 | 91原创国产 | 99国产精品久久久久久久成人热 | 欧美成人精品激情在线视频 | 欧美激情欧美激情在线五月 | 香港三级韩国三级日本三级 | 在线观看亚洲网站 | 国产欧美激情视频 | 欧美视频在线观看视频 | 国产精品日日做人人爱 | 日本福利小视频 | 欧美成人性生活 | 国产婷婷色一区二区在线观看 | www免费网站在线观看 | 日韩黄色三级视频 | 巨胸爆乳美女露双奶头挤奶 | 国产在线观| 福利91| 老熟女毛茸茸 | aaa国产| 18禁裸男晨勃露j毛免费观看 | 亚洲精品一区二区潘金莲 | 日韩成人精品在线观看 | 久色| 人人看人人做 | 经典av在线 | 蜜桃久久久久久久 | 久久精品视频播放 | www.色香蕉 | www.色图 | 久久久久久9999| 成人免费毛片入口 | 欧美一区国产一区 | 天堂av一区 | 三区在线视频 | 欧美综合亚洲图片综合区 | 国产亚洲一区二区三区在线观看 | 精品国产乱码久久久久久闺蜜 | 性色av一区二区三区四区 | 久草福利在线视频 | 亚洲国产精品一区二区久久hs | 视频在线观看一区二区 | 久久精品大片 | 色女仆影院 | 欧洲一级视频 | 久久久av免费 | 久久女人天堂 | 成人写真福利网 | 欧美区视频 | 影音先锋男人资源网站 | 国产91在线观看 | 性――交――性――乱睡觉 | 婷婷五月综合激情 | 国产95在线 | 色乱码一区二区三区 | 中文字幕电影av | 天堂av影院 |