校招面試擴展知識點---STL容器

零、前言

筆者再一次的參加了字節的面試,倒在了二面,自己的技術棧是真的太垃圾了,二面的HR給了我很多的建議和指導,希望自己三年後可以去字節上班哦~~
HR給目前的我推薦了一本神一樣的書籍《C++ Primer》,才知道自己的大學是有多麼的垃圾呀,本文的基礎來源於個人看書之後的總結,大家也一起學一下,順便,祝可愛的豬崽有一份很好的工作~

一、C++庫引用(Import C++ Library)

衆所周知,常用的庫引用如下:

#include  <iostream>  
#include  <cstdio>  
#include <fstream>  
#include <algorithm>  
#include <cmath>  
#include <deque>  
#include <vector>  
#include <queue>  
#include <string>  
#include <cstring>  
#include <map>  
#include <stack>  
#include <set>  

有些太多,所以有了一個新奇的方法:

#include <bits/stdc++.h>

一行代碼解決一系列代碼,方便!

其中 bits/stdc++.h 源碼如下:


/** @file stdc++.h
 *  This is an implementation file for a precompiled header.
 */
// 17.4.1.2 Headers

#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdalign>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif

// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif

不過 VS 會報錯,網上解決辦法一大堆,這裏就不過多贅述了。

二、STL(Standard Template Library)

  1. STL(Standard Template Library),中文名 標準模板庫,是一個高效的C++程序庫,包含很多常用的 基本數據結構 和 基本算法,爲C++程序員們提供了一個可擴展的應用框架,高度體現了軟件的可複用性。
  2. 從邏輯層次來看,在STL中體現了 泛型編程思想(generic programming)。
  3. 從實現層次看,整個STL是以一種 類型參數化(type parameterized) 的方式實現的。
    STL有六大組件(容器(containers)、迭代器(iterators)、空間配置器(allocator)、配接器(adapters)、算法(algorithms)、仿函數(functors))。

但主要是容器、迭代器和算法三個部分~~

容器(Containers):用來管理某類對象的集合。對最常用的數據結構提供了支持,每一種容器都有其優點和缺點,爲了應付程序中的不同需求。

迭代器(Iterators):用來在一個對象集合的元素上進行遍歷動作。這個對象集合或許是個容器,或許是容器的一部分,每種容器都提供了了解該種容器內部結構的迭代器。

算法(Algorithms):用來處理對象集合中的元素。通過所有容器的迭代器提供一致的接口,可以多次複用算法於任意容器之上。

STL 的基本觀念就是將數據和操作分離。

數據由 容器 進行管理;
操作由 算法進行;
而 迭代器 在兩者之間充當粘合劑,使任何 算法 都可以和任何 容器 交互運作。

三、容器(Containers)

一個容器就是一些特定類型對象的集合,是用來管理某類對象的,從C++11標準以來,C++中STL定義的幾種容器的效率非常高,優化的非常好,完全沒有必要自己去定義類似的數據結構,瞭解使用它們,可以滿足90%的日常編程需要!本文是 基於C++11標準,基於《C++primer》參考 完成的。

常用的STL基本容器類型分爲四類:

順序容器(Sequence containers),爲程序員提供了控制元素存儲和訪問順序的能力。順序性容器中的每個元素均有固定的位置,取決於插入時機和地點,和元素值無關,除非用刪除或插入的操作改變這個位置。

關聯容器(Associative containers),支持高效的關鍵字查找和訪問操作。關聯容器中各元素間沒有嚴格的物理順序,取決於特定的排序準則以及元素值,和插入次序無關,元素是有序的集合。默認情況下,標準庫使用關鍵字類型的 < 運算符來進行比較操作。

無序容器(Unordered associative container),使用 哈希函數 和關鍵字類型的 == 運算符組織元素。在關鍵字類型的元素沒有明顯的序關係的情況下,無序容器是非常有用的。在某些應用中,維護元素的序代價非常高昂, 此時無序容器也很有用。使用無序容器通常更爲簡單(通常也會有更好的性能) 。

