C++[2]

目錄

複雜度

複雜

複雜度的用處

衡量一個程式的效率

有分時間效率與空間效率

怎麼衡量?

\(O()\)漸進符號

高一數學,方程式的廣域特徵

當我做一個三次方程式

把他拉遠

\(y = x^3\)

\(y = x^3+2x^2+3x+1\)

即使方程式不同,圖形大概的趨勢仍然是三次方程式

當 \(x\),也就是輸入的的資料量極大時

則整體趨勢幾乎只受到最大冪次 \(x^3\) 的影響

回到漸進符號

我們把剛剛方程式裡的 \(y\) 想成電腦的運算次數

\(x\) 想成輸入的資料量

假設我現在的程式,他的運算次數 \(y=x^3+20x^2+17x+101\)

當輸入的資料量極大時,運算次數增加的趨勢幾乎只受到\(x^3\)的影響

所以我們稱此程式的複雜度為 \(O(N^3)\)

漸進符號用的資料量通常以 \(N\) 表示

空間複雜度同理

如果運算次數與資料量無關呢?

則複雜度為 O(1),也就是常數複雜度

衡量完之後要幹嘛

當然就是要判斷你的程式會不會超時了

電腦的計算速度大約為每秒 \(10^9\) 次

拿出剛才的複雜度 \(O(N^3)\)

這時候當 \(N\) 大於 \(\sqrt[3]{10^9}=10^3\)時,這個程式通常無法在 1 秒內跑完

當然還要考慮一下常數的影響

所以這個 \(N\) 的上限一定會再低一點

衡量完之後要幹嘛

對於一個競賽題目中,輸入資料量的上限,至少需要多低複雜度的程式才能在 1~3 秒內跑完

STL

STL

是 C++ 內建的各種資料結構、演算法、迭代器......

反正很好用,也很常用

Vector

向量?

高級陣列

vector 與傳統陣列最大的不同,就是他可以隨時改變大小,有些題目用 vector 會好寫很多

宣告

vector<int>a(n);

宣告一個名稱為 a,大小為 n,值的資料型態為 int 的 vector

vector<int>a;

也可以不給大小

vector<int>a = {0, 1, 2, 3, 4};
vector<int>b(n, 0);

也可以先給定值

也可以初始化(歸零)

宣告

vector<vector<int>>a(n, vector<int(m, 0));

二維陣列,n*m 的大小

並歸零

常用語法

vector<vector<int>>a;
a.push_back(1);
a.push_back(3);
a.push_back(2);
//a = {1, 3, 2}
a.pop_back();
//a = {1, 3}

push_back:從陣列的尾端推一個元素進來

pop_back:把尾端的元素推出去

常用語法

vector<int>a;
    for(int i = 0 ; i < 19 ; i++) a.push_back(1);
    cout<<a.size() << ' ' << a.capacity(); //19 32
    return 0;

size:抓目前有使用到的空間大小

capacity:抓目前整個陣列(包含以擴充但未使用部分)

甚麼意思?

常用語法

vector<int>a;
a.push_back(1);
//size=1 capacity=1
a.push_back(1);
//size=2 capacity=2
a.push_back(1);
//size=3 capacity=4
a.push_back(1);
//size=4 capacity=4
a.push_back(1);
//size=5 capacity=8

每次push_back時,如果空間不夠,就會先把實際大小擴充成原本的兩倍

常用語法

vector<int>a;
a.push_back(1);
//size=1 capacity=1
a.push_back(1);
//size=2 capacity=2
a.push_back(1);
//size=3 capacity=4
a.push_back(1);
//size=4 capacity=4
a.push_back(1);
//size=5 capacity=8

每次push_back時,如果空間不夠,就會先把實際大小擴充成原本的兩倍

常用語法

vector<int>a;
auto bit = a.begin();
auto eit = a.end();

a.begin():陣列中第一個值的 iterator

a.end():陣列中最後一個值的 iterator

sort(a.begin(), a.end());
reverse(a.begin(), a.end());

可用於 sort, reverse, upper/lower bound 等地方

不好用的語法

vector<int>a(2, 3);
a.insert(1, 4);
//a = {3, 4, 3}

a.insert(位置, 數值);

在陣列中的一個位置插入一個數值

\(O(N)\),爛

vector<int>a(15, 3);
a.erase(a.begin() + 3, a.end());
//a = {3, 3, 3}
a.erase(a.begin());
a = {3, 3}

a.erase(iterator, iterator);

把陣列中的一段數值刪除

即使只有刪除一個數值,複雜度還是能到\(O(N)\),還是爛

pair

一對

一次存兩個變數

一個 pair 有兩個變數,而且資料型態可以不同

像是我可以把一個人名配對一個分數,再對他做排序等動作,兩個被包在一起的元素就會一起移動

宣告

pair<string, int>a;

宣告一個包著 string 與 int 的 pair

pair<string, int>a(wut, 17);

可以初始化

vector<pair<int, int>>a;

還能包進其他 STL 裡

語法

pair<string, int>a(wut, 17);
a.first = "eee"
cout << a.first; //eee
cout << a.second; //17

a.first:pair 的第一個變數

a.second:pair 的第二個變數

注意,這兩個後面都不用加()

pair<string, int>a;
a = make_pair("idk", 123);

make_pair()生成一個 pair

遍歷

便利

python for

a = [1, 2, 4, 3, 5]
for i in a:
	print(i, end = ' ')
#1 2 4 3 5

如果你想要把整個陣列的數字一個一個輸出,在 python 中有這種寫法

注意這時候的 i ,他不像一般的 for 迴圈裡的 i,是一個 int而是跑到陣列裡面變成陣列裡的數值
稱為迭代器

要怎麼用 C++ 寫?

C++ for

vector<int> a = {1, 2, 3, 4, 5};
for (int i = 0 ; i < a.size() ; i++){
	cout << v[i] << ' ';
}
//1 2 3 4 5
vector<int> a = {1, 2, 3, 4, 5};
for(vector<int>::iterator it = a.begin() ; it != a.end() ; it++){
        cout << *it << " ";
}
//1 2 3 4 5

欸不是這太噁心了吧

還記得 auto 嗎

vector 有自己迭代器的資料型態

C++ for

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

還是沒有比原本的好寫啊?

vector<int> a = {1, 2, 3, 4, 5};
for(auto i : a){
        cout << i << " ";
}
//1 2 3 4 5

這很簡短了

基本上這跟 python 的寫法是一樣的

a = [1, 2, 4, 3, 5]
for i in a:
	print(i, end = ' ')
#1 2 4 3 5

stack

一疊椅子

你們應該知道這種椅子

當你要拿走椅子的時候,都會是從最上面拿

當你要放椅子的時候,也是從最上面放

想一下現在有一張椅子,他是第一個放進去的

當你要拿椅子的時候,他是不是在最下面,最後一個才能拿到

這種先進後出(LIFO)的資料結構即為 stack

常用語法


Copy of Minimal

By 硼/Boron

Copy of Minimal

  • 36