STL

STL?

標準樣板函式庫

Standard Template Library

裡面有很多容器、函式

很多好用的工具

演算法的基礎

請務必學會並弄熟

INDEX

  • vector
  • pair
  • stack
  • queue
  • deque
  • set/multiset
  • map
  • priority_queue
  • bitset

Vector

Vector

功能更多的陣列

動態伸縮長度

寫得很好的文

常用功能:

push_back

pop_back

clear

size

empty

宣告

#include <bits/stdc++.h>
using namespace std;
int main() {
    vector<int>v;
    //宣告一個陣列
    vector<char>a(10);
    //大小為10
    vector<int>b={1,3,5};
    //直接指定大小&賦值
    vector<int>(12,0);
    //大小為4且都放0
}

vector<資料型態> 名稱;

加入/移除元素

#include <bits/stdc++.h>
using namespace std;
int main() {
    vector<int>v;
	v.emplace_back(1);
    //加入元素 較快
    v.push_back(2);
    //加入元素 較慢
    v.pop_back();
    //移除尾端元素
    
}

push_back(值);

emplace_back(值);

pop_back();

 

取值

#include <bits/stdc++.h>
using namespace std;
int main() {
    vector<int>v;
	v.emplace_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    cout<<v[2];
}

基本上陣列可以做到的

vector也都可以

取頭尾

#include <bits/stdc++.h>
using namespace std;
int main() {
    vector<int>v;
	v.emplace_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    cout<<v.front()<<v.back()<<v[2];
}

front():第一項

back():最末項

基本上陣列可以做到的

vector也都可以

大小

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> v;
    cout<<"初始:size="<<v.size()<<" capacity="<<v.capacity()<<'\n';
    for (int i = 1; i <= 10; i++) {
        v.push_back(i);
        cout<<"插入"<<i<<"->size="<<v.size()<<" capacity="<<v.capacity()<<'\n';
    }
}

size():取得vector的長度

capacity():陣列加上預留的格子

empty():判斷是否為空 回傳布林值

Vector

size():vector長度

capacity():預留的記憶體空間(通常用倍增)

迭代器 iterator

vector是一排的盒子

iterater像指針 指向陣列的位置(不是值)

 

宣告方法:

vector<int>::iterator it;

變數it 是指向vector<int>的迭代器型別

begin():vector第一格的位置

end():vector最後一格"再下一格"的位置

移動迭代器:

it++:移到下一格

it--:移到上一格

遍歷

1.用for

跑過整個陣列

vector<int>v={1,2,3,4,5};
for(int i=0;i<5;i++){
cout<<v[i]<<' ';
}

跟陣列一樣

遍歷

2.用剛剛學過的迭代器

vector<int>v={1,2,3,4,5};
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
	cout<<*it<<' ';//'*'用來取值
}

這樣也太長了吧

在C++11後 提供auto可以自動判斷型別

vector<int>v={1,2,3,4,5};
for(auto it=v.begin();it!=v.end();it++){
	cout<<*it<<' ';
}

遍歷

3.range-based for

vector<int>v={1,2,3,4,5};
for(int i:v){
	cout<<i<<' ';
}

複製每個元素到i裡面

但是改動i不會影響v裡面的東西

vector<int>v={1,2,3,4,5};
for(int i:v){
	cout<<i<<' ';
    i+=10;
}
cout<<'\n';
for(int x:v){
	cout<<x<<' ';
}

參考(reference)

變數的別名

所以改動的時候也會改動原變數

在變數前面加上'&'

vector<int>v={1,2,3,4,5};
for(int &i:v){
	cout<<i<<' ';
    i+=10;//用了參照 v裡面的值也會改
}
cout<<'\n';
for(int x:v){
	cout<<x<<' ';
}

一些特殊操作

常用:

sort():由小到大排

reverse():反轉

swap():兩元素調換

fill():填滿陣列格子

resize():重新定義大小

vector<int>v={1,2,3,4,5};
sort(v.begin(),v.end());
reverse(v.begin(),v.end());
swap(v[1],v[3]);
fill(v.begin(),v.end(),0);
v.resize(10);

自己操作看看

未來的圖論會用到

也可以直接當成陣列使用

是個極為方便的工具

o076

