[STL] Implement "vector", 」deque「 and "list"

vector


「可增的」數組

vector是一塊連續分配的內存,從數據安排的角度來說,和數組極其類似。html

不一樣的地方就是:node

(1) 數組是靜態分配空間,一旦分配了空間的大小,就不可再改變了;linux

(2) vector是動態分配空間,隨着元素的不斷插入,它會按照自身的一套機制不斷擴充自身的容量。面試

 

內存模型

vector發現本身的空間不夠了,因而申請新的內存空間(自增一倍),並將前面已有數據複製到新空間的前部。算法

 

Comment

自增一倍,主要是「位移」運算。數組

對於vector增長新元素的時候,有可能很快完成,也有可能要進行擴容,效率降低;dom

刪除末尾元素效率很高,刪除中間元素效率低;函數

 

 

 

deque


雙端隊列

deque是雙端隊列,在接口上和vector很是類似,在許多操做的地方能夠直接替換。post

與vector不一樣的是,deque不能保證全部的元素存儲在連續的空間中,在deque中經過指針加偏移量方式訪問元素可能會致使非法的操做。性能

 

內存模型

Ref: C++ STL學習之三:容器deque深刻學習(轉)

除了在頻繁在頭部或尾部進行插入和刪除操做外,deque比list和forward_list的性能更差。

deque是一種優化了的對序列兩端元素進行添加和刪除操做的基本序列容器。

一般由一些獨立的區塊組成,第一區塊朝某方向擴展,最後一個區塊朝另外一方向擴展。

它容許較爲快速地隨機訪問但它不像vector同樣把全部對象保存在一個連續的內存塊,而是多個連續的內存塊。而且在一個映射結構中保存對這些塊以及順序的跟蹤。

 

Ref: STL源碼剖析---deque

deque採用一塊所謂的map(注意,不是STL的map容器)做爲主控。這裏所謂map是一小塊連續空間,其中每一個元素(此處稱爲一個節點,node)都是指針,指向另外一段(較大的)連續線性空間,稱爲緩衝區。緩衝區纔是deque的儲存空間主體。

SGI STL 容許咱們指定緩衝區大小,默認值0表示將使用512 bytes 緩衝區。 

deque的迭代器

讓咱們思考一下,deque的迭代器應該具有什麼結構,首先,

  1. 它必須可以指出分段連續空間(亦即緩衝區)在哪裏;
  2. 其次它必須可以判斷本身是否已經處於其所在緩衝區的邊緣,若是是,一旦前進或後退就必須跳躍至下一個或上一個緩衝區。

爲了可以正確跳躍,deque必須隨時掌握管控中心(map)。因此在迭代器中須要定義:當前元素的指針,當前元素所在緩衝區的起始指針,當前元素所在緩衝區的尾指針,指向map中指向所在緩區地址的指針。

 

 

List


內核鏈表 

From: 深刻分析 Linux 內核鏈表

struct list_head { struct list_head *next, *prev; }; #define list_entry(ptr, type, member) container_of(ptr, type, member)
// container_of宏定義在[include/linux/kernel.h]中 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
// offsetof宏定義在[include
/linux/stddef.h]中 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

 

鏈表問答

Ref: [算法總結] 一文搞懂面試鏈表題

在 O(1) 時間刪除鏈表節點 - 單向鏈表

作一次複製便可。

 

反轉鏈表 O(n)

用三個臨時指針 prev、cur、next 在鏈表上循環一遍便可。

 

旋轉單鏈表,以及刪除單鏈表倒數第 n 個節點

快指針結合慢指針,相同步幅走

 

求單鏈表的中間節點

快指針結合慢指針,不一樣步幅走

 

鏈表劃分,大於x的排後面;小於x的排前面

初始化兩個鏈表leftList, rightList,遍歷原鏈表。

 

在O(nlogn)時間內對鏈表進行排序

快排或者並歸 --> Goto: 排序章節

 

合併兩個排序的鏈表

同步指針法

 

刪除鏈表中重複的結點

