學(xué)好這 13 種數(shù)據(jù)結(jié)構(gòu),應(yīng)對(duì)各種編程語言(C++ 版)

學(xué)了這么長(zhǎng)時(shí)間數(shù)據(jù)結(jié)構(gòu)和算法,有必要來個(gè)總結(jié)了,順便回顧一下我們這段時(shí)間的學(xué)習(xí)成果。以 C++ 語言本身提供的數(shù)據(jù)結(jié)構(gòu)為例。如果能掌握這 13 種數(shù)據(jù)結(jié)構(gòu),相信在學(xué)習(xí)其它語言的時(shí)候就不費(fèi)勁了。?

數(shù)組 Array?

看我主頁簡(jiǎn)介免費(fèi)C++學(xué)習(xí)資源,視頻教程、職業(yè)規(guī)劃、面試詳解、學(xué)習(xí)路線、開發(fā)工具

每晚8點(diǎn)直播講解C++編程技術(shù)。

數(shù)組在初始化的時(shí)候就需要知道其大小,后續(xù)是不可以改變其大小的,可以通過下標(biāo)來獲取某個(gè) index 中存放的元素。在 C++ 中通過源碼可以知道,它其實(shí)是在 C 數(shù)組的基礎(chǔ)上封裝的:?

#include?<array>

void?testArray()?{

// 創(chuàng)建一個(gè)數(shù)組

std::array a = {2,?4,?6,?8,?10};

// 對(duì)數(shù)組進(jìn)行遍歷

for?(const?auto?i : a) {

std::cout?<< i <<?std::endl;

}

for(int?i =?0; i < a.size(); i++) {

std::cout?<< a[i] <<?std::endl;

}

// 取第一個(gè)值,通過 [index] 獲取

std::cout?<<?"a[0] = "?<< a[0] <<?std::endl;

// 修改數(shù)組中第一個(gè)值

a[0] =?5;

/**

at 會(huì)檢查 index 是否越界,越界將 crash,而 a[index] 不會(huì);

libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: array::at

*/

a.at(0);

// 數(shù)組是否為空

a.empty();

// 求數(shù)組的長(zhǎng)度

std::cout?<<?"a.size()="?<< a.size() <<?std::endl;

// 獲取第4個(gè)值

int?value =?std::get<4>(a);

std::cout?<<?"a(4) = "?<< value <<?std::endl;

// 創(chuàng)建一個(gè)空數(shù)組,數(shù)組中的值為0或者是與類型相等的其它值

std::array a2;

std::cout?<<?"end a2"?<< a2[0] <<?std::endl;

// 比較兩個(gè)數(shù)組中的元素是否都相等

if?(a == a2) {}

}

可變數(shù)組,向量vector

在C++中使用 Vector 存當(dāng)可變數(shù)組,它的容量可以動(dòng)態(tài)改變,初始化的時(shí)候不需要確定數(shù)組的大小。使用的方法和數(shù)組基本一致。

#include?<vector>

// 向量

void?testVector()?{

/**

vector<T> 它與Array不同,它的大小是可變的

*/

std::vector v;

// 增加容器的容量,至少容納 20 個(gè)元素

v.reserve(20);

// 初始化一個(gè)向量

std::vector v2 = {2,?4,?5,?7};

// 以數(shù)組初始化一個(gè)vector

std::array words {"one",?"two","three",?"four",?"five"};

std::vector words_copy {std::begin(words) ,?std::end(words)};

// 通過 v[index] 獲取或者修改元素

std::cout?<<?"v[0] = "?<< v2[1] <<?std::endl;

// 獲取第一個(gè)元素

std::cout?<<?"v.front() = "?<< v2.front() <<?std::endl;

// 在末尾插入值為 9

v2.push_back(9);

// 在末尾插入值為 2

v2.emplace_back(2);

// 刪除第一個(gè)元素,傳入的值是一個(gè)迭代器

v2.erase(v2.begin());

// 長(zhǎng)度

v2.size();

// 刪除所有元素

v2.clear();

// 刪除末尾元素

v2.pop_back();

// 在末尾插入元素

v2.insert(v2.end(),?3);

}

雙向鏈表 list?

雙向鏈表具有指向前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)的指針。C++ 中本身提供了雙向鏈表的實(shí)現(xiàn)。

#include?<list>

