奧林匹亞資訊班

7/26

講師:吳崇維(wayne)

Pre-reading

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 容器

STL

 

STL 是 Standard Template Library 的縮寫,他提供了許多好用的標準模板函式庫,只要引入標頭檔就可以作使用。

STL們:

stack

queue

vector

set

map

priority_queue

...

STL

使用方法

 

先前提到過stack 這個資料結構

也自己用陣列實作過

以下先稍微複習,再看如何用STL引出stack !

STL們:

stack

queue

vector

set

map

priority_queue

...

Stack

盤子, 就是 stack 的最佳形容

 

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還可以衍生出一些操作

 

  • top();   將最上面的元素找出來,但不pop掉
  • getSize();  返回整個stack目前的大小。
  • isEmpty(); 確認stack是否為空,如果空的話,return 1;

如何用STL引出stack?

STL stack 的幾個操作

  • top()
  • pop()
  • push()
  • size()

STL使用

 

首先如果需要用stack

需要引入 #include <stack>

再來如同宣告變數一樣,要宣告容器:stack<int> a;

意思就是有一個stack,裡面裝int,stack的名字叫做a

 

再來用到stack的功能則可以這樣用

a.top()                  // 回傳stack最上面的元素

a.push(10)          //  把一個元素放進stack裡面,沒有回傳值

a.pop()                //  把一個元素從stack拿出來

a.size()                //   回傳現在stack中有幾個元素

STL使用

 

練習寫一個程式!

 

一開始有一個空的stack,接著程式會讀取指令 (從cin讀入)

如果指令是 1 , 那麼接下來就會再讀取一個數字,並push進去stack

如果指令是 2 , 那麼就會從stack pop出一個元素

如果指令是 3 , 那麼接下來就會印出stack最上方的元素

如果指令是 4 , 那麼接下來就會印出stack現在的大小

 

P.S. 上面這些指令都是剛剛提過的STL功能

FeedBack!

一起努力快樂寫程式吧!

7/26 暑期課程 - 一些算法

By Wayne Wu

7/26 暑期課程 - 一些算法

  • 40