容器和演算法

2021-09-22 20:12:49 字數 4858 閱讀 8644

map和set都是c++的關聯容器,其底層實現都是紅黑樹(rb-tree)。由於 map 和set所開放的各種操作介面,rb-tree 也都提供了,所以幾乎所有的 map 和set的操作行為,都只是轉調 rb-tree 的操作行為。

map和set區別在於:

(2)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。其原因是因為map和set是根據關鍵字排序來保證其有序性的,如果允許修改key的話,那麼首先需要刪除該鍵,然後調節平衡,再插入修改後的鍵值,調節平衡,如此一來,嚴重破壞了map和set的結構,導致iterator失效,不知道應該指向改變前的位置,還是指向改變後的位置。所以stl中將set的迭代器設定成const,不允許修改迭代器的值;而map的迭代器則不允許修改key值,允許修改value值。

stl的分配器用於封裝stl容器在記憶體管理上的底層細節。在c++中,其記憶體配置和釋放如下:

new運算分兩個階段:(1)呼叫

::operator new

配置記憶體;(2)呼叫物件建構函式構造物件內容

delete運算分兩個階段:(1)呼叫物件希構函式;(2)呼叫

::operator delete

釋放記憶體

為了精密分工,stl allocator將兩個階段操作區分開來:記憶體配置有alloc::allocate()負責,記憶體釋放由alloc::deallocate()負責;物件構造由::construct()負責,物件析構由::destroy()負責。

同時為了提公升記憶體管理的效率,減少申請小記憶體造成的記憶體碎片問題,sgi stl採用了兩級配置器,當分配的空間大小超過128b時,會使用第一級空間配置器;當分配的空間大小小於128b時,將使用第二級空間配置器。第一級空間配置器直接使用malloc()、realloc()、free()函式進行記憶體空間的分配和釋放,而第二級空間配置器採用了記憶體池技術,通過空閒鍊錶來管理記憶體。

這個主要考察的是迭代器失效的問題。

1.對於序列容器vector,deque來說,使用erase(itertor)後,後邊的每個元素的迭代器都會失效,但是後邊每個元素都會往前移動乙個位置,但是erase會返回下乙個有效的迭代器;

2.對於關聯容器map set來說,使用了erase(iterator)後,當前元素的迭代器失效,但是其結構是紅黑樹,刪除當前元素的,不會影響到下乙個元素的迭代器,所以在呼叫erase之前,記錄下乙個元素的迭代器即可。

3.對於list來說,它使用了不連續分配的記憶體,並且它的erase方法也會返回下乙個有效的iterator,因此上面兩種正確的方法都可以使用。

紅黑樹。unordered map底層結構是雜湊表

stl主要由:以下幾部分組成:

他們之間的關係:分配器給容器分配儲存空間,演算法通過迭代器獲取容器中的內容,仿函式可以協助演算法完成各種操作,配接器用來套接適配仿函式

1、map對映,map 的所有元素都是 pair,同時擁有實值(value)和鍵值(key)。pair 的第一元素被視為鍵值,第二元素被視為實值。所有元素都會根據元素的鍵值自動被排序。不允許鍵值重複。

底層實現:紅黑樹

適用場景:有序鍵值對不重複對映

2、multimap

多重對映。multimap 的所有元素都是 pair,同時擁有實值(value)和鍵值(key)。pair 的第一元素被視為鍵值,第二元素被視為實值。所有元素都會根據元素的鍵值自動被排序。允許鍵值重複。

底層實現:紅黑樹

適用場景:有序鍵值對可重複對映

1、概念:

1)vector

連續儲存的容器,動態陣列,在堆上分配空間

底層實現:陣列

兩倍容量增長:

vector 增加(插入)新元素時,如果未超過當時的容量,則還有剩餘空間,那麼直接新增到最後(插入指定位置),然後調整迭代器。

如果沒有剩餘空間了,則會重新配置原有元素個數的兩倍空間,然後將原空間元素通過複製的方式初始化新空間,再向新空間增加元素,最後析構並釋放原空間,之前的迭代器會失效。

效能:訪問:o(1)

插入:在最後插入(空間夠):很快

在最後插入(空間不夠):需要記憶體申請和釋放,以及對之前資料進行拷貝。

在中間插入(空間夠):記憶體拷貝

在中間插入(空間不夠):需要記憶體申請和釋放,以及對之前資料進行拷貝。

刪除:在最後刪除:很快

在中間刪除:記憶體拷貝

適用場景:經常隨機訪問,且不經常對非尾節點進行插入刪除。

2、list

動態鍊錶,在堆上分配空間,每插入乙個元數都會分配空間,每刪除乙個元素都會釋放空間。

底層:雙向鍊錶

效能:訪問:隨機訪問效能很差,只能快速訪問頭尾節點。

插入:很快,一般是常數開銷

刪除:很快,一般是常數開銷

適用場景:經常插入刪除大量資料

2、區別:

1)vector底層實現是陣列;list是雙向 鍊錶。

2)vector支援隨機訪問,list不支援。

3)vector是順序記憶體,list不是。

4)vector在中間節點進行插入刪除會導致記憶體拷貝,list不會。

