# Algorithm & Data Structure in Javascript
[演算法解說](http://alrightchiu.github.io/SecondRound/mu-lu-yan-suan-fa-yu-zi-liao-jie-gou.html)
[傑哥演算法筆記](https://medium.com/%E7%AA%AE%E6%8F%9A%E5%82%91%E7%9A%84%E6%99%AE%E9%80%9A%E5%B8%B8%E8%AD%98/algorithm/home)
<font color="red">**前端必考**</font>
- bubble sort
- binary tree
一般會用到quick sort和insertion sort
merge sort在資料量小才會用
![](https://i.imgur.com/bDIiTRT.png =600x)
## Real Example in Life
- Google Map
- Youtube
- FB/IG
- Excel
## Comparing Algorithm
分為時間和空間比較, 進行比較時不可以拿執行環境(runtime)做比較, 因為不同電腦配備不同執行後的結果也會不同
> code's runtime is not realistic
> different computer power machine gives different run time
## Complexity 複雜度
主要指時間複雜度
越多運算子operator(><= etc..), 就表示運用的時間就會越多, 有幾個就會乘上幾次, complexity正向增加
- `+` addition
- `-` subtraction
- `*` multiplication
- `/` division
### example
```=javascript
function exmaple(n) {
for (i< 3 *n){
}
for (i<n) {
for(j<n) {
}
}
console.log('h');
console.log('h');
console.log('h');
console.log('h');
}
// 執行
example(2)
```
從上述例子看總共會印幾次h?
第一個外圈是`3*2`, 第二個外圈和內圈是`2*2`, 最後4次h
所以是`(3*2)+(2*2)+4 =14`
### 用公式解說複雜度
`f(n)=n^2 + 3n + 4`
- `n` 表示input size
- `f(n)` 表示 complexity
解一元二次方程式, 帶入數字來看, 會得到
```
n=2, f(n)=14
n=3, f(n)=22
n=4, f(n)=32
n=5, f(n)=44
n=9, f(n)=112
```
從14到112是10倍的成長, 平方越大時間複雜度越高, 花費時間也會越久
## Big O Notation
### 怎麼取出Big O
- constant 不看
- small terms 不看
- Log 底數不看
### example
- f(n)= 3n, Big O是 `O(n)`
- f(n)= 3n^2+6n, Big O是 `O(n^2)`
- f(n)= log2n, Big O是 `O(Log n)`
- f(n)= 2n, Big O是 `O(n)`
- f(n)= 13n^3+6n+7, Big O是 `O(Log n)`
- f(n)= 4log2n, Big O是 `O(Log n)`
- f(n)= 5, Big O是 ==O(5), 但要算換成1,所以是O(1)==
### 好到壞
平方的時間複雜度越高, 通常會想辦法把`5`和`6`,優化成`3`和`4`
1. O(1)
2. O(Logn)
3. O(n)
4. O(nLogn)
5. O(n^2)
6. O(n^3)
### Array and Object
- Object, Array是Hash Table的資料結構
- Hash Table的優點是佔用記憶體小
- 物件和陣列皆有`insertion`,`removal`,`searching`,`accessing`對應的Big O
- 底線沒有任何意義,是為了分開obj,arr
``` mermaid
graph TD;
Object-->_insertion;
Object-->_removal;
Object-->_searching;
Object-->_accessing;
```
- _insertion: O(1)
- _removal: O(1)
- _searching: O(n)
- _accessing: O(1)
``` mermaid
graph TD;
Array-->insertion;
insertion-->push;
insertion-->unshift;
Array-->removal;
removal-->pop;
removal-->shift;
Array-->searching;
Array-->accessing;
```
- insertion-push: O(1) 新增元素至末端
- insertion-unshift: O(n) 新增元素到開頭
- removal-pop: O(1) 移除末端元素
- removal-shift: O(n) 移除開頭元素
- searching: O(n)
- accessing: O(1)
Queue: shift, unshift 先進先出 FIFO
Stack: pop, push 後進先出 LIFO
![](https://i.imgur.com/hjcXAMd.png =600x)
![](https://i.imgur.com/yexDrws.png =600x)
## Algorithm Design
### Linear Search
漸進式搜尋, sequentially check each element
![](https://i.imgur.com/Lpj8cPu.jpg =500x)
- Pseudocode
```
for i form O+0
array.length -1:
if(array[i] == n):
return 1
return -1
```
- Coding Practice
```=javascript
function linearSearch(arr, n) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === n) {
console.log("Found number " + n + " at index " + i);
return i;
}
}
console.log("Cannot find " + n);
return -1;
}
linearSearch(numbers, 24);
```
- Overview
- worst: O(n), 要找的i在arr的最後一項
- best: O(1), 要找的在arr的第一項
- average: O(n/2)
### Binary Search
二元搜尋, 切一半找
- 只用在已排序的陣列 ==sorted array==
- more efficient
![](https://i.imgur.com/CjD46FM.png =500x)
- Coding Practice
```=javascript
function binarySearch(arr, n) {
let min = 0;
let max = arr.length - 1;
let step = 0;
while (min <= max) {
step++;
// floor取整數
let middle = Math.floor((max + min) / 2);
if (n > arr[middle]) {
min = middle + 1;
} else if (n < arr[middle]) {
max = middle - 1;
} else if (n === arr[middle]) {
console.log("Found number " + n + " at position " + middle);
console.log("Found it after " + step + " steps.");
return middle;
}
}
console.log("Cannot find number " + n);
return -1;
}
binarySearch(numbers, 213); // 6 - 7
```
以8個元素為例,除2再除2,就會是
8 > 4 > 2 > 1
at lest 3 steps
換成Log就是 Log2的8 = 3, 也就是Log2的n
- Overview
- worst: O(Logn)
- best: O(1)
- average: O(Logn)
### Intersection Problem 交集
找出兩個陣列中重複的元素
![](https://i.imgur.com/H56vbMu.png =500x)
- O(n^2)
- Coding Practice
```=javascript
function intersection(arr1, arr2) {
let result = [];
for (let i = 0; i < arr1.length; i++) {
for( let j = 0; j < arr2.length; j++) {
console.log(arr1[i] , arr2[j]);
if(arr1[i] == arr2[j]) {
result.push(arr1[i]);
}
}
}
console.log(result);
return result;
}
intersection([1,2,3,7,9], [5,16,10,3,1]);
```
### Counter
可以改善Intersection Problem
讓交集的intersction==O(n^2)== 變成 ==O(n)==
![](https://i.imgur.com/9qdhjxu.png =500x)
- O(n)
- Coding Practice
```=javascript
intersection([1,2,3,7,9], [5,16,10,3,1]);
function intersction( arr1: number[], arr2: number[]) {
let res: number[] = [];
let arr3: any[] = arr1.concat(arr2);
let counter: any = {};
for( let i: number = 0; i < arr3.length; i++ ) {
if(!counter[arr3[i]]){
counter[arr3[i]] = 1;
} else {
counter[arr3[i]] ++;
}
}
for ( let property in counter) {
if(counter[property] >= 2) {
res.push(+property);
}
}
console.log(`res ${res}`);
return res;
}
```
### Frequency Problem
- for*3 -> O(3n) -> O(n)
- Coding Practice
```=typescript
function sameFreq( st1: string, st2: string): boolean {
let ar1: string[] = st1.split('');
let ar2: string[] = st2.split('');
if(ar1.length !== ar2.length) return false;
let counter1: any = {};
let counter2: any = {};
for( let i: number = 0; i < ar1.length; i++) {
if(!counter1[ar1[i]]) {
counter1[ar1[i]] = 1;
} else {
counter1[ar1[i]] ++;
}
}
for( let i: number = 0; i < ar2.length; i++) {
if(!counter2[ar2[i]]) {
counter2[ar2[i]] = 1;
} else {
counter2[ar2[i]] ++;
}
}
console.log(counter1, counter2);
for(let property in counter1) {
console.log(`c1: ${counter1[property]}`);
console.log(`c2: ${counter2[property]}`);
if(!counter2[property]) return false;
if(counter2[property] !== counter1[property]) return false;
}
return true;
}
sameFreq("aabc", "abbc");
```
### Average Pair
- 只用在已排序的陣列 ==sorted array==
![](https://i.imgur.com/JmJ2ePi.png =500x)
- O(n^2)
- 找出成對的數字
- Coding Practice
```=javascript
averagePair([-11, 0, 1, 2, 3, 9, 14, 17, 21], 1.5);
function averagePair(arr, avg) {
let result = [];
// 事情做完才+1
// body做完, j才會++
// j的for做完, i才會++
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
if ((arr[i] + arr[j]) / 2 == avg) {
result.push([arr[i], arr[j]]);
}
}
}
// O(n^2) => O(n)
console.log(result);
return result;
}
```
### Pointer
可以解決
1. AvagePair
2. Palindrome
3. Subsequence Problem
4. Slide Window
![](https://i.imgur.com/fXStHAZ.png =500x)
- 讓==O(n^2)== 變成 ==O(n)==
- O(n)
- Coding Practice
```=javascript
averagePair([-11, 0, 1, 2, 3, 9, 14, 17, 21], 1.5);
function averagePair( arr: number[], avg: number) {
let left: number = 0;
let right: number = arr.length -1;
let res: number[] = [];
while( right > left) {
let tempAvg = (arr[right] + arr[left])/2;
if( tempAvg > avg) {
right --;
} else if ( tempAvg < avg) {
left ++;
} else if ( tempAvg === avg) {
res.push(arr[right], arr[left]);
right --;
left ++;
}
}
console.log(res);
return res;
}
```
### Palindrome
從前或後讀是一樣的, i.e. `tacocat`
可以用pointer解
![](https://i.imgur.com/CdYCCXl.png =500x)
- O(Log n)
- Coding Practice
```=javascript
palindrome('tacocat');
// palindrome('amanaplanacanalpanama');
// palindrome('asdfsafeaw');
function palindrome(str: string): boolean | void{
let left: number = 0;
let right: number = str.length -1;
while( right >= left) {
if(str[left] === str[right]) {
left ++;
right --;
} else {
return false;
}
console.log(true);
return true;
}
}
```
### Subsequence Problem
不能改變原本的相對位置, [`book`, `brooklyn`]
可以用pointer解
![](https://i.imgur.com/jVBDDjB.png =500x)
- O(Log n)
- Coding Practice
```=javascript
// isSubsequence("hello", "hello Dear"); // true
// isSubsequence("book", "brooklyn"); // true
// isSubsequence("abc", "bac"); // false (order matters)
isSubsequence("", "abc"); // true
function isSubsequence(str1, str2) {
if (str1.length == 0) {
console.log(true);
return true;
}
let pointer1 = 0;
let pointer2 = 0;
while (pointer2 < str2.length) {
if (str1[pointer1] == str2[pointer2]) {
pointer1++;
}
if (pointer1 >= str1.length) {
console.log(true);
return true;
}
pointer2++;
}
console.log(false);
return false;
}
```
### Slide Window
![](https://i.imgur.com/jwTykzM.jpg =400x)
Max Sum
Min Sum
![](https://i.imgur.com/JPaFRu5.jpg =400x)
![](https://i.imgur.com/eqVKO9V.jpg =400x)
- O(n^2)
- Coding Practice
```=javascript
maxSum([2, 7, 3, 0, 6, 1, -5, -12, -11], 2); // 12
minSum([2, 7, 3, 0, 6, 1, -5, -12, -11], 3); // -28
function maxSum(arr, size) {
let max_value = -Infinity;
if (size > arr.length) {
return null;
}
for (let i = 0; i <= arr.length - size; i++) {
let attempt = 0;
for (let j = i; j < i + size; j++) {
attempt += arr[j];
}
if (attempt > max_value) {
max_value = attempt;
}
}
console.log(max_value);
return max_value;
}
function minSum(arr, size) {
let min_value = Infinity;
if (size > arr.length) {
return null;
}
for (let i = 0; i <= arr.length - size; i++) {
let attempt = 0;
for (let j = i; j < i + size; j++) {
attempt += arr[j];
}
if (attempt < min_value) {
min_value = attempt;
}
}
console.log(min_value);
return min_value;
}
```
### Improve Maxsum
- O(2n) -> O(n)
- Coding Practice
```=javascript
maxSum([2, 7, 3, 7, 25, 0, 6, 1, -5, 12, -11], 3); // 12
function maxSum(arr, size) {
if (size > arr.length) {
return null;
}
let maxValue = 0;
for (let i = 0; i < size; i++) {
maxValue += arr[i];
}
let temValue = maxValue;
for (let j = size; j < arr.length; j++) {
temValue = temValue + arr[j] - arr[j - size];
if (temValue > maxValue) {
maxValue = temValue;
}
}
console.log(maxValue);
return maxValue;
}
```
### Min Sub Array
![](https://i.imgur.com/I7Tvb2q.jpg =400x)
- O(Log n)
- Coding Practice
```=javascript
function minSubArray(arr, sum) {
let start = 0;
let end = 0;
let total = 0;
let minLength = Infinity;
while (start < arr.length) {
if (total < sum && end < arr.length) {
total += arr[end];
end++;
} else if (total >= sum) {
let currentLength = end - start;
if (minLength > currentLength) {
minLength = currentLength;
}
total -= arr[start];
start++;
} else if (end >= arr.length) {
break;
}
}
if (minLength === Infinity) {
console.log("Cannot find subarray that can sum up to the given number");
return 0;
} else {
console.log(minLength);
return minLength;
}
}
minSubArray([8, 1, 6, 15, 3, 16, 5, 7, 14, 30, 12], 70);
```
### Unique Letter Substring
- O(Log n)
- Coding Practice
```=javascript
function uniqueLetterString(str) {
let start = 0;
let end = 0;
let counter = {};
let maxLength = -Infinity;
while (end < str.length) {
console.log(counter);
if (counter[str[end]]) {
counter[str[start]]--;
start++;
} else {
counter[str[end]] = 1;
end++;
if (end - start > maxLength) {
maxLength = end - start;
}
}
}
if (maxLength == -Infinity) {
console.log("Cannot find unique letters substring.");
return null;
}
console.log(maxLength);
return maxLength;
}
uniqueLetterString(""); // 6
```
### Largest Product
- O(NLog n)
- Coding Practice
```=javascript
let thousandDigits =
[7, 3, 1, 6, 7, 1, 7, 6, 5, 3, 1, 3, 3, 0, 6, 2, 4, 9, 1, 9, 2, 2, 5, 1, 1,
9, 6, 7, 4, 4, 2, 6, 5, 7, 4, 7, 4, 2, 3, 5, 2, 4, 9, 1, 9, 2, 2, 5, 1, 1];
function largestProduct(n) {
let currentProduct;
let maxProduct = -Infinity;
let left = 0;
let right = n - 1;
while (right < thousandDigits.length) {
currentProduct = 1;
for (let i = left; i <= right; i++) {
currentProduct *= thousandDigits[i];
}
if (currentProduct > maxProduct) {
maxProduct = currentProduct;
}
right++;
left++;
}
console.log(maxProduct);
return maxProduct;
}
largestProduct(13);
```
### Recursion 遞迴
用在處理stack資料結構
- Ox
- Coding Practice
```=javascript
```
### Fibonacci Sequence
- O(n)
- Coding Practice
```=javascript
function fs(n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fs(n - 1) + fs(n - 2);
}
}
for (let i = 0; i < 15; i++) {
console.log(fs(i));
}
```
### Array of Arrays
- O(n)
- Coding Practice
```=javascript
let arrs = [[[["a", [["b", ["c"]], ["d"]]], [["e"]], [[["f", "g", "h"]]]]]];
function collector(arr1) {
let result = [];
helper(arr1);
return result;
function helper(arr2) {
for (let i = 0; i < arr2.length; i++) {
if (Array.isArray(arr2[i])) {
helper(arr2[i]);
} else {
result.push(arr2[i]);
}
}
}
}
console.log(collector(arrs));
```
## Merge Sort, Quick Sort
使用原則:根據輸入的數據大小,選擇合適的演算法。
### Merge Sort
> 使用情境:數據量小,且記憶體空間足夠的話使用(用空間換時間)
- Time complexity:
best case: O(nlogn)
average case: O(nlogn)
worst case: O(nlogn)
space complexity:O(n)
### Quick Sort
> 使用情境:數據量大時使用;
但如果遇到幾乎已排序的輸入數據,時間複雜度會衝到:O(N²),這時候我選擇用Quick Sort搭配 Insertion sort 以降低時間複雜度
- Time complexity:
best case: O(nlogn)
average case: O(nlogn)
worst case: O(N²)
space complexity:in place sort(原地排序),不需額外的記憶體空間
## Sorting Algorithms I
最慢的三個, Average Performance O(n^2)
### Bubble Sort
- compares adjacent element and swap if they are in the wrong order
- 最簡單的演算法,現實生活中幾乎是沒有被應用
![](https://i.imgur.com/Vra8SSY.jpg =400x)
```=javascript
function bubbleSort(arr: number[]) {
let step = 0;
for (let i: number = 0; i <= arr.length - 2; i++) {
for (let j: number = arr.length - 1; j >= i + 1; j--) {
if (arr[j] < arr[j - 1]) {
// swap arr[j] and arr[j - 1]
let temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
step++;
}
}
}
console.log("It takes " + step + " steps to complete.");
console.log(arr);
}
let test = [];
for (let i = 0; i < 100; i++) {
test.push(Math.floor(Math.random() * 100));
}
bubbleSort(test);
```
### Insertion Sort
- 比bubble sort有效一點,但Big O一樣是 O(n^2)
- Keeping inserting a new value into a sorted array
![](https://i.imgur.com/wOWk0zc.jpg =500x)
```=javascript
let unsorted = [14, -4, 17, 6, 22, 1, -5];
insertionSort(unsorted);
function insertionSort(arr) {
for (let j = 1; j <= arr.length - 1; j++) {
let key = arr[j];
i = j - 1;
while (i >= 0 && arr[i] > key) {
arr[i + 1] = arr[i];
i -= 1;
}
arr[i + 1] = key;
}
console.log(arr);
return arr;
}
```
### Selection Sort
- select the smallest value in unsorted array, and then swap it with the left most value in this unsorted array
![](https://i.imgur.com/Iw7XCNS.png =500x)
```=javascript
let unsorted = [14, -4, 17, 6, 22, 1, -5];
selectionSort(unsorted);
function selectionSort(arr) {
for (let i = 0; i <= arr.length - 2; i++) {
let minIndex = i;
for (let j = i; j <= arr.length - 1; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// swap arr[minIndex] and arr[i]
let temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
console.log(arr);
}
console.log(arr);
return arr;
}
```
## Sorting Algorithms II
Performance O(logn)
### Merge Sort
是一種基於分治法的排序算法。它的基本思想是:
- 原理
1. 將數組不斷地對半分割,直到每個子數組只剩一個元素。
2. 將兩個有序的子數組合併成一個有序的數組。
3. 重複這個過程,直到所有子數組合併成一個有序數組。
- 步驟
1. 分割:將數組從中間劃分為兩部分。
2. 遞歸排序:對每一部分分別遞歸進行 Merge Sort。
3. 合併:將兩個有序的子數組合併成一個有序的數組。
- combining two sorting arrays
- a classic example of "divde and conquer"
- 用的記憶體比較多,因為會宣告新的array
```=ts
function mergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
// 分割數組
const mid = Math.floor(arr.length / 2);
const left = arr.slice(0, mid);
const right = arr.slice(mid);
// 遞歸排序
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
// 合併兩個有序數組
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
// 合併剩餘的元素
return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}
// 測試 mergeSort
let arr = [3, 6, 8, 10, 1, 2, 1];
console.log(mergeSort(arr)); // [1, 1, 2, 3, 6, 8, 10]
```
例子
假設有一個數組 [3, 6, 8, 10, 1, 2, 1],我們進行 Merge Sort 的過程如下:
1. 分割:將數組從中間劃分為兩部分:
- 左邊部分:[3, 6, 8]
- 右邊部分:[10, 1, 2, 1]
2. 遞歸分割:
- 將左邊部分 [3, 6, 8] 再次分割為 [3] 和 [6, 8]:
[6, 8] 再次分割為 [6] 和 [8]。
- 將右邊部分 [10, 1, 2, 1] 再次分割為 [10, 1] 和 [2, 1]:
[10, 1] 再次分割為 [10] 和 [1]。
[2, 1] 再次分割為 [2] 和 [1]。
3. 合併:
- 合併 [3] 和 [6, 8],得到 [3, 6, 8]。
- 合併 [10] 和 [1],得到 [1, 10]。
- 合併 [2] 和 [1],得到 [1, 2]。
- 合併 [1, 10] 和 [1, 2],得到 [1, 1, 2, 10]。
- 最後合併 [3, 6, 8] 和 [1, 1, 2, 10],得到 [1, 1, 2, 3, 6, 8, 10]。
![](https://i.imgur.com/iD633vo.png)
complexity = n * log2n
Big O = O(nlogn)
```=javascript
// a1 and a2 is sorted array
function merge(a1, a2) {
let result = [];
let i = 0;
let j = 0;
while (i < a1.length && j < a2.length) {
if (a1[i] > a2[j]) {
result.push(a2[j]);
j++;
} else {
result.push(a1[i]);
i++;
}
}
// a1 or a2 might have some elements left
// use loop to put them all into result
while (i < a1.length) {
result.push(a1[i]);
i++;
}
while (j < a2.length) {
result.push(a2[j]);
j++;
}
return result;
}
// Math.floor()最大整數
function mergeSort(arr) {
if (arr.length === 1) {
return arr;
} else {
let middle = Math.floor(arr.length / 2);
let left = arr.slice(0, middle);
let right = arr.slice(middle, arr.length);
return merge(mergeSort(right), mergeSort(left));
}
}
console.log(mergeSort([15, 3, 17, 18, 35, 11, 0, 36, -336, 1054]));
```
### Tree
- abstract data type
- node 指圈圈
- edge 指箭頭
- a tree should only have only one root
- tree is acyclic "graph"
![](https://i.imgur.com/FFHPXyK.png =400x)
#### Binary Tree 二元樹
each node has at most two children, which are referred to as the left child and right child
#### Complete Binary Tree
"almost-full" binary tree, the right most nodes of the bottom might be missing. 右邊不是完整的
#### Full Binary Tree
左右深度(長度)要一樣
a binary tree in which all leaf nodes have the same depth
#### Max Heap
a complete binary tree where the largest node is always the at root for any sub-trees
- 一定是complete binary tree
- any subtree, the root is always the biggest
- 任何一個subtree, root 都是最大的
![](https://i.imgur.com/uMP7tLF.jpg =400x)
![](https://i.imgur.com/NMU85gW.png =400x)
## Heap Sort
- uses ==Max Heap== to sort
- 用max heap做heap sort
- heap sort是用root和最後一個值互換,然後把root的值拿出來到新的array, 最後一個到root位置,一路比大小,重複動作
![](https://i.imgur.com/yyfCMvq.jpg =400x)
實做heap sort前提是個binary tree的arr,然後三步驟完成heap sort
1. build max heap
2. heap sort
3. max heapify
```=javascript
let heapSize;
let arr = [15, 3, 17, 18, 20, 2, 1, 666];
heapSort();
console.log(arr);
function buildMaxHeap() {
heapSize = arr.length - 1;
for (let i = Math.floor(heapSize / 2); i >= 0; i--) {
maxHeapify(i);
}
}
function maxHeapify(i) {
let largest;
let l = i * 2 + 1;
let r = i * 2 + 2;
if (l <= heapSize && arr[l] > arr[i]) {
largest = l;
} else {
largest = i;
}
if (r <= heapSize && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
// swap A[i] with A[largest]
let temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
maxHeapify(largest);
}
}
function heapSort() {
buildMaxHeap();
for (let i = arr.length - 1; i >= 0; i--) {
// exchange A[0] with A[i]
let temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapSize -= 1;
maxHeapify(0);
}
return arr;
}
```
### QuickSort
- 原理
Quick Sort 是一種基於分治法的排序算法。它的基本思想是:
1. 選擇一個「pivot」(樞軸)元素。
2. 將數組分成兩個部分:小於 pivot 的元素和大於 pivot 的元素。
3. 對這兩部分遞歸地進行 Quick Sort。
- 步驟
1. 選擇 pivot:通常選擇數組的第一個元素、中間元素或最後一個元素作為 pivot。
2. 分區:重新排序數組,使得所有小於 pivot 的元素都在 pivot 的左邊,所有大於 pivot 的元素都在 pivot 的右邊。這個過程稱為「分區」。
3. 遞歸排序:對 pivot 左邊和右邊的子數組分別遞歸進行 Quick Sort。
```=ts
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
// 選擇 pivot
let pivot = arr[Math.floor(arr.length / 2)];
let left = [];
let right = [];
// 分區
for (let i = 0; i < arr.length; i++) {
if (i === Math.floor(arr.length / 2)) continue;
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 遞歸排序並合併結果
return [...quickSort(left), pivot, ...quickSort(right)];
}
// 測試 quickSort
let arr = [3, 6, 8, 10, 1, 2, 1];
console.log(quickSort(arr)); // [1, 1, 2, 3, 6, 8, 10]
```
例子
假設有一個數組 [3, 6, 8, 10, 1, 2, 1],我們進行 Quick Sort 的過程如下:
1. 選擇 pivot:例如選擇中間的 8。
2. 分區:將數組分成兩部分:
- 左邊部分:[3, 6, 1, 2, 1]
- 右邊部分:[10]
3. 遞歸排序左邊部分和右邊部分:
- 左邊部分的 Quick Sort 結果:[1, 1, 2, 3, 6]
- 右邊部分:[10] 不需要排序
4. 合併結果:[1, 1, 2, 3, 6, 8, 10]
## LeedCode 刷題
### String 字串
- 反轉字串
```=ts
// st = 'olleh'
function rs ( str: string) {
let res: string = '';
let arr: string[] = str.split('');
for(let i: number = arr.length - 1; i >= 0; i--) {
res = res + arr[i];
console.log(`result: ${res}`);
}
return res; //hello
}
rs('olleh');
```
- 檢查同義字 Valid Anagram
```=ts
function isAnagram( str1: string, str2: string): boolean {
if(str1.length !== str2.length) return false;
const s = str1.split('').sort().join();
const t = str2.split('').sort().join();
return s === t;
}
const result = isAnagram('anagram','nagaram');
console.log(`result: ${result}`);
```
### Binary Tree 二元樹
- Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
找出一個二元樹的深度
```=ts
var maxDepth = function(root) {
return find(root);
// 遞迴函式
function find(node){
// 節點到底
if(node === null){
return 0;
}
var deepL = 1;
var deepR = 1;
// 有左節點,往下一層找
if(node.left !== null){
deepL += find(node.left)
}
// 有右節點,往下一層找
if(node.right !== null){
deepR += find(node.right)
}
// 回傳較大的深度depth,給上一層節點
return deepL > deepR ? deepL: deepR;
}
};
```
### Valid Parentheses 問題
問題描述
給定一個只包括 '(',')','{','}','[' 和 ']' 的字符串 s,判斷字符串是否有效。
有效字符串需滿足:
1. 左括號必須用相同類型的右括號閉合。
2. 左括號必須以正確的順序閉合。
- 解題思路
這道題目可以使用 stack 結構來解決。
當遇到左括號時,將其推入堆棧;
當遇到右括號時,檢查堆棧頂部的左括號是否與其匹配。
如果匹配,彈出堆棧頂部的左括號;
如果不匹配或堆棧為空,則字符串無效。
最後,檢查堆棧是否為空,如果為空,則字符串有效。
- 步驟
1. 初始化一個空的堆棧 stack。
2. 遍歷字符串 s 中的每個字符 char:
- 如果 char 是左括號 (, {, [,則將其推入堆棧。
- 如果 char 是右括號 ), }, ],檢查堆棧頂部的元素:
- 如果堆棧為空,返回 false。
- 如果堆棧頂部元素與當前右括號匹配,彈出堆棧頂部元素。
- 如果不匹配,返回 false。
3. 遍歷完畢後,檢查堆棧是否為空。如果為空,返回 true;否則,返回 false。
```=ts
function isValid(s) {
const stack = [];
const map = {
'(': ')',
'{': '}',
'[': ']'
};
for (let i = 0; i < s.length; i++) {
const char = s[i];
if (map[char]) {
// char 是左括號
stack.push(char);
} else {
// char 是右括號
const top = stack.pop();
if (map[top] !== char) {
return false;
}
}
}
return stack.length === 0;
}
// 測試 isValid
console.log(isValid("()")); // true
console.log(isValid("()[]{}")); // true
console.log(isValid("(]")); // false
console.log(isValid("([)]")); // false
console.log(isValid("{[]}")); // true
```
- 適用資料結構
1. Stack:用於存儲左括號,幫助匹配相應的右括號。
2. Hash Table:用於存儲左括號與右括號的映射關係,方便檢查匹配。
### Merge Intervals
```=ts
function merge(intervals: number[][]): number[][] {
if (intervals.length === 0) return [];
// 排序區間,按起始點排序
intervals.sort((a, b) => a[0] - b[0]);
const merged: number[][] = [];
let current: number[] = intervals[0];
for (let i = 1; i < intervals.length; i++) {
const interval = intervals[i];
if (interval[0] <= current[1]) {
// 如果有重疊,合併區間
current[1] = Math.max(current[1], interval[1]);
} else {
// 如果沒有重疊,將 current 區間推入結果
merged.push(current);
current = interval;
}
}
// 推入最後一個區間
merged.push(current);
return merged;
}
```
1. 排序:
- 我們對 intervals 按起始點進行排序,以便我們可以按順序處理區間。
2. 遍歷區間:
- 使用變量 current 來追蹤當前的合併區間。
- 如果新的區間與 current 區間重疊(即新的區間的起始點小於等於 current 的結束點),我們合併它們。
- 如果新的區間不重疊,我們將 current 區間加入結果,並更新 current 為新的區間。
3. 處理最後一個區間:遍歷完成後,將最後一個 current 區間加入結果列表。
- 應用場景
時間區間管理:合併重疊的時間區間,特別是在日曆應用程序中。
資源分配:處理重疊的資源使用情況,例如處理多個工作者的排班表。
###### tags: `study`