C++STL自己總結(jié)

STL部分


1.STL為什么廣泛被使用

C++ STL 之所以得到廣泛的贊譽(yù),也被很多人使用挑社,不只是提供了像vector, string, list等方便的容器忱反,更重要的是STL封裝了許多復(fù)雜的數(shù)據(jù)結(jié)構(gòu)算法和大量常用數(shù)據(jù)結(jié)構(gòu)操作蔫磨。vector封裝數(shù)組,list封裝了鏈表士袄,map和set封裝了二叉樹等

2.標(biāo)準(zhǔn)關(guān)聯(lián)容器 set, multiset, map, multimap內(nèi)部采用數(shù)據(jù)結(jié)構(gòu)是什么?簡要介紹一下谎僻。

(1)都是一種非常高效的平衡檢索二叉樹:紅黑樹娄柳,也稱為為RB樹(Red-BlackTree)。RB樹的統(tǒng)計(jì)性能要好于一般的平衡二叉樹戈稿,

(2)紅黑樹的簡要介紹:紅黑樹(一)之 原理和算法詳細(xì)介紹 - 如果天空不死 - 博客園

3.STL map和set的相關(guān)問題:?

(1)為何map和set的插入刪除效率比用其他序列容器高?西土,樹

答:因?yàn)閷τ陉P(guān)聯(lián)容器來說,不需要做內(nèi)存拷貝和內(nèi)存移動鞍盗。說對了需了,確實(shí)如此。map和set容器內(nèi)所有元素都是以節(jié)點(diǎn)的方式來存儲般甲,其節(jié)點(diǎn)結(jié)構(gòu)和鏈表差不多肋乍,指向父節(jié)點(diǎn)和子節(jié)點(diǎn)

(2)為何每次insert之后,以前保存的iterator不會失效?

答:iterator這里就相當(dāng)于指向節(jié)點(diǎn)的指針敷存,內(nèi)存沒有變墓造,指向內(nèi)存的指針怎么會失效呢(當(dāng)然被刪除的那個元素本身已經(jīng)失效了)。相對于vector來說锚烦,每一次刪除和插入觅闽,指針都有可能失效,調(diào)用push_back在尾部插入也是如此涮俄。因?yàn)闉榱吮WC內(nèi)部數(shù)據(jù)的連續(xù)存放蛉拙,iterator指向的那塊內(nèi)存在刪除和插入過程中可能已經(jīng)被其他內(nèi)存覆蓋或者內(nèi)存已經(jīng)被釋放了。即使是push_back的時候彻亲,容器內(nèi)部空間可能不夠孕锄,需要一塊新的更大的內(nèi)存,只有把以前的內(nèi)存釋放苞尝,申請新的更大的內(nèi)存畸肆,復(fù)制已有的數(shù)據(jù)元素到新的內(nèi)存,最后把需要插入的元素放到最后宙址,那么以前的內(nèi)存指針自然就不可用了轴脐。特別時在和find等算法在一起使用的時候,牢記這個原則:不要使用過期的iterator。

(3)為何map和set不能像vector一樣有個reserve函數(shù)來預(yù)分配數(shù)據(jù)?

答:我以前也這么問豁辉,究其原理來說時令野,引起它的原因在于在map和set內(nèi)部存儲的已經(jīng)不是元素本身了,而是包含元素的節(jié)點(diǎn)徽级。也就是說map內(nèi)部使用的Alloc并不是map聲明的時候從參數(shù)中傳入的Alloc气破。例如:

4.? set 與 multiset

set和multiset會根據(jù)特定的排序準(zhǔn)則自動將元素排序,set中元素不允許重復(fù)餐抢,multiset可以重復(fù)现使。

因?yàn)槭桥判虻模詓et中的元素不能被修改旷痕,只能刪除后再添加碳锈。

set與multiset有序存儲元素,這兩種容器的底層實(shí)現(xiàn)與map一樣都是紅黑樹欺抗,所以能實(shí)現(xiàn)O(lgn)的查找售碳,插入,刪除操作绞呈。

5.? map與multimap底層數(shù)據(jù)結(jié)構(gòu)

map與multimap是STL中的關(guān)聯(lián)容器贸人、提供一對一key-value的數(shù)據(jù)處理能力; map與multimap的區(qū)別在于佃声,multimap允許關(guān)鍵字重復(fù)艺智,而map不允許重復(fù)。

這兩個關(guān)聯(lián)容器的底層數(shù)據(jù)結(jié)構(gòu)均為紅黑樹圾亏,關(guān)于紅黑樹的理解可以參考教你透徹了解紅黑樹一文十拣。

根據(jù)紅黑樹的原理,map與multimap可以實(shí)現(xiàn)O(lgn)的查找志鹃,插入和刪除夭问。

6. List的功能方法

實(shí)際上有兩種List: 一種是基本的ArrayList,其優(yōu)點(diǎn)在于隨機(jī)訪問元素,另一種是更強(qiáng)大的LinkedList,它并不是為快速隨機(jī)訪問設(shè)計(jì)的曹铃,而是具有一套更通用的方法甲喝。

