【筆記】資料結構與演算法 (JavaScript) (1) - Linear Search & Binary Search
2月 16, 2023
最近又上了這門課程-資料結構與演算法 (JavaScript) ,一樣附上 Udemy 課程連結給有需要的人參考唷!這門課是全中文授課的,如果對於自己的英文能力沒那麼有把握的話可以選擇這門課程,教得很好懂唷!這篇文章的重點會著重在 Linear Search 線性搜尋法 與 Binary Search 二分搜尋法這兩種演算法,也會分享 Wilson 在課程中特別分享的 Counter 與 Pointer 兩種常見的解題技巧。
先看看這張圖,大概就可以理解這兩個演算法的差異:
什麼是演算法? 什麼是演算法?來看看牛津字典的定義吧!”A process or set of rules to be followed in calculations or other problem-solving operations, especially by a computer.” 演算法是一個有序、有步驟與規則的計算過程,而這個計算過程能幫助我們解決特定問題。
演算法也會有效能與適用在解決不同問題的差異,而演算法的效能會以「空間複雜度」以及「時間複雜度」作為評估標準,想了解空間複雜度可以參考這篇文章唷!Master the Coding Interview (1) - BigO 時間複雜度
Linear Search (線性搜尋法) Linear Search 線性搜尋法大概是這門課程中最好懂的演算法了,線性搜尋法簡單來說就是從頭到尾一個個的搜尋,直到找到目標為止,例如下方的程式碼,就是遍歷 Array 中的每一個位置直到找到答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const arr = [1, 3, 5, 7, 9, 11, 13, 15, 17]; const LinearSearch = (arr, n) => { for (let i = 0; i < arr.length; i++) { if (arr[i] === n) { return i; } } return -1; }; LinearSearch(arr, 13); // 6 answer
Linear Search 的時間複雜度:
1. Worst Case:O(n) 答案很慘的在第 n 個位置2. Best Case:O(1) 答案很讚的在第 1 個位置3. Average Case:O(n/2)
Binary Search (二分法) Binary Search 二分法顧名思義就是不斷地分成兩部分再去針對特定一半做搜索。以下方的函式為例,當我們要在一個 Array 中找到目標數值,我們可以先將 Array 從中切開,取中間數值跟目標數值比對,刪去不可能的一半,再繼續對半,以此類推可以一直省去不必要的運算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const arr = [1, 3, 5, 7, 9, 11, 13, 15, 17]; const BinarySearch = (arr, n) => { let min = 0; let max = arr.length - 1; while (min <= max) { let middle = Math.floor((min + max) / 2); if (n > arr[middle]) { min = middle + 1; } else if (n < arr[middle]) { max = middle - 1; } else if (n === arr[middle]) { return middle; } } return -1; }; BinarySearch(arr, 17);
BinarySearch 的時間複雜度:
1. Worst Case: O(log n)2. Best Case: O(1) 答案在 1/2 的位置3. Average Case: O(log n)
二分法範例題: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const averagePair = (array, avg) => { let left = 0; let right = array.length - 1; let result = []; while (right > left) { let temp = (array[right] + array[left]) / 2; if (temp > avg) { right--; } else if (temp < avg) { left++; } else if (temp === avg) { result.push([array[right], array[left]]); right--; left++; } } return result; }; averagePair([-11, 0, 1, 2, 3, 9, 14, 17, 21], 1.5); // [ [ 14, -11 ], [ 3, 0 ], [ 2, 1 ] ] answer
接著是兩個衍伸出來非常實用的解題技巧,分別是 Counter 與 Pointer(Wilson 有提到這樣的概念其實有很多種名稱,只是在這門課程裡用 Counter 與 Pointer 作為技巧名字。)
解題技巧 1:Counter Counter 就是建立一個計算區 (object),透過 key and value pair 的方式暫時記著要運算的數值,最後在從 key and value pair 中取得我們要的答案,這樣可以簡化運算的歷程。
1. General Skill when doing algorithm design 2. Using a counter object will reduce the complexity of algorithm
(1) 範例題:Frequency Problem 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 const SameFrequency = (string1, string2) => { const arr1 = string1.split(''); const arr2 = string2.split(''); if (arr1.length !== arr2.length) { return false; } let counter1 = {}; let counter2 = {}; for (let i = 0; i < arr1.length; i++) { if (counter1[arr1[i]]) { counter1[arr1[i]]++; } else { counter1[arr1[i]] = 1; } } for (let i = 0; i < arr2.length; i++) { if (counter2[arr2[i]]) { counter2[arr2[i]]++; } else { counter2[arr2[i]] = 1; } } for (let property in counter1) { if (!counter2[property]) { return false; } if (counter2[property] !== counter1[property]) { return false; } } return true; }; // counter1 { a: 2, b: 1, c: 1 } counter2 { a: 1, b: 2, c: 1 } SameFrequency('aabc', 'abbc'); // false answer
解題技巧 2:Pointer Pointer 中文是指標、定位的意思,Pointer 常用在解 string 或是 array 比對的考題中,我們可以透過定位指出特定位置的數值與另一個位置的數值做比對,像第一題 Palindrome 對稱題,就是第一個位置與最後一個位置比對,接著是第二個位置與倒數第二個位置做比對,以此類推,這樣的解法就是運用 Pointer 的概念。
(1) 範例題:Palindrome (判斷單詞是否對稱) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const palindrome = (str) => { let left = 0; let right = str.length - 1; while (left <= right) { if (str[left] === str[right]) { right--; left++; } else { return false; } } return true; }; palindrome('tacocat'); //true palindrome('hsuan'); //false palindrome('madam'); //true
圖解 Pointer 概念:
接下來這一題 Subsequence Problem 比較複雜,因為題目允許刪除中間多餘的字串,我們一樣把兩個 array 第一個位置的數值做比對,但是當值不同時,與之比對的字串就要往下再移一個位置去做比對,直到找到對應的值為止。
(2) 範例題:Subsequence Problem 判斷長字串中是否包含段字串的字母 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const isSubsequence = (str1, str2) => { if (str1.length === 0) { return true; } let pointer1 = 0; let pointer2 = 0; while (pointer2 < str2.length) { if (str1[pointer1] === str2[pointer2]) { pointer1++; } if (pointer1 >= str1.length) { return true; } pointer2++; } return false; }; isSubsequence('hello', 'hello World'); //true isSubsequence('book', 'brooklyn'); //true isSubsequence('abc', 'bac'); //false
以上就是資料結構與演算法 (JavaScript) (1) - Linear Search & Binary Search 的課程摘要與練習題,很好我們一腳踏進演算法的世界了 XD
參考文章:https://tw.alphacamp.co/blog/algorithm-introduction