#include<bits/stdc++.h>
using namespace std;
vector<int>v;
int main(){
    int n;cin>>n;
    for(int i=0;i<n;i++){
        int a;cin>>a;
        v.push_back(a);
    }
    int x=v[0],tmp=1,ans=1;
    for(int i=1;i<n;i++){
        if(v[i]<v[i-1])tmp++;
        else tmp=1;
        ans=max(ans,tmp);
    }
    cout<<ans<<'\n';
}

toj 575

#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
vector<int>g[maxn];
int main(){
    int n,k;cin>>n>>k;
    for(int i=0;i<k;i++){
        int a,b;cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for(int i=1;i<=n;i++){
        sort(g[i].begin(),g[i].end());
        for(auto x:g[i]){
            cout<<x<<' ';
        }
        cout<<'\n';
    }
}

Pair

Pair

把兩組資料綁在一起

看成一種新的型態

pair<int,int>p

宣告方式跟vector很像

pair<資料型態,資料型態>名稱;

取值

first:第一項

second:第二項

#include<bits/stdc++.h>
using namespace std;
int main(){
    pair<int,int>p;
    p.first=3;//將第一項設成3
    p.second=5;
    cout<<p.first<<' '<<p.second<<'\n';
}

宣告方式跟vector很像

pair<資料型態,資料型態>名稱;

不用加()

vector+pair

pair裡面可以裝整數

當然也可以裝任何資料型態

#include<bits/stdc++.h>
using namespace std;
int main(){
    pair<int,int>p;
	pair<int,pair<int,int>>p1;
    pair<int,char>p2;
    pair<vector<int>,pair<int,int>>p3;
}

vector+pair

vector也不例外

#include<bits/stdc++.h>
using namespace std;
int main(){
	vector<int>v;
    vector<pair<int,int>>v1;
    vector<vector<int>>v2;
}

注意事項

在傳入和輸出的時候有些要注意

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<pair<int,int>>v;
    v.push_back(make_pair(3,5));
    v.push_back({3,5});
    v.emplace_back(3,5);
}

vector要放pair要加make_pair(a,b)或{a,b}

emplace_back(a,b)直接放

pair不能直接輸出 要分開用first second取

大小比較

在用sort()函式比較pair的大小時

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<pair<int,int>>v;
    v.push_back(make_pair(3,5));
    v.push_back({5,5});
    v.emplace_back(5,7);
    sort(v.begin(),v.end());
}

會先比較first 再比較second

練習

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;cin>>n;
    vector<pair<int,int>>v;
    for(int i=0;i<n;i++){
        int x,y;
        cin>>x>>y;
        v.push_back({x,y});
    }
    sort(v.begin(),v.end());
    for(auto i:v)cout<<i.first<<' '<<i.second<<'\n';
}

a130

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;cin>>t;
    int cnt=1;
    while(t--){
        int mx=0;
        vector<pair<string,int>>v;
        for(int i=0;i<10;i++){
            string c;
            int a;
            cin>>c>>a;
            v.push_back({c,a});
            mx=max(mx,a);
        }
        cout<<"Case #"<<cnt<<":\n";
        for(int i=0;i<10;i++){
            if(mx==v[i].second)cout<<v[i].first<<'\n';
        }
        cnt++;
    }
}

Stack

Stack

後進先出(LIFO)

有點像堆疊 書堆的概念

或是一個乒乓球桶的感覺

常用功能:

push()

pop()

top()

宣告

stack<資料型態>名稱;

stack<int>st;
stack<char>stk;

無法預設大小

可以用size()來看有多長

empty()看是否為空

推入移除元素

push():從尾端推入元素

pop():從尾端移除元素

在pop的時候

如果不確定大小時

記得要檢查是否為空

否則會出錯(RE)

stack<int>st;
st.push(1);
st.push(2);
st.push(3);
st.pop();
if(!st.empty())st.pop();

取值

無法用[]取值

top():

拿最上面的那個

只能拿最上面那一個

若stack為空也會RE

stack<int>st;
st.push(1);
st.push(2);
st.push(3);
st.pop();
cout<<st.top()<<' ';

題目

i213

f345

e155

b838

c123

進階:

c907(單調隊列)

i213

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;cin>>n;
    stack<int>st;
    for(int i=0;i<n;i++){
        int a;cin>>a;
        if(a==1){
            int b;cin>>b;
            st.push(b);
        }
        else if(a==2){
            if(st.empty())cout<<-1;
            else cout<<st.top();
            cout<<'\n';
        }
        else{
            if(!st.empty())st.pop();
        }

    }
}