容器適配器(Adaptor),是標準庫中的一個通用概念,容器、迭代器和函數都有適配器。本質上,一個適配器是一種機制,能使某種事物的行爲看起來像另外一種事物一樣的一種機制。適配器是容器的接口,它本身不能直接保存元素,它保存元素的機制是調用另一種順序容器去實現,即可以把適配器看作"它保存一個容器,這個容器再保存所有元素"。

其中,STL 提供的 最常用的:

四個 順序容器
向量(vector);
雙端隊列(deque);
列表(list);
字符串(string);
四個 關聯容器
集合(set);多重集合(multiset);
映射(map);多重映射(multimap);
三種 適配器
棧(stack);

隊列(queue);
優先級隊列(priority_queue);
四種 無序容器
unordered_map;
unordered_multimap;
unordered_set;
unordered_multiset;
容器類自動申請和釋放內存,因此無需new和delete操作。

四、順序容器(Sequence containers)

4.1)常用操作(共同點)

1_添加元素

在這裏插入圖片描述

2_訪問元素

在這裏插入圖片描述

3_刪除元素

在這裏插入圖片描述

4_改變容器大小

在這裏插入圖片描述

5_容器操作可能使迭代器失效

向容器中添加或刪除元素可能會使指向容器元素的指針、引用或迭代器失效。失效的指針、引用或迭代器不再表示任何元素,使用它們是一種嚴重的程序設計錯誤。

向容器中添加元素後:
如果容器是 vector 或 string 類型,且存儲空間被重新分配,則指向容器的迭代器、指針和引用都會失效。如果存儲空間未重新分配,指向插入位置之前元素的迭代器、指針和引用仍然有效,但指向插入位置之後元素的迭代器、指針和引用都會失效。

如果容器是 deque 類型,添加到除首尾之外的任何位置都會使迭代器、指針和引用失效。如果添加到首尾位置,則迭代器會失效,而指針和引用不會失效。

如果容器是 list 或 forward_list 類型,指向容器的迭代器、指針和引用仍然有效。
從容器中刪除元素後,指向被刪除元素的迭代器、指針和引用失效:

如果容器是 list 或 forward_list 類型,指向容器其他位置的迭代器、指針和引用仍然有效。

如果容器是 deque 類型,刪除除首尾之外的任何元素都會使迭代器、指針和引用失效。如果刪除尾元素,則尾後迭代器失效,其他迭代器、指針和引用不受影響。如果刪除首元素,這些也不會受影響。

如果容器是 vector 或 string 類型,指向刪除位置之前元素的迭代器、指針和引用仍然有效。但尾後迭代器總會失效。

4.2)向量(vector)

vector(向量):事實上和數組差不多,但比數組更優越,一般來說數組不能動態拓展,因此在程序運行的時候不是浪費內存,就是造成越界,而 vector 正好彌補了這個缺陷,它的特徵是相當於可變大小的數組(動態數組)。由於元素是連續存儲的,隨機訪問快,在末端插入和刪除快,但在中間插入和刪除慢。

優缺點:

優點:支持隨機訪問,即 [] 操作和 .at(),查詢效率高。

缺點:當向頭部或中部,插入或刪除元素,插入效率低。

需要導入頭文件 #include 。

1_定義和初始化:

在這裏插入圖片描述

2_簡單vector操作

在這裏插入圖片描述

3_關鍵概念: vector對象能高效增長

C++標準要求 vector 應該能在運行時高效快速地添加元素,因此定義 vector 對象的大小沒有必要,事實上性能可能更差,只有一種例外情況,就是所有元素的值都一樣!一旦元素的值有所不同,更有效的辦法是先定義一個空的 vector 對象,再在運行時向其中添加具體值。

開始的時候創建空的 vector 對象,在運行時再動態添加元素,這一做法與C語言及其他大多數語言中內置數組類型的用法不同,特別是如果用慣了C或者Java,可以預計在創建 vector 對象時順便指定其容量是最好的,然而事實上,通常的情況是恰恰相反。

4_實例

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> ivec1;
	for (int i = 0; i<9; i++)
		ivec1.push_back(i);
	cout << ivec1.size() << endl;
	for (int i = 0; i<ivec1.size(); i++)
		cout << ivec1[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

4.3)雙端隊列(deque)

