7/26
講師:吳崇維(wayne)
https://medium.com/appworks-school/初學者學演算法-談什麼是演算法和時間複雜度-b1f6908e4b80
二分搜尋
binary search
概念
當有一些排列過的資料,想要找到其中一個元素的時候,我們可以使用二分搜尋提升速度!
舉例 1
終極密碼!
關主會挑選一個1~100的數字
猜的人要說一個數字,關主會說數字比他大或小
誰先猜到就 贏 / 輸了~
如果要最快的猜到密碼,有沒有什麼方法呢?
舉例 1
方法 1 : 從1一個一個猜到100
這樣的期望值 (平均下來猜到) 需要 50 次
這個算法的複雜度是線性時間 也就是O(N)
也就是說,要是今天數字範圍是1~1000,猜到就需要平均500次
猜到的次數與數字範圍成正比
舉例 1
方法 2 : 剖西瓜法
先猜50,接著看答案比50大或小,往那半邊去詢問
假設答案是30,猜測過程就會是
50 => 25 => 37 => 31 => 28 => 30
顯然他可以很快的就逼近正確答案!
對數概念
(指數對數是高中數學的概念,這邊大概帶過)
log是一個運算符號,其中以2為底的log
log2 = 1
log4 = 2
log8 = 3
log1024 = 10
我們可以發現把很大的數字丟進log,出來的數字會大幅減少
對數概念
而剖西瓜法就是運用了二分搜尋的概念
需要的時間是資料量的 log 級
(現階段知道時間會大幅縮小就可以了)
我們記作 O(logN)
1. 從1猜到100 時間 O(N) 慢!
2. 剖西瓜法 時間 O(logN) 非常快!
舉例 2
陣列 : [1 3 4 5 6 8 9 10 20]
現在往程式的撰寫方面下手,我們假定已經有一個排序好的陣列
我們想要從這個陣列中找到一個特定的數字 比如說 6
那麼上面的例子,先找7,再找4,再找6
演算法寫法
二分搜的寫法有很多種,這邊介紹我個人習慣的一閉一開
什麼是閉開呢?
簡單來說,二分搜會有兩個變數,分別代表現在搜尋的範圍左右界
閉區間就是現在所指的地方有可能是答案
開區間就是現在所指的地方不可能是答案
演算法寫法
白板演示
先給你陣列長度N (從cin讀入)
再給你一個陣列 (從cin讀入) 保證降序排列
接著給你1個數字,輸出他在陣列的哪個位置!
範例輸入:
5
9 8 8 8 7 7 7 7 7 6 6 6 6 5
7
輸出:
2
https://codeforces.com/problemset/problem/474/B
https://codeforces.com/problemset/problem/706/B
快速冪
通常我們需要某個數的某個次方
Ex : 2 的 10次方 = 1024
可以這樣寫
int x = 1;
for (int i = 0; i < 10; i++) {
x *= 2;
}
cout << x;
但是要是我們需要一個數字的超級大次方
例如 2 的 100000000 次方 ,這樣子寫 for 就太慢了
int x = 1;
for (int i = 0; i < 1000000000; i++) {
x *= 2;
}
cout << x;
問題:想要求取 2 的 10000000000 次方的後三位數
如果用for的話會需要跑一段時間
int x = 1;
for (int i = 0; i < 10000000000; i++) {
x = x * 2;
x = x % 1000;
}
問題:想要求取 2 的 10000000000 次方的後三位數
使用快速冪的技巧可以加速成log級 先看code
int k = 10000000000;
int a = 2;
int x = 1;
while (k != 0) {
if (k % 2 == 1) {
x = x * a;
x = x % 1000;
}
a = a * a % 1000;
k = k/2;
}
白板講解
int k = 10000000000;
int a = 2;
int x = 1;
while (k != 0) {
if (k % 2 == 1) {
x = x * a;
x = x % 1000;
}
a = a * a % 1000;
k = k/2;
}
STL 是 Standard Template Library 的縮寫,他提供了許多好用的標準模板函式庫,只要引入標頭檔就可以作使用。
STL們:
stack
queue
vector
set
map
priority_queue
...
使用方法
先前提到過stack 這個資料結構
也自己用陣列實作過
以下先稍微複習,再看如何用STL引出stack !
STL們:
stack
queue
vector
set
map
priority_queue
...
盤子, 就是 stack 的最佳形容
stack 的特性是:先進後出
也就是可以想像成
你先疊進去的盤子(越下面)
一個一個拿出來的時候,就越晚拿出來
stack 有兩種基本操作
1. push
將一個元素(通常是數字)
放進stack裡面
(將一個盤子疊上去)
2. pop
將一個元素從 stack 裡面拿出來
並且只能拿最上面的元素
(將一個盤子輕輕拿走)
Stack 操作模擬:
push(4);
push(3);
push(2);
push(1);
pop();
pop();
push(5);
pop();
pop();
pop();
[] 前方代表堆疊下方,尾端是上方
[4]
[4,3]
[4,3,2]
[4,3,2,1]
[4,3,2]
[4,3]
[4,3,5]
[4,3]
[4]
[]
stack還可以衍生出一些操作
首先如果需要用stack
需要引入 #include <stack>
再來如同宣告變數一樣,要宣告容器:stack<int> a;
意思就是有一個stack,裡面裝int,stack的名字叫做a
再來用到stack的功能則可以這樣用
a.top() // 回傳stack最上面的元素
a.push(10) // 把一個元素放進stack裡面,沒有回傳值
a.pop() // 把一個元素從stack拿出來
a.size() // 回傳現在stack中有幾個元素
練習寫一個程式!
一開始有一個空的stack,接著程式會讀取指令 (從cin讀入)
如果指令是 1 , 那麼接下來就會再讀取一個數字,並push進去stack
如果指令是 2 , 那麼就會從stack pop出一個元素
如果指令是 3 , 那麼接下來就會印出stack最上方的元素
如果指令是 4 , 那麼接下來就會印出stack現在的大小
P.S. 上面這些指令都是剛剛提過的STL功能
一起努力快樂寫程式吧!