利用兩個指針,相似冒泡排序

 

判斷單鏈表是否存在環

快慢指針,慢指針每次移動一步,快指針每次移動兩步,若是存在環,那麼兩個指針必定會在環內相遇。

 

找到環的入口點

咱們設置兩個指針,一個是快指針fast,一個是慢指針slow,fast一次走兩步,slow一次走一步,若是單鏈表有環那麼當兩個指針相遇時必定在環內。

此時將一個指針指到鏈表頭部,另外一個不變,兩者同時每次向前移一格,當兩個指針再次相遇時即爲環的入口節點。若是fast走到null則無環。

 

判斷兩個無環單鏈表是否相交

尾巴必然同樣;或者用」有環「的思想去解題。

 

求兩個無環單鏈表的第一個相交點

 妙哉,四種方法四個角度:

    • 方法一 [倒轉鏈表能夠用」棧「] 若是兩個鏈表存在公共結點,那麼它們從公共結點開始一直到鏈表的結尾都是同樣的,所以咱們只須要從鏈表的結尾開始,往前搜索,找到最後一個相同的結點便可。可是題目給出的單向鏈表,咱們只能從前向後搜索,這時,咱們就能夠藉助棧來完成。先把兩個鏈表依次裝到兩個棧中,而後比較兩個棧的棧頂結點是否相同,若是相同則出棧,若是不一樣,那最後相同的結點就是咱們要的返回值。
    • 方法二 [利用尾部共享] 先找出2個鏈表的長度,而後讓長的先走兩個鏈表的長度差,而後再一塊兒走,直到找到第一個公共結點。
    • 方法三 [環的入口點] 因爲2個鏈表都沒有環,咱們能夠把第二個鏈表接在第一個鏈表後面,這樣就把問題轉化爲求環的入口節點問題。
    • 方法四 [互補長度一致策略] 兩個指針p1和p2分別指向鏈表A和鏈表B,它們同時向前走,當走到尾節點時,轉向另外一個鏈表,好比p1走到鏈表 A 的尾節點時,下一步就走到鏈表B,p2走到鏈表 B 的尾節點時,下一步就走到鏈表 A,當p1==p2 時,就是鏈表的相交點

 

判斷兩個有環單鏈表是否相交

若是兩個有環單鏈表相交,那麼它們必定共有一個環,即環上的任意一個節點都存在於兩個鏈表上。

 

複雜鏈表的複製

題目描述:輸入一個複雜鏈表(每一個節點中有節點值,以及兩個指針,一個指向下一個節點,另外一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,不然判題程序會直接返回空)

第二個指針如何複製的問題?

奇偶複製。這樣,random指針指向的結點天然知道」本身的copy"的位置。

 

 

 

Understand the cons and pros


 

 

 

 

 

數組 & 單鏈表


std::array

What is the difference between std::array and std::vector? When do you use one over other? [duplicate]

std::array is just a class version of the classic C array. That means its size is fixed at compile time and it will be allocated as a single chunk (e.g. taking space on the stack). The advantage it has is slightly better performance because there is no indirection between the object and the arrayed data. 

 

std::forward_list

forward_list 容器以單鏈表的形式存儲元素。forward_list 的模板定義在頭文件 forward_list 中。fdrward_list 和 list 最主要的區別是:它不能反向遍歷元素;只能從頭至尾遍歷。
forward_list 的單向連接性也意味着它會有一些其餘的特性:

  1. 沒法使用反向迭代器。只能從它獲得const或non-const前向迭代器,這些迭代器都不能解引用,只能自增;
  2. 沒有能夠返回最後一個元素引用的成員函數back();只有成員函數front();
  3. 由於只能經過自增前面元素的迭代器來到達序列的終點,因此push_back()、pop_back()、emplace_back()也沒法使用。

forward_list 的操做比 list 容器還要快,並且佔用的內存更少,儘管它在使用上有不少限制,但僅這一點也足以讓咱們滿意了。

 

End.

相關文章
相關標籤/搜索