deque(雙端隊列):是一個更爲複雜的數據結構,最大任務就是在這些分段的連續空間上,維護其整體連續的假象,並提供隨機存取的接口。與 vector 類似,隨機訪問快,不過是在兩端插入和刪除快,但在中間插入和刪除慢。

優缺點:

優點:支持隨機訪問,即 [] 操作和 .at(),查詢效率高;當向兩端,插入或刪除元素,插入效率高。

缺點:當向中部,插入或刪除元素,插入效率低。

需要導入頭文件 #include

1_實例

#include <iostream>
#include <deque>
using namespace std;
int main()
{
	deque<int> ideq1;
	for (int i = 0; i<9; i++)
		ideq1.push_back(i);
	cout << ideq1.size() << endl;
	for (int i = 0; i<ideq1.size(); i++)
		cout << ideq1[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

4.4)列表(list)

list(列表):由 deque 實現而成,元素也存放在堆中。設計目的是令容器任何位置的添加和刪除操作都很快速,作爲代價不支持元素的隨機訪問——爲了訪問一個元素,只能遍歷整個容器。

優缺點:

優點:內存不連續,動態操作,可在任意位置插入或刪除且效率高。

缺點:不支持隨機訪問。

需要導入頭文件 #include

1_實例

#include <iostream>
#include <list>
using namespace std;
int main(int argc, char* argv[])
{
	list<char> ilist;
	for (char c = 'a'; c <= 'z'; ++c)
		ilist.push_back(c);
	cout << ilist.size() << endl;
	while (!ilist.empty())
	{
		cout << ilist.front() << " ";
		ilist.pop_front();
	}
	system("pause");
	return 0;
}

4.5)字符串(string)

string(字符串):表示可變長的字符序列,事實上和 vector 差不多。由於元素是連續存儲的,隨機訪問快,在末端插入和刪除快,但在中間插入和刪除慢。

優缺點:

優點:支持隨機訪問,即 [] 操作和 .at(),查詢效率高。

缺點:當向頭部或中部,插入或刪除元素,插入效率低。

需要導入頭文件 #include 。

1_定義和初始化:

在這裏插入圖片描述

2_簡單vector操作

在這裏插入圖片描述

3_處理字符

在這裏插入圖片描述

4_額外操作

在這裏插入圖片描述

5_實例

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string ivec1;
	for (int i = 0; i<9; i++)
		ivec1.push_back('i');
	cout << ivec1.size() << endl;
	for (int i = 0; i<ivec1.size(); i++)
		cout << ivec1[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

4.6)容器選擇

容器選擇原則:

除非有合適的理由選擇其他容器,否則應該使用 vector。
如果程序有很多小的元素,且空間的額外開銷很重要,則不要使用 list 或 forward_list。
如果程序要求隨機訪問容器元素,則應該使用 vector 或 deque。
如果程序需要在容器頭尾位置插入/刪除元素,但不會在中間位置操作,則應該使用 deque。
如果程序只有在讀取輸入時才需要在容器中間位置插入元素,之後需要隨機訪問元素。則:
先確定是否真的需要在容器中間位置插入元素。當處理輸入數據時,可以先向 vector 追加數據,再調用標準庫的 sort 函數重排元素,從而避免在中間位置添加元素。
如果必須在中間位置插入元素,可以在輸入階段使用 list。輸入完成後將 list 中的內容拷貝到 vector 中。
不確定應該使用哪種容器時,可以先只使用 vector 和 list 的公共操作:使用迭代器,不使用下標操作,避免隨機訪問。這樣在必要時選擇 vector 或 list 都很方便。

五、關聯容器(Associative containers)

5.1)常用操作(共同點)

1_類型別名

在這裏插入圖片描述

2_添加元素

在這裏插入圖片描述

3_刪除元素

在這裏插入圖片描述

4_訪問元素

在這裏插入圖片描述

5.2)集合(set)和多重集合(multiset)

set(集合):由紅黑樹實現,其中每個元素只包含一個關鍵字並依據其值自動排序,支持高效的關鍵字查詢操作,每個元素值只能出現一次,不允許重複。插入和刪除效率比用其他序列容器高,因爲對於關聯容器來說,不需要做內存拷貝和內存移動。

