###### tags: `MDCPP講義` # 基礎資料結構 **Author : 謝侑哲** --- ## 甚麼是資料結構(Data structure)? ---- 簡單來說 資料結構,就是一種儲存資料的方法 像是我們最常用的的陣列就算一種 除此之外還有很多種類,各有不同的特性 ---- 今天要講幾個最基本的: - 堆疊(stack) - 動態陣列(vector) - 隊列(deque) - 佇列(queue) --- ## 在開始之前 ---- 我們要先來介紹STL ---- STL $\rightarrow$ Standard Template Library Standard = 標準 , Template = 模板 , Library = 庫 就是C++內建的模板庫,可以讓你直接呼叫(競賽中也行) ---- STL 主要分成 **<span style="color: red">容器</span>**、演算法、迭代器、函式 而今天所教的資料結構都包含在STL容器中 這意味著你們不用手刻出整個資料結構了 ---- 不過不是所有資料結構都可以直接抓STL容器的 所以你們以後還是會碰到自己手刻的部分 --- ## 堆疊(stack) ---- 推疊就像你們帶了一堆書回家,卻一本都沒有讀 等打電動打到半夜,突然想到明天的考試 拿書時,一定得先從最上面的開始拿 ![](https://i.imgur.com/orKAgCt.png =300x) ---- 所以它會是一個先進後出的結構 FILO, first in last out 這樣講很難懂吧,讓我們直接上圖 ---- ![](https://i.imgur.com/afkczja.png =800x ) ---- 一般常見的用法有: push( DT value ) 把東西從最上面塞進去 pop(DT value) 把東西從最上面拿出來 ---- empty() 判斷stack是否為空 空則 return true 有東西則 return false ---- size() 判斷stack的大小 以這張圖來說,pop完的size就會是6 ![](https://i.imgur.com/GNNL2NH.png) ---- top() 得到stack疊頂端的值 以這張圖來說,return的值就會是4 ![](https://i.imgur.com/719VBmb.png =250x) ---- 看起來是個酷東西,那原理呢? 我有一個同學常跟我說,要學東西就從最底層學 可以學到最多 然後他現在一直想拖我去建一個CNN神經網路 看著現成的東西卻不能用,我感到非常難過 反正重點是再來教原理 ---- 想像有一個指針,它叫做top(這裡暫時用int存它) 而他一直指著stack的頂端 像這樣 ![](https://i.imgur.com/32Fa4n6.png =300x) ---- 現在進行push的動作 - 首先將top+1(往上移) ![](https://i.imgur.com/SNaDpuJ.png =300x) ---- - 接者讓top指到的位置變成要插入的值 - 也就是 stack[top] = value ![](https://i.imgur.com/uTbiEZw.png =300x) 這樣就完成push操作了 ---- 接下來是pop的動作 - 只要簡單的把top -1(下移) 就行了 ![](https://i.imgur.com/ZUThdHQ.png =300x) ---- 你們可能會疑惑,不用把原本的位置清掉之類的嗎 事實上,我們只會對0~top之間的數做操作 因此刪不刪不會有影響 push時,也會直接把舊數字覆蓋掉 ---- 那size()就很簡單了 只要看你的top在哪裡就可以了 這張圖的top在 2的位置,所以回傳值會是3 ![](https://i.imgur.com/ZUThdHQ.png =300x) ---- empty()也是一樣的概念 只要top<0,就是空stack return Ture ---- 以上的原理,以陣列實作如下: ```cpp= int top = 0; int st[1000]; top++; st[top] = a; // push(a)操作 top --;// pop()操作 cout << st[top] << "\n"; // top()操作 if(top < 0); // empty() int size = top; // size() ``` ---- 我們在更之前有提到STL容器 那要怎麼使用他呢? ---- 這樣就行了 ```cpp= #include <stack> signed main() { stack<int> st; st.push(123); //加入 st.pop(); //丟出 if(st.empty()); //判斷是否為空 st.top(); //獲得最上面的值 } ``` ---- 練習題: [Parentheses Balance](http://mdcpp.mingdao.edu.tw/problem/B005) --- ## 動態陣列(vector) ---- 還記得前面教的stack嗎,現在把她忘掉吧 因為這東西比他好用啊 ---- 他之所以叫做動態陣列 就是因為它是可以自動隨長度擴展的陣列 ---- 講白了,他就是個高階版本的陣列 跟stack不一樣的是它可以用vec[i]的方式來存取 而且時間複雜度幾乎是O(1) 也可以開多維來使用 ---- 除此之外,因為他是動態的關係,stack做得到的,他也都做得到 速度甚至比它還快 所我也不是很確定stack的存在意義 ---- 在vector中,主要有兩個重要的特性 size : 元素長度 capacity : 容器大小 ---- 用圖來講就是這樣 ![](https://i.imgur.com/Pv7wzqI.png) ---- 在一開始vector會開一個預設的capacity 當size超過capacity時,它就會開更長的記憶體 然後整個轉移它 ---- 這個做法感覺是個O(n)的作法 而且可能會有多次進行,導致常數很大 但如果只是打打題目,不看效率的話可以忽略它沒差 ---- 不過也有方法處理這個問題 ---- ### 方法一 在已知會用到的空間下,可以先把它的容量開大一點 ```cpp= vector<int> vec(1000000); ``` ---- ### 方法二 在不確定空間下 可以使用reserve(預留)函式來一次擴很大 ```cpp= vector<int> vec; vec.reserve(100); ``` ---- 因為它的原理滿複雜的 大概長這樣 ![](https://i.imgur.com/m0iiHYE.png) 我就不說了 現在就會用就好 ---- 再來講講它的常用功能 ---- 宣告 ```cpp= #include<vector> vector<int> vc; //宣告一個vector vc vector<int> vc(100); //宣告一個大小為100的vector vector<int> vc(100, 9); //宣告一個大小為100,每個值都是9的vector vector<int> vc = {4, 8, 7, 6, 3}; //跟陣列一樣用法 vector< vector<int> > vec; //vector二維陣列 vector<int> vc[1000] ; // 第二維大小100的二維陣列 ``` ---- - empty() - 判斷是否為空 - size() - 取得vector大小 - front() - 取得第一個元素(=vc[0]) - back() - 取得最後面的元素(=vc[vc.size()-1]) - reserve( value ) - 重新弄vector的空間 ```cpp= vector<int> vec; vec.empty(); vec.size(); vec.front(); vec.back(); vec.reserve(10000) ``` ---- - push_back - 在最上面插入值 - pop_back - 把最上面的值丟掉 - clear() - 淨空整個vector ```cpp= vec.push_back(); vec.pop_back(); vec.claer(); ``` ---- 這些功能就可以取代stack了呢 還比它更好用 ---- vector的功用除了代替stack還可以幹嘛呢? ---- - 代替stack - 代替陣列 - 存圖 - 離線查詢 - 模擬 ...... 總之很多,看不懂也沒關係 會慢慢學到的 --- ## 雙向隊列(deque) ---- deque是個跟stack很像的東西 不過它除了可以從上面放上面拿之外 他也可以從下面放下面拿 ---- 就像這樣,不破壞隊伍的情況,可以從隊伍的尾端或是隊伍的前端進出 ![](https://i.imgur.com/EceM1oI.png) ---- 也就是說它會是一個兩邊都可以進出的結構 事實上,它也是一種動態陣列 所以我們可以把它當成是雙向的vector ---- 那他有甚麼用法呢? ---- 宣告基本上跟vector一樣 不過它有自己的標頭 ```cpp= #include<deque> ``` ---- 最基本的函式: empty() - 判斷是否為空。 size() - 取得deque大小。 front() - 取得最前面的元素。 back() - 取得最後面的元素。 deque[value] - 取那個位置的值 ( 不建議使用 ```cpp= deque<int> deq; deq.empty(); deq.size(); deq.front(); deq.back(); cout<<deq[13]; //( 不建議使用 ``` ---- 再來是雙向進出的部分 push_back(DT value) - 放入一個元素至最後面(數字大)。 push_front(DT value) - 放入一個元素至最前面(0的位置)。 pop_back() - 把最後面的元素移除(數字大)。 pop_front() - 把最前面的元素移除(0的位置)。 ```cpp= deq.push_back(123); deq.pop_back(); deq.push_front(123); deq.pop_front(); deq.clear(); ``` ---- 那它的原理呢? ---- 有請在vector出現過的圖 ![](https://i.imgur.com/hU8Zfqa.png) ---- 會發現它就是比stack多了在底部的指針 指針的運作原理都一樣,就可以知道它怎麼操作了 ---- 寫成陣列的實作長這樣: ```cpp= int head = 100, tail = 100; int deq[1000]; tail++; deq[tail] = a; // push_back(a) tail --; // pop_back()操作 head--; st[head] = a; // push_front(a)操作 head ++;// pop_front()操作 cout << q[head] << "\n"; // front()操作 cout << q[tail] << "\n"; // back()操作 if(tail <= head); // empty() int size = tail - head; // size() ``` ---- 練習題: <!--http://mdcpp.mingdao.edu.tw/problem/T063--> --- ## 佇列(queue) ---- 基本上queue就是殘廢版的deque 像是滴定管一樣 從哪裡進去就從另一邊出來 ![](https://i.imgur.com/OVLHuxu.png) ---- 是不是聽不懂這個比喻 我很抱歉,聯想力不足 ---- 所以給你們看這張圖 ![](https://i.imgur.com/Hcsvsbm.png =800x) ---- 也就是說,它是個先進後出的結構 (FIFO, first in first out) ---- 再來講一下用法 ---- 宣告: ```cpp= #include<queue> queue<int> que; ``` 基本上跟前面一樣 ---- 基本函式: empty() - 判斷是否為空。 size() - 取得queue大小。 front() - 取得最前面的元素。 back() - 取得最後面的元素。 還是熟悉的樣子 ```cpp= queue<int> que; que.empty(); que.size(); que.front(); que.back(); ``` ---- 進出的函式: - push(DT value) - 放入一個元素至最後面。 - pop() - 把最前面的元素移除。 - clear() - 清掉它 ```cpp= queue<int> que; que.push(123); que.pop(); que.clear(); ``` ---- 至於它的原理 還記得我說它是殘廢的deque嗎? ---- 基本上他就是deque把某些功能封掉 原理基本上是一樣的 ---- 唯一的差別大概是因為它跟deque一樣的原理 所以這個queue在push,pop時 指針都是往同一個方向的 所以看起來會造成空間的浪費 ---- 因此C++的STL容器用了環狀queue的概念 也就是當指針指到某數字,就把它丟回初始位置 變成環形的,減少浪費 ---- 練習題: [智乃還債計畫](http://mdcpp.mingdao.edu.tw/problem/T006) (sliding window) [小組隊列](http://mdcpp.mingdao.edu.tw/problem/B002)
{"metaMigratedAt":"2023-06-16T12:36:12.071Z","metaMigratedFrom":"Content","title":"基礎資料結構","breaks":true,"description":"Author : 謝侑哲","contributors":"[{\"id\":\"f547d745-63f3-4bad-986b-1751eeec19d1\",\"add\":6599,\"del\":261}]"}
    1503 views
   owned this note