f345

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;cin>>n;
    stack<int>st;
    for(int i=0;i<n;i++){
        int a;cin>>a;
        st.push(a);
    }
    while(!st.empty()){
        cout<<st.top()<<' ';
        st.pop();
    }
}

Queue

Queue

先進先出(FIFO)

排隊的概念

跟stack有相反的感覺

常用功能:

push()

pop()

front()

宣告

queue<資料型態>名稱;

queue<int>q;
queue<char>qq;

和stack基本一樣

無法預設大小

size()看有多長

empty()檢查是否為空

推入移除元素

push():從尾端推入元素

pop():從最前端移除元素

queue<int>q;
q.push(1);
q.push(2);
q.push(3);
q.pop();
if(!q.empty())q.pop();

pop的時候一樣要注意是否為空

取值

front():取最前面元素

queue<int>q;
q.push(1);
q.push(2);
q.push(3);
q.pop();
cout<<q.front<<' ';
if(!q.empty())q.pop();

pop的時候一樣要注意是否為空

題目

e447

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;cin>>n;
    queue<int>q;
    for(int i=0;i<n;i++){
        int a;cin>>a;
        if(a==1){
            int b;cin>>b;
            q.push(b);
        }
        else if(a==2){
            if(q.empty())cout<<-1;
            else cout<<q.front();
            cout<<'\n';
        }
        else{
            if(!q.empty())q.pop();
        }

    }
}

Deque

Deque

怎麼唸?

dq?

deck

雙端佇列

(double-ended queue)

stack+queue

宣告

方法跟前面兩個基本差不多

直接放code

#include<bits/stdc++.h>
using namespace std;
int main(){
    deque<int>dq;
    deque<char>dq1;
}

推入移除元素

雙向=可從頭尾加入移除元素

push_back(),pop_back():從後端增減元素

push_front(),pop_front:從前端加減元素

#include<bits/stdc++.h>
using namespace std;
int main(){
    deque<int>dq;
    dq.push_front(1);
    dq.push_back(2);
    dq.push_back(3);
    dq.pop_back();
    dq.pop_front();
}

取值

front():最前端元素

back():最後端元素

#include<bits/stdc++.h>
using namespace std;
int main(){
    deque<int>dq;
    dq.push_front(1);
    dq.push_back(2);
    dq.push_back(3);
	cout<<dq.front()<<dq.back();
}

大小

empty():判斷是否為空

size():回傳大小

#include<bits/stdc++.h>
using namespace std;
int main(){
    deque<int>dq;
    dq.push_front(1);
    dq.push_back(2);
    dq.push_back(3);
    if(dq.empty())cout<<"empty!\n";
    cout<<dq.size();
}

缺點

各個操作都比vector慢3倍

沒事不要用到它

沒事不要用到它

沒事不要用到它

 

 

開stack跟queue的時候其實都是在開一個deque

所以速度上stack=queue=deque

題目

如果要練習的話

就用deque把前面的題目寫一次

Set/Multiset

Set

集合

裡面元素都是唯一(不重複)

且都排序好的

實作方式 紅黑樹

很難很複雜(我不會)

宣告

set<int>s;
set<char>st;

就像一個黑盒子

1.可以放東西進去

2.拿東西出來

3.詢問一個東西是否在裡面

放入/移除東西

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
st.erase(1);

insert():放入東西

erase():移除東西

把1移除

裡面剩下2 3

查詢

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
if(st.find(2)!=st.end())cout<<"Yes\n";
if(st.count(2))cout<<"Yes\n";
if(st.empty())cout<<"empty";

find():如果找到 回傳記憶體位置

(如果沒找到 回傳end())

count():裡面有幾個

empty():是否為空

查詢

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
auto it=st.find(2);
//auto=set<int>::iterater
if(it!=st.end())cout<<*it;


find()回傳的是記憶體位置

輸出位置的值的話加個*

不知道各位有沒有忘記它

迭代器

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
auto it=st.begin(2);
//auto=set<int>::iterater
cout<<*it<<'\n';
cout<<*next(it)<<'\n';
cout<<*prev(it)<<'\n';

begin(),end()

prev():此記憶體位置的前一項

next():此記憶體位置的前一項

可以不用修改it的值