multiset(多重集合):唯一的區別是插入的元素可以相同。

優缺點:

優點:關鍵字查詢高效,且元素唯一,以及能自動排序。

缺點:每次插入值的時候,都需要調整紅黑樹,效率有一定影響。

需要導入頭文件 #include 。

1_實例

#include <iostream>
#include <set>
using namespace std;
int main(int argc, char* argv[])
{
	set<int> iset;
	iset.insert(3);
	iset.insert(1);
	iset.insert(2);
	iset.insert(1);
	set<int>::iterator it;
	cout << iset.size() << endl;
	for (it = iset.begin(); it != iset.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

5.3)映射(map)和多重映射(multimap)

map(映射):由紅黑樹實現,其中每個元素都是一些 鍵值對(key-value):關鍵字起索引作用,值表示與索引相關聯的數據。每個元素有一個鍵,是排序準則的基礎。每一個鍵只能出現一次,不允許重複。插入和刪除效率比用其他序列容器高,因爲對於關聯容器來說,不需要做內存拷貝和內存移動。

multimap(多重映射):唯一的區別是插入的元素(值)可以相同,即同一個鍵可以對應多個值。

優缺點:

優點:關鍵字查詢高效,且元素唯一,以及能自動排序。把一個值映射成另一個值,可以創建字典。

缺點:每次插入值的時候,都需要調整紅黑樹,效率有一定影響。

需要導入頭文件 #include

1_實例

#include <iostream>
#include <map>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
	map<int, string> imap;
	imap.insert({ 3, "張三" });
	imap.insert({ 4, "李四" });
	imap.insert({ 1, "王二麻子" });
	imap.insert({ 2, "小淘氣" });
	map<int, string>::iterator it;
	for (it = imap.begin(); it != imap.end(); it++)
	{
		printf("學號:%d 姓名:%s\n", (*it).first, (*it).second.c_str());
	}
	system("pause");
	return 0;
}

六、適配器(Adaptor)

6.1)常用操作(共同點)

在這裏插入圖片描述

6.2)棧(stack)

stack(棧):LIFO(後進先出)。

需要導入頭文件 #include 。

1_基本操作

在這裏插入圖片描述

2_實例

《C++ Primer》習題參考答案:第9章 - 順序容器 最後一題。

6.3)隊列(queue)和優先級隊列(priority_queue)

queue(隊列):FIFO(先進先出),即普通的緩衝區(buffer)。

priority_queue(優先級隊列):基於程序員提供的排序準則定義不同的優先權。

需要導入頭文件 #include 。

1_基本操作

在這裏插入圖片描述

七、無序容器(Unordered associative container)

7.1)unordered_map/unordered_multimap

7.2)unordered_set/unordered_multiset

無序容器:在關鍵字類型的元素沒有明顯的序關係的情況下,無序容器是非常有用的。在某些應用中,維護元素的序代價非常高昂, 此時無序容器也很有用。事實上使用無序容器通常更爲簡單(通常也會有更好的性能) 。如果關鍵字類型固有就是無序的,或者性能測試發現問題可以用哈希技術解決,就可以使用無序容器。

1_常用操作(共同點)

在這裏插入圖片描述
通常可以用一個無序容器替換對應的有序容器,反之亦然。但是輸出(通常)會不同。

八、sizeof、size和capacity

sizeof 操作符統計的是容器 vector 的大小;
size() 操作符統計的是容器(vector)內元素的大小;
capacity 操作符統計的是容器(vector)能存儲的容量大小。

#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
	vector<int> ivec;
	cout << sizeof(ivec) << endl;
	cout << ivec.size() << endl;
	cout << ivec.capacity() << endl;
	for (int i = 0; i<10; i++)
		ivec.push_back(1);
	cout << sizeof(ivec) << endl;
	cout << ivec.size() << endl;
	cout << ivec.capacity() << endl;
	system("pause");
	return 0;
}

另外:

reserve() 操作符統計的是指定容器內能存儲元素的數量;
resize() 操作符統計的是重新指定容器內有效元素的數量;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章