CPP[2]

基礎語法

章魚

INDEX

複雜度

Pair

Vector

Iterator

Iterator

Iterator

Stack

Queue

Priority_queue

List

複雜度 complexity

1.簡介

2.範例

3.怎麼算

簡介

複雜度

  • 用來衡量解決一個問題所需的計算資源
  • 這些資源通常是時間和空間
  • 今天主要著重在時間部分

為什麼要衡量

美國進行人口普查,在沒有電腦前每10年進行一次,完成一次的統計需要7.5年 → 等你查完小孩子都長大了

簡介

怎麼衡量

直接用電腦跑一次?

太難了

每台電腦運算速度不同

運算速度也會受到當天狀況(溫度、濕度)影響

𝑂(𝑔(𝑛))

𝑂符號

範例

來看個飯粒

int a[n];
for(int i=0;i<n;i++) cin>>a[i];

將n筆資料輸入陣列a裡

當n越大

需要的空間越大

0 1 2 ... n-1

空間&時間複雜度:𝑂(𝑛)

程式執行的時間也越久

n

T

範例

再舉個例子,假設一個演算法的執行時間(或者所需步驟數)可以用

𝑇(𝑛)=2𝑛²+𝑛+10

表示,其中𝑛為輸入的大小

範例

𝑇(𝑛)=2𝑛²+𝑛+10

當𝑛夠大的時候,除了
2𝑛²
以外的其他項的影響可以忽略

n=10,T(10)=200+10+10=220

n=100,T(100)=20000+100+10=20110

n=1000,T(1000)=2000000+1000+10=2001010

參考自資訊之芽簡報

這時候我們就說這段程式的複雜度是𝑂(𝑛²)

範例

再舉個例子

複雜度:O(1) a.k.a 常數

複雜度:O(0.004)

怎麼算

乘法比加法慢,那複雜度是否相同?

答案是都一樣

兩者的計算時間都是常數

都是𝑂(1)

怎麼算

宣告跟輸入都要時間,這些時間要算進去嗎?

宣告跟輸入所花的時間遠小於計算

可以忽略

怎麼算

常見題目限制

時間限制: 1000 ms

記憶體量: 65536 MB

電腦每秒約10⁹次運算

複雜度 x 常數<10⁹

怎麼算

可以根據複雜度判斷你所用的演算法適不適合

input限制

O(1)

𝑂(1)    ->    

𝑂(log(𝑛))    ->   10²⁰      

𝑂(𝑛)    ->   10

𝑂(𝑛log(𝑛))    ->   10⁷          

𝑂(𝑛²)   ->   10⁴

𝑂(2ⁿ)   ->   28

𝑂(𝑛!)   ->   12

複雜度限制

實際情況因常數而定

STL

Standard Template Library

標準樣板函式庫

在C++中可以直接使用的

  • 資料結構
  • 演算法
  • 迭代器

Standard Template Library

標準樣板函式庫

How to use

cplusplus.com查STL

聲明

等等所有的STL都適用萬用標頭檔

#include<bits/stdc++.h>

也都需要

using namespace std;

但為了方便閱讀

等等的簡報裡會省略

pair

存兩個變數的方法

pair

first second

pair

first(string) second(bool)

怎麼宣告

#include<utility>

pair<string,bool> p;

pair

first(小黑) second(false)

賦值

其一、
pair<string,bool> p("小黑",false);
其二、
p=make_pair("小黑",false);
其三、
p.first="小黑";
p.second=false;

vector

Vector

簡單來講

就是可以改變長度的array

#include<vector>

array

int arr[5]={};//{0,0,0,0,0}

vector

宣告

vector<int> v(5,0);//{0,0,0,0,0}
int arr[5]
vector<int> v(5);
404 not found
vector<int> v;

array

vector

花式宣告

int arr[5]={1,1,1,1,1}
vector<int> v(5,1);//{1,1,1,1,1}
int arr[5][3];
vector<vector<int>> v(5,vector<int>(3));

取值

與array相同

vector<int> v={1,4,2,8,5}
cout<<v[1];//4

.size()

vector<int> v(5,1);
cout<<v.size();//5

取得vector大小

.empty()

vector<int> v;
cout<<v.empty();//1

檢查vector是否為空

.push_back()

vector<int> v={1,4,2};
v.push_back(8);
v.push_back(5);
//v={1,4,2,8,5}

插入數字到vector後方

容量不足!!

1 4 2

8

自動開兩倍大

&

自動複製過去

1

4

2

8

複雜度:𝑂(2𝑛)

複雜度:𝑂(𝑛)

.capacity()

vector<int> v={1,4,2};
v.push_back(8);
cout<<v.size()<<' '<<v.capacity();//4 6

取得容量空間大小

實際使用空間

分配到的空間

.pop_back()

vector<int> v={1,4,2,8,5};
v.pop_back();
v.pop_back();
//v={1,4,2}

將vector最後方的數字移除

不要做過頭!

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int> v(5,0);
    for(int i=0;i<6;i++){
        v.pop_back();
        cout<<v.size()<<' ';
    }
}

2⁶⁴-1

不要做過頭!

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int> v(5,0);
    for(int i=0;i<6;i++){
        if(!v.empty()){
            v.pop_back();
        }
        cout<<v.size()<<' ';
    }
}

水啦

v1.swap(v2)

vector<int> v={1,4,2,8,5};
vector<int>().swap(v);//跟空的vector交換=>重置
cout<<v.empty();//1

高效交換兩個vector的內容

iterator

we call it 迭代

迭代器

已經寫好的

讓你便利遍歷

你會的遍歷

vector<int> v={1,4,2,8,5};
for(int i=0;i<v.size();i++){
	cout<<v[i];
}
//14285