用法

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
auto it=st.begin();
//auto=set<int>::iterater
cout<<*next(it)<<'\n';
cout<<*(++it)<<'\n';


如何用set輸出第2小的數字?

set裡面的元素是有序的

第二大=第二個元素

可以思考一下兩種輸出有什麼差別

遍歷

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
for(int i:st)cout<<i<<'\n';
for(auto iter=st.begin();iter!=s.end();iter++){
    cout<<*iter<<'\n';
}

跟vector差不多

一般用上面那種

搜尋

set<int>st;
st.insert(1);
st.insert(2);
st.insert(3);
cout<<*st.upper_bound(2);

set有upper_bound()函式

.upper_bound(k)尋找第一個大於k的位置

注意 是位置 值的話*

(為甚麼說"也有" 因為我還沒講)

不滿足?

set一種元素只能存一個

不夠 要更多?

STL有個東西叫做Multiset

Multiset

可以同時存在多個相同元素

與set語法大致相同

1.count():可回傳元素數量

2.erase(n):刪除所有為n的元素

只刪除一個:s.erase(s.find(10))

multiset<int>st;
st.insert(1);
st.insert(1);
st.insert(2);
st.insert(3);
cout<<st.count(1)<<'\n';

太多了?

只要知道元素是否在裡面

不用排序?

STL 還有個東西是unordered_set !!!

unordered_set

功能如其名

底層用hash實作

失去upper_bound功能

整體比set快

有時候可以拿來優化

(還有unordered_multiset)

總結

目前用法最特殊的

功能也一堆

不過多寫點題目

每次反覆使用

就可以輕鬆拿捏

Set挺重要的

題目

b130

想法:

輸出大小

按照順序輸出

相同的去掉

用set

#include<bits/stdc++.h>
using namespace std;
int main(){
    set<int>st;
    int n,a;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a;
        st.insert(a);
    }
    cout<<st.size()<<'\n';
    for(auto x:st){
        cout<<x<<' ';
    }

}

cses distinct number

想法:

不同值的數量

有點像...

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    set<int>s;
    for(int i=0;i<n;i++){
        int a;
        cin>>a;
        s.insert(a);
    }
    cout<<s.size();
}

沒錯 set的大小

d123

想法:

用set來檢查是否出現過

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n,c=1;
    while(cin>>n){
        int arr[1005];
        set<int>st;
        for(int i=0;i<n;i++){
            cin>>arr[i];
        }
        bool flag=1;
        for(int i=0;i<n-1;i++){
            for(int j=i;j<n;j++){
                int tmp=arr[i]+arr[j];
                if(st.count(tmp)){
                    flag=0;
                    break;
                }
                st.insert(tmp);
            }
            if(!flag)break;
        }
        if(flag)cout<<"Case #"<<c<<": It is a B2-Sequence.\n";
        else cout<<"Case #"<<c<<": It is not a B2-Sequence.\n";
        c++;
    }
}

count()>0代表出現過

可以用unordered_set

m932

想法:

陣列操作(複雜)

用set來計算種類數

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
set<char>st;
int dx[6]={-1,0,1,1,0,-1};
int dy[6]={0,1,1,0,-1,-1};
int main(){
    string s="";
    cin>>n>>m>>k;
    char v[25][25];
    for(int i=0;i<n;i++){
        string b;
        cin>>b;
        for(int j=0;j<m;j++){
            v[i][j]=b[j];
        }
    }
    int x=n-1,y=0;
    for(int i=0;i<k;i++){
        int a,tx,ty;
        cin>>a;
        tx=x+dx[a];
        ty=y+dy[a];
        if((tx<n&&tx>=0)&&(ty<m&&ty>=0)){
            x+=tx-x;
            y+=ty-y;
        }

        st.insert(v[x][y]);
        s+=v[x][y];
    }
    cout<<s<<'\n';
    cout<<st.size();
}

count()>0代表出現過

可以用unordered_set

m932

想法:

陣列操作(複雜)