List : 次序是List最重要的特點(diǎn):它保證維護(hù)元素特定的順序。List為Collection添加了許多方法铛只,使得能夠向List中間插入與移除元素(這只推薦LinkedList使用)一個List可以生成ListIterator,使用它可以從兩個方向遍歷List,也可以從List中間插入和移除元素。

ArrayList : 由數(shù)組實(shí)現(xiàn)的List糠溜。允許對元素進(jìn)行快速隨機(jī)訪問淳玩,但是向List中間插入與移除元素的速度很慢。ListIterator只應(yīng)該用來由后向前遍歷ArrayList,而不是用來插入和移除元素非竿。因?yàn)槟潜萀inkedList開銷要大很多蜕着。

LinkedList : 對順序訪問進(jìn)行了優(yōu)化,向List中間插入與刪除的開銷并不大。隨機(jī)訪問則相對較慢承匣。(使用ArrayList代替蓖乘。)還具有下列方法:addFirst(), addLast(), getFirst(),getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何接口或基類中定義過)使得LinkedList可以當(dāng)作堆棧、隊(duì)列和雙向隊(duì)列使用

List常用操作函數(shù):

Lst1.assign() 給list賦值 韧骗; ? Lst1.back() 返回最后一個元素嘉抒; ? Lst1.begin() 返回指向第一個元素的迭代器

Lst1.clear() 刪除所有元素; Lst1.empty() 如果list是空的則返回true袍暴; ? Lst1.end() 返回末尾的迭代器

Lst1.erase() 刪除一個元素些侍;? Lst1.front() 返回第一個元素; ?? Lst1.get_allocator() 返回list的配置器

Lst1.insert() 插入一個元素到list中政模;(Lst1.insert(iter, 100)//在迭代器iter指的元素后面加入元素100

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? Lst1.insert(iter岗宣,2, 100)? //在迭代器iter指的元素后面加如2個100)

? Lst1.max_size() 返回list能容納的最大元素?cái)?shù)量

Lst1.merge() 合并兩個list淋样; ? ? ? Lst1.pop_back() 刪除最后一個元素; ?? Lst1.pop_front() 刪除第一個元素

Lst1.push_back() 在list的末尾添加一個元素刊咳;Lst1.push_front() 在list的頭部添加一個元素

Lst1.rbegin() 返回指向第一個元素的逆向迭代器躲叼; Lst1.remove() 從list刪除元素;

Lst1.remove_if() 按指定條件刪除元素枫慷;? Lst1.rend() 指向list末尾的逆向迭代器; Lst1.resize() 改變list的大小

Lst1.reverse() 把list的元素倒轉(zhuǎn)探孝; Lst1.size() 返回list中的元素個數(shù);? Lst1.sort() 給list排序

Lst1.splice() 合并兩個list誉裆; Lst1.swap() 交換兩個list顿颅;? Lst1.unique() 刪除list中重復(fù)的元素

7.? hash_map和map的區(qū)別在哪里?

構(gòu)造函數(shù):hash_map需要hash函數(shù)(等于函數(shù)); ? ?? map只需要比較函數(shù)(小于函數(shù)).

存儲結(jié)構(gòu):hash_map采用hash表存儲,map一般采用紅黑樹(RBTree)實(shí)現(xiàn)足丢。因此其memory數(shù)據(jù)結(jié)構(gòu)是不一樣的粱腻。

8 .什么時候需要用hash_map,什么時候需要用map?

總體來說斩跌,hash_map 查找速度會比map快绍些,而且查找速度基本和數(shù)據(jù)數(shù)據(jù)量大小無關(guān),屬于常數(shù)級別;而map的查找速度是log(n)級別耀鸦。并不一定常數(shù)就比log(n)小柬批,hash還有hash函數(shù)的耗時啸澡,明白了吧,如果你考慮效率氮帐,特別是在元素達(dá)到一定數(shù)量級時嗅虏,考慮考慮hash_map。但若你對內(nèi)存使用特別嚴(yán)格上沐,希望程序盡可能少消耗內(nèi)存皮服,那么一定要小心,hash_map可能會讓你陷入尷尬奄容,特別是當(dāng)你的hash_map對象特別多時冰更,你就更無法控制了,而且hash_map的構(gòu)造速度較慢昂勒。

現(xiàn)在知道如何選擇了嗎?權(quán)衡三個因素: 查找速度, 數(shù)據(jù)量, 內(nèi)存使用蜀细。

9.?一些容器使用上的建議(應(yīng)用場景)

Level 1 - 僅僅作為Map使用:采用靜態(tài)數(shù)組

Level 2 - 保存定長數(shù)據(jù)奠衔,使用時也是全部遍歷:采用動態(tài)數(shù)組(長度一開始就固定的話靜態(tài)數(shù)組也行)

Level 3 - 保存不定長數(shù)組,需要動態(tài)增加的能力脏里,側(cè)重于尋找數(shù)據(jù)的速度:采用vector

Level 3 - 保存不定長數(shù)組迫横,需要動態(tài)增加的能力,側(cè)重于增加刪除數(shù)據(jù)的速度:采用list