void?testList()?{

list words {"hello",?"list"};

// 頭部插入元素

words.push_front("push_fron");

words.emplace_front("emplace_front");

// 尾部插入

words.push_back("push_back");

words.emplace_back("emplace_back");

// 指定位置插入

words.insert(words.begin()++,?"insert");

// 刪除元素

words.remove("push_fron");

// 通過迭代器來訪問鏈表中的元素

list::iterator beg_iter = words.begin();

list::iterator end_iter = words.end();

cout?<<?"beg_iter:"?<< *beg_iter <<?endl;

cout?<<?"end_iter:"?<< *end_iter <<?endl;

for?(auto?a : words) {

cout?<< a <<?endl;

}

}

單鏈表 forward_list?

單鏈表只有指向下一個(gè)節(jié)點(diǎn)的指針,前面關(guān)于單鏈表我們做了好多算法題。

#include?<forward_list>

void?testForwardList()?{

forward_list flist {"name",?"lefe",?"hello"};

// 計(jì)算它的大小

auto?count = distance(begin(flist), end(flist));

cout?<<?"size:"?<< count <<?endl;

// 頭部插入

flist.push_front("before3");

// 在頭部插入

flist.insert_after(flist.begin(),?"before2");

// 在頭結(jié)點(diǎn)前面插入

flist.insert_after(flist.before_begin(),?"before1");

// 遍歷單鏈表

for?(auto?word : flist) {

cout?<< word <<?endl;

}

}

隊(duì)列 queue

隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),C++底層使用「雙端隊(duì)列」實(shí)現(xiàn)。關(guān)于隊(duì)列的更多內(nèi)容,可以看這篇內(nèi)容?圖解設(shè)計(jì)一個(gè)循環(huán)隊(duì)列。

#include?<queue>

void?testQueue()?{

queue s;

// 入隊(duì)

s.push(1);

// 出隊(duì)

s.pop();

// 隊(duì)首

s.front();

// 隊(duì)尾

s.back();

// 隊(duì)大小

s.size();

}

雙端隊(duì)列deque

雙端隊(duì)列可以對(duì)隊(duì)頭和隊(duì)尾元素進(jìn)行操作。在做算法的時(shí)候我們?cè)O(shè)計(jì)過一個(gè)雙端隊(duì)列圖解設(shè)計(jì)一個(gè)雙端隊(duì)列。?

void?testDeque()?{

// 初始化一個(gè)空雙端隊(duì)列

deque my_deque;

// 初始化一個(gè)含有兩個(gè)元素雙端隊(duì)列

deque name_queue {"lefe",?"wsy"};

cout?<<?"[0] = "?<< name_queue[0] <<?endl;

// 獲取隊(duì)頭元素

cout?<<?"front = "?<< name_queue.front() <<?endl;

// 獲取隊(duì)尾元素

cout?<<?"back = "?<< name_queue.back() <<?endl;

// 從尾部入隊(duì)

name_queue.push_back("bx");

name_queue.pop_front();

}

優(yōu)先隊(duì)列 priority_queue

優(yōu)先隊(duì)列和普通隊(duì)列一樣只能在隊(duì)尾插入元素,隊(duì)頭刪除元素,但是它有一個(gè)特點(diǎn),隊(duì)頭永遠(yuǎn)是優(yōu)先級(jí)最大的元素,出隊(duì)順序與入隊(duì)順序無關(guān)。C++ 中的底層使用「堆」實(shí)現(xiàn)的,這樣時(shí)間復(fù)雜度可以控制在 O(logn)。

void?testPriorityQueue()?{

// 創(chuàng)建優(yōu)先隊(duì)列

priority_queue q;

// 向隊(duì)列中添加元素

q.push(4);

q.push(1);

for(int?i =?0?; i < q.size() ; i++) {

cout?<< q.top() <<?endl;

// 刪除第一個(gè)元素

q.pop();

}

// 隊(duì)列是否為空

q.empty();

}

堆heap

堆是一顆完全二叉樹,某個(gè)節(jié)點(diǎn)的值總是不大于父節(jié)點(diǎn)的值(大根堆),可以使用數(shù)組來表示一個(gè)堆,C++ 默認(rèn)提供的是大根堆。在堆排序中我們有詳細(xì)介紹了堆。?圖解排序 6/10 - 堆排序(題目寫出了)。在這篇文章中,我們實(shí)現(xiàn)了一個(gè)堆動(dòng)手寫個(gè)“堆”。?

C++ 中的堆是通過算法實(shí)現(xiàn)的,需要導(dǎo)入?#include <algorithm>

#include?<algorithm>