用set來計算種類數

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
set<char>st;
int dx[6]={-1,0,1,1,0,-1};
int dy[6]={0,1,1,0,-1,-1};
int main(){
    string s="";
    cin>>n>>m>>k;
    char v[25][25];
    for(int i=0;i<n;i++){
        string b;
        cin>>b;
        for(int j=0;j<m;j++){
            v[i][j]=b[j];
        }
    }
    int x=n-1,y=0;
    for(int i=0;i<k;i++){
        int a,tx,ty;
        cin>>a;
        tx=x+dx[a];
        ty=y+dy[a];
        if((tx<n&&tx>=0)&&(ty<m&&ty>=0)){
            x+=tx-x;
            y+=ty-y;
        }

        st.insert(v[x][y]);
        s+=v[x][y];
    }
    cout<<s<<'\n';
    cout<<st.size();
}

count()>0代表出現過

可以用unordered_set

Map

Map

前面教過陣列取值

int arr[105];
arr[5]=3;
cout<<arr[5]<<'\n';

陣列名稱[索引值]

索引5 對應到的值是3

那今天如果索引值不是整數?

只有辦法從0到陣列大小-1嗎?

Map

那肯定是可以的

map就是在做這件事

map<int,int>mp;//宣告
map<string,int>mp1;
map<string,string>mp2;

map能夠將一個鍵(key)

對應到一個值(value)

可以把它當查資料

輸入書名 告訴你作者是誰

新增與指派

map<int,int>mp;//宣告
map<string,int>mp1;
mp[5]=3;
mp[3]++;
mp1["abc"]=100;
mp.erase(5);//刪除

如果key存在 修改它的值

否則新增一個key

一個新的key預設值

整數是0

字串是""

查詢

map<int,int>mp;//宣告
mp[5]=3;
cout<<mp[5]<<'\n';
cout<<mp[0]<<'\n';

如果key存在 輸出它的值

否則新增一個value是預設值的key

所以mp[0]是0

遍歷

map<int,int>mp;//宣告
mp[5]=3;
mp[0]=10;
for(pair<int,int> i:mp){
	cout<<i.first<<' '<<i.second<<'\n';
}

會按照key的值由小到大排

型態是pair

first是key

second是value

pair太長了 寫auto比較簡略

Map

底層結構是紅黑樹

跟set一樣

插入、刪除、查詢的時間複雜度

都是O(log n) n是map大小

如果key不想要排序?

unordered_map

實作方式改為hash

失去順序

插入、查詢、刪除的複雜度變成O(1)

題目

a135

#include<bits/stdc++.h>
using namespace std;
int main(){
    unordered_map<string,string>mp;
    mp["HELLO"]="ENGLISH";
    mp["HOLA"]="SPANISH";
    mp["HALLO"]="GERMAN";
    mp["BONJOUR"]="FRENCH";
    mp["CIAO"]="ITALIAN";
    mp["ZDRAVSTVUJTE"]="RUSSIAN";
    int t=1;
    string s;
    while(cin>>s && s!="#"){
        cout<<"Case "<<t<<": ";
        if(mp[s]!="")cout<<mp[s]<<'\n';
        else cout<<"UNKNOWN\n";
        t++;
    }
}

基本的查詢

e641

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s,a;
    map<string,int>m{
        {"B",1},{"F",1},{"P",1},{"V",1},
        {"C",2},{"G",2},{"J",2},{"K",2},{"Q",2},{"S",2},{"X",2},{"Z",2},
        {"D",3},{"T",3},
        {"L",4},
        {"M",5},{"N",5},
        {"R",6}
    };
    int x=0;
    while(cin>>s){
        for(int i=0;i<s.size();i++){
            a=s[i];
            if(m[a]==0){
                x=0;
                continue;
            }
            else if(m[a]==x)continue;
            else x=m[a];
            cout<<x;
        }
        cout<<'\n';
    }
}

另一種建立key value的方式

其他都基本操作

d518

#include<bits/stdc++.h>
using namespace std;
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    int n;
    while(cin>>n){
        string s;
        int x=1;
        map<string,int>m;
        for(int i=1;i<=n;i++){
            cin>>s;
            if(m[s]!=0){
                cout<<"Old! "<<m[s]<<'\n';
            }
            else{
                m[s]=x;
                x++;
                cout<<"New! "<<m[s]<<'\n';
            }
        }
    }
}

利用map記第幾個

Priority_Queue

priority_queue

功能:將數字丟進去,由大排到小

可以查詢最大值

可以刪除最大值

優先隊列

宣告

priority_queue<int>pq;

如果想要由小排到大

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

加入/移除

priority_queue<int>pq;
pq.push(3);
pq.push(5);
pq.push(4);
pq.push(1);
pq.pop();
pq.push(2)