Level 4 - 對數(shù)據(jù)有復(fù)雜操作呛讲,即需要前后增刪數(shù)據(jù)的能力贝搁,又要良好的數(shù)據(jù)訪問速度:采用deque

Level 5 - 對數(shù)據(jù)中間的增刪操作比較多:采用list,建議在排序的基礎(chǔ)上,批量進(jìn)行增刪可以對運(yùn)行效率提供最大的保證

Level 6 - 上述中找不到適合的:組合STL容器或者自己建立特殊的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)

10 vector, list, deque 三者比較分析

(1).vector ----- 會自動增長的數(shù)組

vector由于數(shù)組的增長只能向前十厢,所以也只提供了后端插入和后端刪除缩抡,也就是push_back和pop_back瞻想。當(dāng)然在前端和中間要操作數(shù)據(jù)也是可以的,用insert和erase佃迄,但是前端和中間對數(shù)據(jù)進(jìn)行操作必然會引起數(shù)據(jù)塊的移動呵俏,

這對性能影響是非常大的。最大的優(yōu)勢就是隨機(jī)訪問的能力麻车。

(2).list -------- 擅長插入刪除的鏈表

list 的常用操作:?push_back(), pop_back()绪氛,push_front(), pop_front()

list是一個雙向鏈表的實(shí)現(xiàn):

為了提供雙向遍歷的能力序目,list要比一般的數(shù)據(jù)單元多出兩個指向前后的指針猿涨,一個使用iterator來刪除元素的例子

list stringList;

list::iterator iter;

advance(iter, 5); //將iterator指向stringList的第五個元素

stringList.erase(iterator);//刪除

那么刪除操作進(jìn)行以后澡绩,傳入erase()方法的iterator指向哪里了呢?它指向了刪除操作前所指向的那個元素的后一個元素肥卡。

(3).deque ------? 擁有vector和list兩者優(yōu)點(diǎn)的雙端隊(duì)列

這三個模板的總結(jié) 比較和一般使用準(zhǔn)則

這三個模板都屬于序列類模板,可以看到他們有一些通用方法

size():得到容器大小

begin():得到指向容器內(nèi)第一個元素的指針(這個指針的類型依容器的不同而不同)

end():得到指向容器內(nèi)最后一個元素之后一個位置的指針

erase():刪除傳入指針指向的那個元素

clear():清除所有的元素

==運(yùn)算符:判斷兩個容器是否相等

=運(yùn)算符:用來給容器賦值

上面的三個模板有各自的特點(diǎn)

vector模板的數(shù)據(jù)在內(nèi)存中連續(xù)的排列镰矿,所以隨機(jī)存取元素(即通過[]運(yùn)算符存取)的速度最快,這一點(diǎn)和數(shù)組是一致的障般。同樣由于它的連續(xù)排列挽荡,所以它在除尾部以外的位置刪除或添加元素的速度很慢,在使用vector時青自,要避免這種操作延窜。

list模板的數(shù)據(jù)是鏈?zhǔn)酱鎯δ嫒穑圆荒茈S機(jī)存取元素获高。它的優(yōu)勢在于任意位置添加 刪除元素的速度。

deque模板是通過鏈接若干片連續(xù)的數(shù)據(jù)實(shí)現(xiàn)的摊趾,所以均衡了以上兩個容器的特點(diǎn)

11.說說vector的底層(存儲)機(jī)制

?vector就是一個動態(tài)數(shù)組砾层,里面有一個指針指向一片連續(xù)的內(nèi)存空間,當(dāng)空間不夠裝下數(shù)據(jù)時铸董,會自動申請另一片更大的空間(一般是增加當(dāng)前容量的50%或100%)粟害,然后把原來的數(shù)據(jù)拷貝過去,接著釋放原來的那片空間站蝠;當(dāng)釋放或者刪除當(dāng)前vector里面的數(shù)據(jù)時留荔,當(dāng)前vector的存儲空間不釋放,僅僅是清空了里面的數(shù)據(jù)藻治。

12.vector的自增長機(jī)制

當(dāng)已經(jīng)分配的空間不夠裝下數(shù)據(jù)時验靡,分配雙倍于當(dāng)前容量的存儲區(qū)晴叨,把當(dāng)前的值拷貝到新分配的內(nèi)存中,并釋放原來的內(nèi)存孙技。

13.說說list的底層(存儲)機(jī)制

以結(jié)點(diǎn)為單位存放數(shù)據(jù)亚情,結(jié)點(diǎn)的地址在內(nèi)存中不一定連續(xù)楞件,每次插入或刪除一個元素土浸,就配置或釋放一個元素空間

14. unordeded_map和unordeded_set的底層實(shí)現(xiàn)

? ?? 在STL函數(shù)庫里邊有關(guān)map和set的總共有8個函數(shù)。

? map还最; set拓轻;multimap悦即;multiset;unordered_map作瞄;unordered_set宗挥;unordered_multiset;unordered_multimap

?簡單的做以區(qū)分:

map: ? K/V (鍵/值)結(jié)構(gòu)搪桂。 ? ? ? ? ? set: ? K(鍵)結(jié)構(gòu)酗电。

multi: ? 可以冗余,如沒有這個關(guān)鍵字嫩与,則是防冗余版本。