for迴圈&while迴圈

用iterator的遍歷

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int> v(5);

    for(vector<int>::iterator it=v.begin();it!=v.end();it++){
        cin>>*it;
    }
    for(vector<int>::reverse_iterator it=v.rbegin();it!=v.rend();it++){
        cout<<*it<<" ";
    }
}
vector<int> v(5);
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
  cin>>*it;
}

v(5)

v.begin()

v.end()

1

5

2

4

8

for(vector<int>::reverse_iterator it=v.rbegin();it!=v.rend();it++){
        cout<<*it<<" ";
}

v(5)

v.rend()

1

5

2

4

8

5

8

2

4

1

output:

扣好長喔,怎麼辦

便利的遍歷

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int> v(5);

    for(auto it=v.begin();it!=v.end();it++){
        cin>>*it;
    }
    for(auto it=v.rbegin();it!=v.rend();it++){
        cout<<*it<<" ";
    }
}

讓auto幫你決定變數型態

變例的便利的遍歷

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int> v(5);

    for(int &i:v){
        cin>>i;
    }
    for(auto i:v){
        cout<<i<<" ";
    }
}

這超好用

stack

stack

新資料放最上層

拿也先從最上層

Last-In First-Out

LIFO

宣告

#include<stack>

stack<int> stk;
stk.push(1);
stk.push(4);
stk.push(1);

.push()
#不是push_back()

stk

1

1

4

.top()

取得最上方元素

stk.top()++;
cout<<stk.top();//2

stk

1

1

4

2

取得最上方元素

stack是空的時,會Segment Default

=>先判斷是否.empty()

.size()

取得大小

cout<<stk.size();//3

stk

1

2

4

queue

queue

佇(ㄓㄨˋ)列

First-In First-Out

FIFO

宣告

#include<queue>

queue<int> q;
q.push(1);
q.push(4);
q.push(2);

.push()

q

1

2

4

.front() & .back()

q.front()--;
q.back()=5;

q

1

2

4

5

0

.size() & .pop() & .empty()

while(q.size()>1){
	cout<<q.front();
    q.pop();
}
cout<<q.empty();

q

4

0

5

output:

0

4

0

priority_queue

priority_queue

優先權佇列

根據比較規則排列,最大者先出

預設數值大的在前

宣告

#include<queue>

priority_queue<int> pq;
//使用預設的由大到小

宣告

由小到大

priority_queue<int,vector<int>,greater<int>> pq;

宣告

自訂比較函式

#include<bits/stdc++.h>
using namespace std;
struct comp{
    bool operator()(int a,int b){
        return a<b;//小到大
    }
};//<-加分號!!
int main(){
    priority_queue<int,vector<int>,comp> pq;
}

.push() &

.top() &

.pop() &

.size() &

.empty()

你懂得

直接看扣

priority_queue<int> pq;//由大到小
pq.push(1);
pq.push(4);
pq.push(2);
while(!pq.empty()){
	cout<<pq.top()<<' ';
    pq.pop();
}

output:

1

2

4

pq

型態是heap(堆)但做動畫好麻煩

priority_queue<int> pq;//由大到小
pq.push(1);
pq.push(4);
pq.push(2);
while(!pq.empty()){
	cout<<pq.top()<<' ';
    pq.pop();
}

output:

1

2

4

pq

4

2

priority_queue<int> pq;//由大到小
pq.push(1);
pq.push(4);
pq.push(2);
while(!pq.empty()){
	cout<<pq.top()<<' ';
    pq.pop();
}
return 0;

output:

1

pq

4

2

1

補充

 

另一種自訂比較函式的方法

講師也不會

list

list

雙向鏈結串列

每個元素裡有:

  • 一個數值
  • 前一個元素的指標
  • 後一個元素的指標

宣告

#include<list>

list<int> l={2,1}

0x1428

0x1429

2

1

nullptr

nullptr

0x1429

0x1428

宣告

基本上跟vector一樣

list<int> l(3,1);
list<int> l(3);
list<int> l;

.front() & .back()

取得最前方和最後方的元素

list<int> l={1,4,2,8};
cout<<l.front()+l.back();
//1+8=9

1

8

2

4

front

back

.push_front() & .push_back()

新增元素在最前方和最後方

l.push_front(0);
l.push_back(5);

front

back

1

8

2

4

front

0

back

5

迭代器

它也可以用迭代器ㄛ

#include<bits/stdc++.h>
using namespace std;
int main(){
    list<int> l(5);

    for(list<int>::iterator it=l.begin();it!=l.end();it++){
        cin>>*it;
    }
    for(list<int>::reverse_iterator it=l.rbegin();it!=l.rend();it++){
        cout<<*it<<" ";
    }
}

迭代器

便利遍歷和變例便利遍歷也可以

#include<bits/stdc++.h>
using namespace std;
int main(){
    list<int> l(5);

    for(auto it=l.begin();it!=l.end();it++){
        cin>>*it;
    }
    for(auto it=l.rbegin();it!=l.rend();it++){
        cout<<*it<<" ";
    }
}

迭代器

便利遍歷和變例便利遍歷也可以

#include<bits/stdc++.h>
using namespace std;
int main(){
    list<int> l(5);

    for(auto &i:l){
        cin>>i;
    }
    for(auto i:l){
        cout<<i<<" ";
    }
}

它也可以

  • .insert()
  • .erase()
  • .empty()
  • .size()

But!

list不能用索引取值

list<int> l={1,4,2,8,5}
cout<<l[2];

來烤肉!!

點我

來烤肉!!

c++語法小社

By oct0920

c++語法小社

  • 297