void?testHeap()?{

vector numbers {6,?20,?7,?3,?5};

/**提供判斷方法*/

// make_heap(numbers.begin(), numbers.end(), [](int a,int b){return a < b;});

// 創(chuàng)建堆后,numbers 中元素的值為: 20,6,7,3,5,大根堆

make_heap(numbers.begin(), numbers.end(), [](int?a,int?b){return?a < b;});

// 向堆中添加元素

numbers.push_back(40);

// 重組堆:40,6,20,3,5,7 大根堆,調(diào)用 push_heap 先確保先前的 vertor 是個(gè)堆

push_heap(numbers.begin(), numbers.end());

// 移除堆頂元素,需要把 numbers 中的尾部元素移除,不會(huì)自動(dòng)移除

pop_heap(numbers.begin(), numbers.end());

numbers.pop_back();

}

棧 stack?

棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),C++ 底層使用雙端隊(duì)列實(shí)現(xiàn)。在以前最小棧算法中我們?cè)敿?xì)介紹了這種數(shù)據(jù)結(jié)構(gòu)。?圖解最小棧(LeetCode155. Min Stack)?。

#include?<stack>

void?testStack()?{

stack s;

s.push(1);

s.pop();

cout?<<?"top="?<< s.top() <<?endl;

s.size();

}

映射 map、unordered_map

map 是一種保存 key 和 vaule 的數(shù)據(jù)結(jié)構(gòu),key 是唯一的。C++ 中提供了有序的 map 和無序的 map 「unordered_map」。

#include?<unordered_map>

#include?<map>

void?testMap()?{

// 初始化

map m;

pair::iterator,?bool> ret_iter =

m.insert(pair("name",?"lefe"));

if?(ret_iter.second ==?false) {

cout?<<?"name have existed"?<<?endl;

}

// 初始化

map m2 = {

{2014,?"iOS"},

{2015,?"Android"},

};

// 單值插入

m["name"] =?"wsy";

// 多值插入

m.insert({{"age",?"20"}, {"from",?"nmg"}});

cout?<<?"size = "?<< m.size() <<?endl;

// 使用迭代器刪除

m.erase(m.begin());

// 查找

map::iterator find_iter = m.find("from");

if?(find_iter != m.end()) {

cout?<<?"find"?<<?endl;

}

// 遍歷, key 是有序的

for?(pair v : m) {

cout?<< v.first <<?" = "?<< v.second <<?endl;

}

// 刪除全部元素

m.clear();

}

pair

pair中保存了兩個(gè)值,這兩個(gè)值的類型可以是任意類型,也可以不同。通過 first 和 second 來獲取對(duì)應(yīng)的值。

void?testPair()?{

pair p = {"name",?"lefex"};

// 通過first 和 second 來獲取第一個(gè)和第二個(gè)值

cout?<< p.first;

cout?<< p.second;

}

元組 tuple

它是 pair 的擴(kuò)充版,可以存儲(chǔ)多個(gè)不同類型的元素。

void?testTuple()?{

pair p = {"name",?"lefe"};

// 創(chuàng)建一個(gè) tuple,類型為 <strinng, int, double, pair>

auto?my_tuple = make_tuple("name",?20,?10.3, p);

// 獲取第一個(gè)元素

cout?<<?"0 = "?<< get<0>(my_tuple) <<?endl;

// 獲取第二個(gè)元素

cout?<<?"1 = "?<< get<1>(my_tuple) <<?endl;

}

集合 set?

集合中不能存儲(chǔ)相同的元素,它底層基于平衡二叉樹實(shí)現(xiàn),在前面的文章中我們通過二分搜索樹實(shí)現(xiàn)了 set。使用 BST 實(shí)現(xiàn) Set。在 C++ 中 set 是有序的,同時(shí)也提供了無序的 set 「UnorderSet」。?

#include?<set>

#include?<unordered_set>

void?testSet()?{

set s {7,?3,?4};

s.insert(5);

// 3,4,5,7

for?(auto?v : s) {

cout?<< v <<?endl;

}

unordered_set us {7,?3,?4};

us.insert(5);

// 7,3,4,5

for?(auto?v : us) {

cout?<< v <<?endl;

}

}

總結(jié)

我們介紹了 C++ 語言本身提供的數(shù)據(jù)結(jié)構(gòu),有線性結(jié)構(gòu)和非線性結(jié)構(gòu)。這些數(shù)據(jù)結(jié)構(gòu)在其它語言中幾乎也會(huì)提供,而且底層實(shí)現(xiàn)基本一致,所有只有掌握了這些數(shù)據(jù)結(jié)構(gòu)原理,在學(xué)習(xí)一門其它語言變的非常輕松,調(diào)用 API 時(shí)更爽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容