unordered: ? 底層由哈希表(哈希桶算法)來實(shí)現(xiàn),如無該關(guān)鍵字都许,則底層是由紅黑樹來實(shí)現(xiàn)。

unordered_map和unordered_set ? &? unordered_multiset unordered_multimap中key為無序排列睛低,其底層實(shí)現(xiàn)為hash table,因此其查找時間復(fù)雜度理論上達(dá)到了O(n)

15.什么情況下用vector吹零,什么情況下用list

vector可以隨機(jī)存儲元素(即可以通過公式直接計(jì)算出元素地址灿椅,而不需要挨個查找)操刀,但在非尾部插入刪除數(shù)據(jù)時骨坑,效率很低静稻,適合對象簡單,對象數(shù)量變化不大押搪,隨機(jī)訪問頻繁大州。

list不支持隨機(jī)存儲,適用于對象大垂谢,對象數(shù)量變化頻繁厦画,插入和刪除頻繁。

16. list自帶排序函數(shù)的排序原理

將前兩個元素合并滥朱,再將后兩個元素合并根暑,然后合并這兩個子序列成4個元素的子序列,重復(fù)這一過程徙邻,得到8個颇象,16個耍贾,...,子序列,最后得到的就是排序后的序列砂竖。

時間復(fù)雜度:O(nlgn)

17.說說deque的底層機(jī)制 ? STL之deque容器的實(shí)現(xiàn)框架 - CSDN博客

深入剖析deque容器實(shí)現(xiàn) - 綜合編程類其他綜合 - 紅黑聯(lián)盟

queue的底層數(shù)據(jù)結(jié)構(gòu)

deque動態(tài)地以分段連續(xù)空間組合而成,隨時可以增加一段新的連續(xù)空間并鏈接起來脏款。不提供空間保留功能腺占。

注意:除非必要,我們盡可能選擇使用vector而非deque,因?yàn)閐eque的迭代器比vector迭代器復(fù)雜很多闰靴。對deque排序捆交,為了提高效率,可先將deque復(fù)制到一個vector上排序鲫趁,然后再復(fù)制回deque。

deque采用一塊map(不是STL的map容器)這個_Map是一個指針,指向一塊特殊的內(nèi)存地址役电,這里保存著指向deque動態(tài)申請的所有512字節(jié)內(nèi)存空間的首地址。作為中控器/ 中央控制器 /主控,其為一小塊連續(xù)空間,其中每個元素都是指針,指向另一段較大的連續(xù)空間(緩沖區(qū))。

deque先用一段小的連續(xù)空間順序存放了一個一個指針砂吞,然后這些順序存放的指針再各自指向用來真正存放數(shù)據(jù)的512字節(jié)連續(xù)性空間當(dāng)_Map指向的這塊空間不夠存放內(nèi)存指針的時候,就會另覓一塊更大的連續(xù)性空間(是map空間重新尋找胶惰,而不是緩沖區(qū)重新尋找),然后把指針一個一個復(fù)制過去尿赚,并銷毀舊的空間(銷毀的是原來的map空間)皿渗。利用這種數(shù)據(jù)結(jié)構(gòu)挤土,deque就能方便地模擬自身的存儲區(qū)是連續(xù)性空間的假象懦尝,并且可以實(shí)現(xiàn)雙向插入刪除的功能

map的結(jié)構(gòu)

deque 的迭代器

??讓我們思考一下甥绿,deque的迭代器應(yīng)該具備什么結(jié)構(gòu)字币,首先,它必須能夠指出分段連續(xù)空間(亦即緩沖區(qū))在哪里共缕,其次它必須能夠判斷自己是否已經(jīng)處于其所在緩沖區(qū)的邊緣洗出,如果是,一旦前進(jìn)或后退就必須跳躍至下一個或上一個緩沖區(qū)图谷。為了能夠正確跳躍翩活,deque必須隨時掌握管控中心(map)。所以在迭代器中需要定義:當(dāng)前元素的指針cur便贵,當(dāng)前元素所在緩沖區(qū)的起始指針first菠镇,當(dāng)前元素所在緩沖區(qū)的尾指針last,指向map中指向所在緩區(qū)地址的指針node承璃, ?? 分別為cur, first, last, node辟犀。

deque的迭代器包含4個內(nèi)容:

1)cur:迭代器當(dāng)前所指元素

2)first:此迭代器所指的緩沖區(qū)的頭。

3)last:緩沖區(qū)尾绸硕。

4)node:指向管控中心堂竟。

deque迭代器失效的原因:

查看deque源碼可知,在push_front/push_back中玻佩,由于要滿足以上預(yù)留一個節(jié)點(diǎn)的要求出嘹,若當(dāng)前map所管理的節(jié)點(diǎn)個數(shù)不足以擴(kuò)充時,map需要重分配咬崔。如圖所示税稼; 當(dāng)前deque中含有23個元素,此時push_back(23)垮斯,會導(dǎo)致node節(jié)點(diǎn)不足郎仆,于是引起map的重分配。原來的迭代器的node指向的map節(jié)點(diǎn)被釋放兜蠕,也就無法找到對應(yīng)的元素扰肌,故原迭代器失效

