STL & std

Standard Template Library

什麼是標準模版庫?

Standard    Template   Lirary

  • 顧名思義,在 C++ 標準底下的一個程式庫
  • C++ 強大的原因之一
  • 幫你寫好很多強大的資料結構
  • 其實不只會講 STL,也會講一點其他 std 底下的東西

開始前,先講一下複雜度

namespace std

  • C++ 裡有東西叫 namespace,幫助識別函式/物件等
  • 如果有很多函式都叫同一個名字,我們需要 namespace 協助辨別誰是誰,就這樣
  • std 是 C++ 底下的一個 namespace
  • 程式開始時要 using namespace std; 目的就是省掉每次都要打出識別碼 std 的麻煩,但開發專案請不要這麼用

迭代器

Iterator

替代指標

  • 傳統的指標有很大的問題是會不小心戳到範圍以外的東西
  • 傳統指標過於基礎,操作困難
  • 可能會讓人碰到一些不能碰的資料
  • 如果結構不像陣列一樣是一直線就不能透過 +1 取得右邊的指標
  • 迭代器的用意就是讓人在進行各種操作時可以免掉很多困擾

功能

  • 在 STL 中,通常在迭代器前面使用 * 就能取得某個資料的參考,這樣就能修改資料了
  • 通常支援 ++ , -- 要看情況, +n -n 也要看情況

pair

數對

數對

  • 不要懷疑,就是數對
  • 包含兩個值, .first 和 .second
  • 啊... 我要這個幹嘛?
    • 太常用到,所以 C++ 索性幫你寫好了
    • 有些東西的回傳或函式參數也會用到

宣告

  • 使用前 #include <utility>
  • pair<T1, T2> pair_name = {a, b};
    • T1: first 的型態
    • T2: second 的型態
    • = {a, b} 初始化,可有可無

Vector

更強大的陣列

動態陣列

  • 在原先的 C 裡面,陣列的大小是不可變的,而且不會偵測 index 超過範圍的問題
  • Vector 解決了這個問題

宣告

  • 使用前 #include <vector>
  • vector<T> vec_name(size);
    • T: 元素型態 (int, long long, string...)
    • vec_name: 陣列名稱
    • size: 可加可不加,初始化陣列大小

使用

  • 和陣列幾乎一樣
  • 不能視作指標,它是一個物件,所以傳入函式通常會加參考

函式

  • .size(): 回傳 size_t (unsigned int),表示陣列大小
  • .resize(size, n): O(size), 重設陣列大小,還沒用到的部分初始化為 n
  • .push_back(n): 從後面推入一個元素 n
  • .pop_back(): 刪掉後面的一個元素 n
  • .begin(): 回傳陣列的頭的迭代器,支援 ++, --, +n, -n 等
  • .end(): 回傳陣列尾端 +1 的迭代器

小練習

  • vector<int> vec(5);
  • for(auto i = vec.begin(); i != vec.end(); i++)
    • auto 是讓編譯器自動幫你判斷型別
    • 在這裡是 std::vector<int>::iterator
    • 會讓一個迭代器 i 從 vec 的頭跑到尾
  • 請利用 i 將 vec 裡面通通設成 48763

雙參數函式

Binary Function

max, min

  • 我忘記在 algorithm 還是 functional 裡面了 反正都試試看 應該有一個是對的
  • 你覺得它是幹嘛用的 它就是幹嘛用的 by 註解
  • 回傳兩個值裡面比較大/小的那個
  • 記得兩個變數的型態要是一樣的,不然會報錯

greater, less, greater_equal...

  • 如果要比較的型別為 T,則用 greater<T>
  • 實際上相當於 > 或 <
  • 用函式包起來的版本
  • 回傳 a 是否 > / < b
  • 有時候要加 {},有時候不用,就很神奇

隊列

Queue / Stack / Deque

Queue

  • 排隊,人從後面進來,從前面出去
  • 不可以插隊

Queue

  • 使用前 #include<queue>
  • 宣告:queue<T> q;
    • T: 資料類型
  • .push(x): 從隊伍後面加入一個元素
  • .front(): 回傳隊伍最前面的元素
  • .pop(): 丟掉隊伍最前面的元素
  • .empty(): 是否為空
  • .size(): 隊列裡有幾個元素

Stack

  • 不是電腦記憶體那個 stack
  • 剛剛 queue 是先進先出,這是後進先出
  • 有一疊書,因為書很重所以你只能從最上面拿,要疊也只能從最上面