push():加入一個數字

pop():刪除最大值

查詢

priority_queue<int>pq;
pq.push(3);
pq.push(5);
pq.push(4);
cout<<pq.top();

top():查詢最大值

常用

priority_queue<int>pq;
pq.push(3);
pq.push(5);
pq.push(4);
cout<<pq.size();
if(!pq.empty())cout<<pq.top();

empty():回傳是否為空

size():回傳大小

其他

實作是用heap

資芽會教 去報名

複雜度雖然跟set一樣(O(log(n))

但常數比較小

有很多greedy的題目會用到

題目

d221

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
    int n,a;
    priority_queue<ll,vector<ll>,greater<ll>>pq;
    while(cin>>n &&n!=0){
        ll ans=0;
        for(int i=0;i<n;i++){
           cin>>a;
           pq.push(a);
        }
        while(pq.size()>1){
            int a,b,cost;
            a=pq.top();
            pq.pop();
            b=pq.top();
            pq.pop();
            cost=a+b;
            ans+=cost;
            pq.push(cost);
        }
        cout<<ans<<'\n';
        pq.pop();
    }
}

Bitset

布林陣列

bool arr[10005];

裡面都存布林值(0,1)

bitset

特別優化過的布林陣列

空間跟時間都有優化

尤其是時間

宣告

<>裡面放的是大小

bitset<100005>bs;

開一個大小為100005的陣列

語法

bitset<100005>bs;
b[3]=1;//第四項為true
b[2]=1;
cout<<b[1];

和布林陣列一樣

特殊語法

bitset<100005>bs;
b[3]=1;//第四項為true
b[2]=1;
cout<<b.count();
b.reset();
b.set();

count():回傳裡面1的數量

set():全部設成1

reset():全部設成0

快?

bitset可以進行位元運算

速度極快

大概是正常的1/32倍

所以有些題目可以用bitset優化

但那些題目挺難

之後有機會可以分享

遇到布林陣列的題目或位元運算操作的題目可以用bitset練習看看

 

常用工具/函式

min/max

用來求最小最大值

int a=10,b=5,c=11;
cout<<min(a,b);
cout<<max(b,c);
cout<<max({a,b,c});
//3個數字加大括號

要注意不同型別不能比較

long long跟int不能放一起

int a=10;
long long b=5;
cout<<min((long long)a,b);
//強制轉型

abs

絕對值

int a=10,b=-10;
cout<<abs(a)<<'\n';
cout<<abs(b)<<'\n';

回傳的型態跟傳入值一樣

swap

交換兩數

int a=10,b=130;
cout<<a<<' '<<b<<'\n';
swap(a,b);
cout<<a<<' '<<b<<'\n';

之前排序的code也有

可以交換陣列的兩項

sqrt

開根號

int a=100,b=15;
cout<<sqrt(a)<<'\n';
cout<<sqrt(b)<<'\n';

回傳型態跟輸入值相同

複雜度:

O(log n)

pow

pow(a,b)回傳a的b次方

int a=2,b=15;
cout<<pow(a,b)<<'\n';

複雜度不一定 雖然底層是用快速冪

但聽說因為很慢 被叫慢速冪

用浮點數運算

會有誤差 別用

__gcd

__gcd(a,b)回傳a,b的最大公因數

int a=243,b=15;
cout<<__gcd(a,b)<<'\n';

複雜度:

O(log\ max (a,b))

__lg

__lg(a)回傳a以2為底取對數值

int a=1024;
cout<<__lg(a)<<'\n';

複雜度:

O(log\ n)

傳入必須是整數

回傳會取整

log

回傳以e為底的對數值

int a=50;
cout<<log(a)<<'\n';

複雜度:

O(log\ n)

浮點數運算

sort

sort(a,b)

將a位置(包含)到b位置(不包含)元素由小到大排

int arr[4]={3,2,1,4};
sort(arr,arr+4);
for(int i=0;i<4;i++){
	cout<<arr[i]<<' ';
}

複雜度:

O(n\ log\ n)

開始     結束

sort

隱藏參數?

sort(a,b,cmp)

cmp如果回傳false則交換

bool cmp(int a,int b){
	return a>b;
}

從大排到小

也有其他種 比如二維點或其他種structure

greater<int>()

reverse

reverse(a,b)

將a位置(包含)到b位置(不包含)元素反轉