18. deque與vector的區(qū)別

1)vector是單向開口的連續(xù)線性空間,deque是雙向開口的連續(xù)線性空間熊杨。(雙向開口是指可以在頭尾兩端分別做元素的插入和刪除操作)曙旭。

2)deque沒有提供空間保留功能盗舰,而vector則要提供空間保留功能。

3)deque也提供隨機(jī)訪問迭代器桂躏,但是其迭代器比vector迭代器復(fù)雜很多钻趋。

19.不允許有遍歷行為的容器有哪些(不提供迭代器)?

1)queue剂习,除了頭部外蛮位,沒有其他方法存取deque的其他元素。

2)stack(底層以deque實(shí)現(xiàn))鳞绕,除了最頂端外土至,沒有任何其他方法可以存取stack的其他元素。

3)heap猾昆,所有元素都必須遵循特別的排序規(guī)則陶因,不提供遍歷功能。

20.說說map底層機(jī)制

map以RB-TREE為底層機(jī)制垂蜗。RB-TREE是一種平衡二叉搜索樹楷扬,自動排序效果不錯。

通過map的迭代器不能修改其鍵值贴见,只能修改其實(shí)值烘苹。所以map的迭代器既不是const也不是mutable。

21.vector插入刪除和list有什么區(qū)別片部?

vector插入和刪除數(shù)據(jù)镣衡,需要對現(xiàn)有數(shù)據(jù)進(jìn)行復(fù)制移動,如果vector存儲的對象很大或者構(gòu)造函數(shù)很復(fù)雜档悠,則開銷較大廊鸥,如果是簡單的小數(shù)據(jù),效率優(yōu)于list辖所。

list插入和刪除數(shù)據(jù)惰说,需要對現(xiàn)有數(shù)據(jù)進(jìn)行遍歷,但在首部插入數(shù)據(jù)缘回,效率很高吆视。

22.hashtable如何避免地址沖突?

1)線性探測:先用hash function計(jì)算某個元素的插入位置酥宴,如果該位置的空間已被占用啦吧,則繼續(xù)往下尋找,知道找到一個可用空間為止拙寡。

進(jìn)行元素搜索的時候授滓,如果hash function計(jì)算出來的位置上的元素值與我們搜尋目標(biāo)不符,就循環(huán)往下一一尋找,直到找到吻合者褒墨,或直到遇上空格元素。

其刪除采用惰性刪除:只標(biāo)記刪除記號擎宝,實(shí)際刪除操作等到表格重新整理時再進(jìn)行郁妈。(因?yàn)閔ash table中的每一個元素不僅表述它自己,也關(guān)系到其他元素的排列绍申。)

2)二次探測:如果計(jì)算出的位置為H且被占用噩咪,則依次嘗試H+1^2,H+2^2等(解決線性探測中主集團(tuán)問題)极阅。

3)開鏈:每一個哈希表元素中維護(hù)一個list鏈表胃碾,hash function為我們分配一個list,然后在那個list執(zhí)行插入筋搏、刪除等操作仆百。當(dāng)發(fā)生沖突時,該位置上的數(shù)據(jù)會用鏈表鏈起來奔脐,當(dāng)表中的某些位置沒有結(jié)點(diǎn)時俄周,該位置就為NULL!髓迎!這種方法在實(shí)現(xiàn)時就需要多加一個next指針峦朗,使這些結(jié)點(diǎn)才能鏈起來。

23. hashtable排龄,hash_set波势,hash_map的區(qū)別

hash_set以hashtable為底層,不具有排序功能橄维,能快速查找尺铣。其鍵值就是實(shí)值。(set以RB-TREE為底層争舞,具有排序功能迄埃。)

hash_map以以hashtable為底層,沒有自動排序功能兑障,能快速查找侄非,每一個元素同時擁有一個實(shí)值和鍵值。(map以RB-TREE為底層流译,具有排序功能逞怨。)

24 .hash_map與map的區(qū)別?什么時候用hash_map福澡,什么時候用map叠赦?

構(gòu)造函數(shù):hash_map需要hash function和等于函數(shù),而map需要比較函數(shù)(大于或小于)。

存儲結(jié)構(gòu):hash_map以hashtable為底層除秀,而map以RB-TREE為底層糯累。?

總的說來,hash_map查找速度比map快册踩,而且查找速度基本和數(shù)據(jù)量大小無關(guān)泳姐,屬于常數(shù)級別。而map的查找速度是logn級別暂吉。但不一定常數(shù)就比log小胖秒,而且hash_map還有hash function耗時。

如果考慮效率慕的,特別當(dāng)元素達(dá)到一定數(shù)量級時阎肝,用hash_map。

考慮內(nèi)存肮街,或者元素?cái)?shù)量較少時风题,用map。

25.紅黑樹有什么性質(zhì)嫉父? ??紅黑樹(一)之 原理和算法詳細(xì)介紹 - 如果天空不死 - 博客園