Stack

  • #include<stack>
  • stack<T> st;
    • T: 資料型態
  • .push(x): 推入一個資料 x
  • .pop(): 從頂端刪除一個資料
  • .top(): 取得頂端資料
  • .empty(): 是否為空
  • .size(): 裡面有幾個元素

ZJ c123

queue 的裸題 暫時想不到 待補

deque

  • 這東西發音和 deck 相同 不要念 de queue
  • 雙端的隊列,可以從前面來前面走後面來後面走
  • 支援隨機存取 []
  • .push_front(x), .push_back(x)
  • .pop_front(), pop_back()
  • 常數比較大,但 stack, queue 底層實際上好像也都是用 deque 去做的(
  • 但一般來講只用 queue 功能就用 queue,因為函式名比較簡單

Priority Queue

優先權佇列

圖源:807

事有輕重緩急

  • 平常做事,總是有優先順序吧
  • 在定義事情的優先權後,Priority Queue 就可以自動幫你排你現在要做什麼
  • 支援插入、刪除、取最優先的值

函式

  • 宣告 priority_queue<T> pq; 或 priority_queue<T, vector<T>, cmp> pq;
    • T: 資料類型
    • vector<T>: 底層容器,不用理它
    • cmp: 比較函式,預設是 less<T>,堆頂會是最大的值
  • .push(x): O(log n), 加入一個值
  • .top(): O(1), 取得目前優先權最大的值
  • .pop(): O(log n), 刪除優先權最大的值

Set/Map

很難寫的東西

Index 太大/不是整數

  • 給你 n 個數對 (a, b),接著 q 次對於每次給定 a,求 b 的值,保證 a 不重複
  • 你可以開一個陣列,存 arr[a] = b
  • a < 1e18 ?
  • 顯然會爆掉吧
  • 如果對於每個數列存 pair 到陣列,然後每筆詢問跑過整個陣列?
  • n, q < 1e6,爛掉

key 對值存取

  • map 提供 key 對 value 在                  時間內存取的方法
  • 原理等之後資料結構的課會講
O(log\ n)

使用

  • #include <map>

  • map<_Key, _Mapped> mp_name;

    • _Key 是 key(索引值) 的型態

    • _Mapped 是對到的值的型態 

  • .insert(pair<_Key, _Mapped> n):插入一對關係,如果 _Key 已經存在會回傳{存在的迭代器, false} 表示插入失敗,否則回傳{新元素的迭代器, true}

  • .erase(_Key k):刪除以 k 為 key 的鍵

  • .find(_Key k):找以 k,找不到回傳 .end()

  • .lower_bound(_Key k):回傳 >=k 的最小元素的迭代器

:覺得太難用嗎

:其實你可以用 [] 代替很多東西

:如果找不到 [] 裡的 key,會插入一個 key

set

只用於查找一個元素是否存在

基本上用法和 map 類似,但沒有 []

常用演算法

當然,大部分東西還是要自己寫

#include <algorithm>

sort

  • 不用想也知道,對數列排序是一個還滿常用的功能
  • std 的 algorithm 就寫好了,而且跑得飛快
  • 最佳 O(n), 平均 O(n log n), 最差                       (據說) 
  • sort(arr, arr+n, cmp);
    • arr 要排序的陣列指標頭 / 迭代器
    • n 要排的長度
    • cmp 比較函式,可有可無,預設是 std::less
O(n\ log^2 n)

lower_bound

  • 內建的二分搜(之後會講),用來搜尋已排序好陣列中 >= 目標的值,回傳一個迭代器
  • O(log n)
  • lower_bound(arr, arr+n, k, cmp)
    • 搜尋的頭(指標或迭代器)
    • k 目標
    • cmp 比較函式
  • upper_bound 是回傳嚴格大於的值
  • map 還有 set 都是用特別的.lower_bound,功能一樣但是因為資料結構不同所以必須用特別的函式

練習

int arr[5] = {1, 2, 3, 4, 5};
int target = 3;
int *result = lower_bound(arr, arr + 5, target);
cout << (result - arr);

練習

int arr[5] = {1, 2, 3, 4, 5};
int target = 3;
int *result = lower_bound(arr, arr + 5, target);
cout << (result - arr);

2

Unique

  • 對於一個已排序好的陣列,刪除相同的值
  • 返回新的 指標 / 迭代器
  • unique(arr, arr + n);
    • arr: 陣列頭
    • n: 原陣列大小
Made with Slides.com