int arr[4]={3,2,1,4};
reverse(arr+1,arr+4);
for(int i=0;i<4;i++){
	cout<<arr[i]<<' ';
    //3 4 1 2
}

字串蠻常用到的

min(/max)_element

min/max_element(a,b)

回傳a位置(包含)到b位置(不包含)最小/大值的指標

int arr[4]={3,2,1,4};
cout<<*min_element(arr,arr+4)<<'\n';
cout<<min_element(arr,arr+4)-arr;

找到的位置-第一項的位置=它的index

fill

fill(a,b,c)

回傳a位置(包含)到b位置(不包含)的值變成c

int arr[4]={3,2,1,4};
fill(arr,arr+3,6767);
for(int i=0;i<4;i++){
	cout<<arr[i]<<' ';
}

可用於初始化陣列

lower_bound

lower_bound(a,b,c)

回傳a位置(包含)到b位置(不包含)第一個>=c的位置

int arr[4]={-10,2,12,48};
sort(arr,arr+4);
cout<<*lower_bound(arr,arr+4,3)

回傳b的話 代表沒找到

要再由小到大排的陣列才可使用

upper_bound

upper_bound(a,b,c)

回傳a位置(包含)到b位置(不包含)第一個>c的位置

int arr[4]={-10,2,12,48};
sort(arr,arr+4);
cout<<*upper_bound(arr,arr+4,3)

回傳b的話 代表沒找到

要再由小到大排的陣列才可使用

IO優化

IO優化

有時候題目一直TLE

卻想不到怎麼優化

輸入量又很大

怎麼辦

IO優化!

IO優化

ios::sync_with_stdio(0),cin.tie(0);

可以當成模板打

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0),cin.tie(0);
}

放在main函式裡面

可以加速輸入輸出

IO優化

ios::sync_with_stdio(0),cin.tie(0);

可以當成模板打

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0),cin.tie(0);
}

放在main函式裡面

可以加速輸入輸出

但是打上去後cin就不能和scanf混用

IO優化

sync_with_stdio(0)

如何優化的?

會關閉cin cout scanf printf 的同步

減少性能損失

所以打了之後不能混用

IO優化

cin.tie(0)

如何優化的?

當電腦cin的時候 會刷新緩衝區

讓輸入前將輸出完成

但就會耗費很多時間

cin.tie(0)

關閉了此動作

所以可以加速

IO優化

cin.tie(0)

如何優化的?

當電腦cin的時候 會刷新緩衝區

讓輸入前將輸出完成

但就會耗費很多時間

cin.tie(0)

關閉了此動作

所以可以加速

指標

指標

類似一個變數的概念

但它存的是16進位的記憶體位置

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a[10] = {2,4,3,5,12,5,23,6,3,6};
    cout<<a<<endl;
}

去跑跑看

會回傳a[0]的記憶體位置

指標

如果知道一個指標

要怎麼知道真正的值

*

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a[10] = {2,4,3,5,12,5,23,6,3,6};
    cout<<*a<<endl;
}

指標

那如果知道一個值

要怎麼知道它的指標

&

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a = 5;
    int *ptr = &a;
    cout<<ptr<<endl;
}

指標

C++的*有兩種功能

(1)取出指標的值

(2)指標變數

當這個變數要存指標時 就需要加*

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a = 5;
    int *ptr = &a;
    cout<<ptr<<endl;
}

指標

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a = 5;
    int *ptr = &a;
    cout<<a<<endl;
    *ptr = 10;
    cout<<a<<endl;
}

既然ptr存的是a的記憶體位置

那是不是就可以直接改a的值

指標

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a[10] = {23,4,3,5,12,5,23,6,3,6};
    cout<<*(a+4)<<endl;
    cout<<*(a+7)<<endl;

}

好想有點似曾相識?

指標

int main(){
    int a[10] = {23,4,3,5,12,5,23,6,3,6};
    cout<<*(a+4)<<endl;
    cout<<*(a+7)<<endl;

    cout<<a[4]<<endl;
    cout<<a[7]<<endl;
}

沒錯 跟[]是一模一樣的東西

指標

小結:

非常抽象

是個很厲害很好用的工具

linked list,動態開點線段樹...

還有一堆極抽象的東西

在高中競程用的比較少

我有很熟

STL

By wuchanghualeo

STL

  • 202