紅黑樹的應(yīng)用比較廣泛俯邓,主要是用它來存儲有序的數(shù)據(jù),它的時間復(fù)雜度是O(lgn)熔号,效率非常之高稽鞭。例如,Java集合中的TreeSet和TreeMap引镊,C++ STL中的set朦蕴、map,以及Linux虛擬內(nèi)存的管理弟头,都是通過紅黑樹去實(shí)現(xiàn)的吩抓。

1)每個結(jié)點(diǎn)是紅色或者黑色。

2)根結(jié)點(diǎn)為黑色赴恨。

3)葉結(jié)點(diǎn)為黑色的NULL結(jié)點(diǎn)疹娶。

4)如果結(jié)點(diǎn)為紅,其子節(jié)點(diǎn)必須為黑伦连。

5)任一結(jié)點(diǎn)到NULL的任何路徑雨饺,所含黑結(jié)點(diǎn)數(shù)必須相同。

紅黑樹的應(yīng)用比較廣泛惑淳,主要是用它來存儲有序的數(shù)據(jù)额港,它的時間復(fù)雜度是O(lgn),效率非常之高歧焦。例如移斩,Java集合中的TreeSetTreeMap,C++ STL中的set、map向瓷,以及Linux虛擬內(nèi)存的管理肠套,都是通過紅黑樹去實(shí)現(xiàn)的。

26.map和set的3個問題

1)為何map和set的插入刪除效率比其他序列容器高猖任。

因?yàn)椴恍枰獌?nèi)存拷貝和內(nèi)存移動

2)為何map和set每次Insert之后你稚,以前保存的iterator不會失效?

因?yàn)椴迦氩僮髦皇墙Y(jié)點(diǎn)指針換來換去超升,結(jié)點(diǎn)內(nèi)存沒有改變入宦。而iterator就像指向結(jié)點(diǎn)的指針哺徊,內(nèi)存沒變室琢,指向內(nèi)存的指針也不會變。

3)當(dāng)數(shù)據(jù)元素增多時(從10000到20000)落追,map和set的查找速度會怎樣變化盈滴?

RB-TREE用二分查找法,時間復(fù)雜度為logn轿钠,所以從10000增到20000時巢钓,查找次數(shù)從log10000=4次到log20000=5次,多了1次而已疗垛。

set常用方法:比如先創(chuàng)建 set<int> s

s.begin()????    ,返回set容器的第一個元素

s.end()      ,返回set容器的最后一個元素

s.clear()??   ??? ?,刪除set容器中的所有的元素

s.empty()    ,判斷set容器是否為空

s.max_size()   ,返回set容器可能包含的元素最大個數(shù)

s.size()      ,返回當(dāng)前set容器中的元素個數(shù)

s.rbegin     ,返回的值和end()相同

s.rend()     ,返回的值和rbegin()相同

map的相關(guān)操作 : ?C/C++——map的基本操作總結(jié) - CSDN博客

27.vector中begin和end函數(shù)返回的是什么症汹?

begin返回的是第一個元素的迭代器,end返回的是最后一個元素后面位置的迭代器贷腕。

28.為什么vector的插入操作可能會導(dǎo)致迭代器失效背镇?

vector動態(tài)增加大小時,并不是在原空間后增加新的空間泽裳,而是以原大小的兩倍在另外配置一片較大的新空間瞒斩,然后將內(nèi)容拷貝過來,并釋放原來的空間涮总。由于操作改變了空間胸囱,所以迭代器失效。

29.vector瀑梗、list烹笔、map、deque用erase(it)后抛丽,迭代器的變化

vector和deque是序列式容器箕宙,其內(nèi)存分別是連續(xù)空間和分段連續(xù)空間,刪除迭代器it后铺纽,其后面的迭代器都失效了柬帕,此時it及其后面的迭代器會自動加1,使it指向被刪除元素的下一個元素。

list刪除迭代器it時陷寝,其后面的迭代器都不會失效锅很,將前面和后面連接起來即可。

map也是只能使當(dāng)前刪除的迭代器失效凤跑,其后面的迭代器依然有效爆安。

30. hashtable和hashmap的區(qū)別

hashmap以hashtable為底層。主要有以下幾點(diǎn)不同:

1)hashtable是Dictionary的子類仔引,而hashmap是Map接口的一個實(shí)現(xiàn)類扔仓。

2)hashtable中的方法是同步的,而hashmap的方法不同步咖耘。

重點(diǎn)說 ? hashtable:

哈希表(hash table, 也叫散列表)翘簇,是種根據(jù)關(guān)鍵字(Key value)而直接進(jìn)行訪問數(shù)據(jù)結(jié)構(gòu),它可以提供快速的插入操作和查找操作儿倒。也就是說版保,它通過把關(guān)鍵字值映射到一個位置來訪問記錄,以加快查找的速度夫否。這個映射函數(shù)稱為哈希函數(shù)(也稱為散列函數(shù))彻犁,映射過程稱為哈希化凰慈,存放記錄的數(shù)組叫做哈希表(散列表)汞幢。哈希表最大的優(yōu)點(diǎn),就是把數(shù)據(jù)的存儲和查找消耗的時間大大降低微谓,幾乎可以看成是常數(shù)時間森篷;而代價僅僅是消耗比較多的內(nèi)存。