5)vector一次性分配好記憶體,不夠時才進行2倍擴容;list每次插入新節點都會進行記憶體申請。

6)vector隨機訪問效能好,插入刪除效能差;list隨機訪問效能差,插入刪除效能好。

3、應用

vector擁有一段連續的記憶體空間,因此支援隨機訪問,如果需要高效的隨即訪問,而不在乎插入和刪除的效率,使用vector。

list擁有一段不連續的記憶體空間,如果需要高效的插入和刪除,而不關心隨機訪問,則應使用list。

1、迭代器

iterator(迭代器)模式又稱cursor(游標)模式,用於提供一種方法順序訪問乙個聚合物件中各個元素, 而又不需暴露該物件的內部表示。或者這樣說可能更容易理解:iterator模式是運用於聚合物件的一種模式,通過運用該模式,使得我們可以在不知道物件內部表示的情況下,按照一定順序(由iterator提供的方法)訪問聚合物件中的各個元素。

由於iterator模式的以上特性:與聚合物件耦合,在一定程度上限制了它的廣泛運用,一般僅用於底層聚合支援類,如stl的list、vector、stack等容器類及ostream_iterator等擴充套件iterator。

2、迭代器和指標的區別

迭代器不是指標,是類模板,表現的像指標。他只是模擬了指標的一些功能,通過過載了指標的一些操作符,->、*、++、--等。迭代器封裝了指標,是乙個「可遍歷stl( standard template library)容器內全部或部分元素」的物件, 本質是封裝了原生指標,是指標概念的一種提公升(lift),提供了比指標更高階的行為,相當於一種智慧型指標,他可以根據不同型別的資料結構來實現不同的++,--等操作。

迭代器返回的是物件引用而不是物件的值,所以cout只能輸出迭代器使用*取值後的值而不能直接輸出其自身。

3、迭代器產生原因

iterator類的訪問方式就是把不同集合類的訪問邏輯抽象出來,使得不用暴露集合內部的結構而達到迴圈遍歷集合的效果。

呼叫順序:

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

首先建立乙個epoll物件,然後使用epoll_ctl對這個物件進行操作,把需要監控的描述新增進去,這些描述符將會以epoll_event結構體的形式組成一顆紅黑樹,接著阻塞在epoll_wait,進入大迴圈,當某個fd上有事件發生時,核心將會把其對應的結構體放入到乙個鍊錶中,返回有事件發生的鍊錶。

單調棧

vectorfindmax(vectornum)

else

}while (!s.empty())

for (int i = 0; i < res.size(); i++)

cout << res[i] << endl;

return res;

}

resize():改變當前容器內含有元素的數量(size()),eg:

vectorv;

v.resize(len);

v的size變為len,如果原來v的size小於len,那麼容器新增(len-size)個元素(初始化為0),元素的值為預設為0.當v.push_back(3);之後,則是3是放在了v的末尾,即下標為len,此時容器是size為len+1;

reserve():改變當前容器的最大容量(capacity),它不會生成元素,只是確定這個容器允許放入多少物件,如果reserve(len)的值大於當前的capacity(),那麼會重新分配一塊能存len個物件的空間,然後把之前v.size()個物件通過copy construtor複製過來,銷毀之前的記憶體;

測試**如下:

#include #include using namespace std;

int main()

集合,所有元素都會根據元素的值自動被排序,且不允許重複。

底層實現:紅黑樹

set 底層是通過紅黑樹(rb-tree)來實現的,由於紅黑樹是一種平衡二叉搜尋樹,自動排序的效果很不錯,所以標準的 stl 的 set 即以 rb-tree 為底層機制。又由於 set 所開放的各種操作介面,rb-tree 也都提供了,所以幾乎所有的 set 操作行為,都只有轉呼叫 rb-tree 的操作行為而已。

適用場景:有序不重複集合

2、map

對映。map 的所有元素都是 pair,同時擁有實值(value)和鍵值(key)。pair 的第一元素被視為鍵值,第二元素被視為實值。所有元素都會根據元素的鍵值自動被排序。不允許鍵值重複。

底層:紅黑樹

適用場景:有序鍵值對不重複對映

容器和演算法

在我們編寫每乙個程式時,都會或多或少的需要儲存一些資料,而c 在這方面只提供了幾種最基本的方法,我們可以建立區域性變數或者全域性變數在存放某個值,也可以用陣列來存放多個值。陣列是c 唯一支援的容器,但陣列並不適合用來解決所有的問題 假如我們編寫乙個程式 輸入一串字母,判斷與給定單詞是否一樣。這時候我...

C 容器和演算法

c 容器和演算法 容器部分 順序容器 vector deque list 通過元素在容器中的位置順序儲存和訪問元素 vector 支援快速隨機訪問 容器介面卡為stack後進先出棧 deque 雙端佇列 容器介面卡為priority queue有優先順序管理的佇列 list 支援快速插入 刪除 容器...

C 容器和演算法

1.map和set有什麼區別,分別又是怎麼實現的?2.stl的allocator?c 容器的空間配置器,由兩級分配器構成,大於128位元組,呼叫一級配置器,malloc free,realloc 小於128位元組,預設二級配置器,分配記憶體池。為了便於記憶體管理,減少記憶體碎片產生 3.stl 標準...