哈希函數(shù)構(gòu)造

當(dāng)需要使用一個下標(biāo)范圍比較大的數(shù)組來存儲元素時堰酿,可以設(shè)計(jì)一個函數(shù)(哈希函數(shù)疾宏, 也叫做散列函數(shù)),使得每個元素的關(guān)鍵字都與一個函數(shù)值(即數(shù)組下標(biāo))相對應(yīng)触创,于是用這個數(shù)組單元來存儲這個元素坎藐。也可以簡單的理解為,按照關(guān)鍵字為每一 個元素“分類”哼绑,然后將這個元素存儲在相應(yīng)“類”所對應(yīng)的地方岩馍。點(diǎn)擊打開鏈接

Hashtable底層實(shí)現(xiàn)是通過開鏈法來實(shí)現(xiàn)的,hash table表格內(nèi)的元素稱為桶(bucket),而由桶所鏈接的元素稱為節(jié)點(diǎn)(node),其中保存桶元素的容器為vector容器抖韩。之所以選擇vector為存放桶元素的基礎(chǔ)容器蛀恩,主要是因?yàn)関ector容器本身具有動態(tài)擴(kuò)容能力,無需人工干預(yù)茂浮。而節(jié)點(diǎn)元素為自定義的結(jié)構(gòu)體:

可以看到双谆,這本身就是一種很典型的鏈?zhǔn)搅斜碓氐谋硎痉椒枪荆ㄟ^當(dāng)前節(jié)點(diǎn),我們可以很方便地通過節(jié)點(diǎn)自身的next指針來獲取下一鏈接節(jié)點(diǎn)元素顽馋。

無序容器在存儲上組織為一組桶谓厘,每個桶保存0個或多個元素。無序容器使用一個哈希函數(shù)將元素映射到桶寸谜。為了訪問一個元素竟稳,(1)容器首先計(jì)算元素的哈希值, 它指出應(yīng)該搜索哪個桶熊痴;(2)容器將具有一個特定哈希值的所有元素都保存在相同的桶中他爸;因此,無序容器的性能依賴于哈希函數(shù)的質(zhì)量和桶數(shù)量和大小果善。

實(shí)際工作中需視不同情況采用不同的哈希函數(shù)诊笤,通常,考慮的因素有:

(1)計(jì)算哈希函數(shù)所需時間(包括硬件指令的因素)岭埠;

(2)關(guān)鍵字的長度盏混;

(3)哈希表的大形蹬浮惜论;

(4)關(guān)鍵字的分布情況;

(5)記錄的查找頻率止喷。

31. priority_queue

priority_queue優(yōu)先級隊(duì)列相當(dāng)于一個有權(quán)值的單向隊(duì)列queue馆类,在這個隊(duì)列中,所有元素是按照優(yōu)先級排列的弹谁。

priority_queue根據(jù)堆的處理規(guī)則來調(diào)整元素之間的位置乾巧,關(guān)于堆的原理,可以參考堆预愤;

根據(jù)堆的特性沟于,優(yōu)先級隊(duì)列實(shí)現(xiàn)了取出最大最小元素時間復(fù)雜度為O(1),? 對于插入和刪除,其最壞情況為O(lgn)植康。

32.STL 空間配置器 allocator?

揭秘——STL空間配置器 - CSDN博客

為什么要有空間配置器呢旷太?這主要是從兩個方面來考慮的

1销睁、小塊內(nèi)存帶來的內(nèi)存碎片問題

單從分配的角度來看供璧。由于頻繁分配、釋放小塊內(nèi)存容易在堆中造成外碎片(極端情況下就是堆中空閑的內(nèi)存總量滿足一個請求冻记,但是這些空閑的塊都不連續(xù)睡毒,導(dǎo)致任何一個單獨(dú)的空閑的塊都無法滿足這個請求)。

2冗栗、小塊內(nèi)存頻繁申請釋放帶來的性能問題演顾。

? 開辟空間的時候供搀,分配器會去找一塊空閑塊給用戶,找空閑塊也是需要時間的钠至,尤其是在外碎片比較多的情況下趁曼。如果分配器其找不到,就要考慮處理假碎片現(xiàn)象(釋放的小塊空間沒有合并)棕洋,這時候就要將這些已經(jīng)釋放的的空閑塊進(jìn)行合并挡闰,這也是需要時間的。 malloc在開辟空間的時候掰盘,這些空間會帶有一些附加的信息摄悯,這樣的話也就造成了空間的利用率有所降低,尤其是在頻繁申請小塊內(nèi)存的時候愧捕。

內(nèi)存池的概念

為了解決上面這些問題奢驯,所以就提出有了內(nèi)存池的概念。內(nèi)存池最基本的思想就是一次向heap申請一塊很大的內(nèi)存(內(nèi)存池)次绘,如果申請小塊內(nèi)存的話就直接到內(nèi)存池中去要瘪阁。這樣的話,就能夠有效的解決上面所提到的問題邮偎。

下面剖析一下STL里面的空間配置器(SGI版)

STL里面的空間配置主要分為兩級管跺,一級空間配置器(__malloc_alloc_template)和二級空間配置器(__default_alloc_template)。在STL中默認(rèn)如果要分配的內(nèi)存大于128個字節(jié)的話就是大塊內(nèi)存禾进,調(diào)用一級空間配置器直接向系統(tǒng)申請豁跑,如果小于等于128個字節(jié)的話則認(rèn)為是小內(nèi)存,則就去內(nèi)存池中申請泻云。

(1)一級空間配置器很簡單艇拍,直接封裝了malloc和free處理,增加_malloc_alloc_oom_handle處理機(jī)制宠纯。

(2)二級空間配置器才是STL的精華卸夕,二級空間配置器主要由? 內(nèi)存池+自由鏈表 ? 構(gòu)成。

? 二級空間配置器使用內(nèi)存池+自由鏈表的形式避免了小塊內(nèi)存帶來的碎片化婆瓜,提高了分配的效率快集,提高了利用率。SGI的做法是先判斷要開辟的大小是不是大于128勃救,如果大于128則就認(rèn)為是一塊大塊內(nèi)存碍讨,調(diào)用一級空間配置器直接分配。否則的話就通過內(nèi)存池來分配蒙秒,假設(shè)要分配8個字節(jié)大小的空間勃黍,那么他就會去內(nèi)存池中分配多個8個字節(jié)大小的內(nèi)存塊,將多余的掛在自由鏈表上晕讲,下一次再需要8個字節(jié)時就去自由鏈表上取就可以了覆获,如果回收這8個字節(jié)的話马澈,直接將它掛在自由鏈表上就可以了。

?為了便于管理弄息,二級空間配置器在分配的時候都是以8的倍數(shù)對齊痊班。也就是說二級配置器會將任何小塊內(nèi)存的需求上調(diào)到8的倍數(shù)處(例如:要7個字節(jié),會給你分配8個字節(jié)摹量。要9個字節(jié)涤伐,會給你16個字節(jié)),盡管這樣做有內(nèi)碎片的問題,但是對于我們管理來說卻簡單了不少缨称。因?yàn)檫@樣的話只要維護(hù)16個free_list就可以了凝果,free_list這16個結(jié)點(diǎn)分別管理大小為8,16,24,32,40,48,56,64,72,80,88,86,96,104,112,120,128字節(jié)大小的內(nèi)存塊就行了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睦尽,一起剝皮案震驚了整個濱河市器净,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌当凡,老刑警劉巖山害,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沿量,居然都是意外死亡浪慌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門欧瘪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眷射,“玉大人匙赞,你說我怎么就攤上這事佛掖。” “怎么了涌庭?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵芥被,是天一觀的道長。 經(jīng)常有香客問我坐榆,道長拴魄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任席镀,我火速辦了婚禮匹中,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豪诲。我一直安慰自己顶捷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布屎篱。 她就那樣靜靜地躺著服赎,像睡著了一般葵蒂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上重虑,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天践付,我揣著相機(jī)與錄音,去河邊找鬼缺厉。 笑死永高,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的提针。 我是一名探鬼主播乏梁,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼关贵!你這毒婦竟也來了遇骑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤揖曾,失蹤者是張志新(化名)和其女友劉穎落萎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炭剪,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡练链,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奴拦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒鼓。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖错妖,靈堂內(nèi)的尸體忽然破棺而出绿鸣,到底是詐尸還是另有隱情,我是刑警寧澤暂氯,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布潮模,位于F島的核電站,受9級特大地震影響痴施,放射性物質(zhì)發(fā)生泄漏擎厢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一辣吃、第九天 我趴在偏房一處隱蔽的房頂上張望动遭。 院中可真熱鬧,春花似錦神得、人聲如沸厘惦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绵估。三九已至炎疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間国裳,已是汗流浹背形入。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缝左,地道東北人亿遂。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像渺杉,于是被迫代替她去往敵國和親蛇数。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 標(biāo)簽(空格分隔): STL 運(yùn)用STL是越,可以充分利用該庫的設(shè)計(jì)耳舅,讓我為簡單而直接的問題設(shè)計(jì)出簡單而直接的解決方案,...
  • 前言: 詳細(xì)介紹: List:元素有放入順序呢岗,元素可重復(fù)Map:元素按鍵值對存儲冕香,無放入順序Set:元素?zé)o放入順序...
    YBshone閱讀 8,620評論 0 17
  • 十八.STL庫 主要包括三大組件:容器、算法后豫、迭代器悉尾。 容器:序列式容器:vector、deque硬贯、list焕襟;關(guān)聯(lián)...
    b83dcb2e8b71閱讀 939評論 0 2
  • 組成:容器,迭代器饭豹,算法 各種容器的元素在內(nèi)存中的儲存方式 1、vector(向量):相當(dāng)于數(shù)組务漩,但其大小可以不預(yù)...
    analanxingde閱讀 364評論 0 0
  • 正值盛夏拄衰,天氣很熱,大街小巷上已經(jīng)見不到多少人了饵骨。要么是在家里吹空調(diào)的翘悉,要么是在QQ空間里看別人吹空調(diào)的瓮顽,這是一個...
    豪少爺閱讀 581評論 0 2