# algorithm & data structure (JS)
## Big O Analysis
best ----> worst
1 < logn < sqrt(n) < nlogn < n^(2) < n^(3) ... < 2^(n) < 3^(n) ... < n^(n)
### Objects
Insertion -> O(1)
Removal -> O(1)
Searching -> O(n) (視乎object大小)
Accessing -> O(1) 每個 obj 跟 arr 的所需時間是一樣的,因為是 **Hash Tables** 形態的原因,這會在後續補充解釋。
### Array
Inservation
- push(從尾巴新增) -> O(1)
- unshift(從開始位置新增) -> O(n) 因為會動到 index number
```
let n=5
let arr = [1,2,3,4]
for (let i=0; i<n; i++) {
arr.push(10)
}
// 此時 big O 為 O(n)
for (let i=0; i<n; i++) {
arr.unshift(10)
}
// 此時 big O 為 O(n^(2)),即便看起來都是只有一個 for loop,但 arr.unshift 已有一個 O(n),因此會是 n^(2)
```
Removal
- pop(從尾刪) -> O(1)
- shift(從頭刪) -> O(n)
Searching -> O(n)
Accessing -> O(1)
## Algorithm Design
### 1. Linear Search (Sequential Search)
兩個條件停止搜尋
1)until a match is found
2)whole list has been searched
```
let arr1 = [1,3,5....,n]
function linear(arr, n) {
for(let i=0; i <= arr.length; i++) {
if(arr[i] === n) return `${n} at index ${i} of array `
}
return 'Not found'
}
linear(arr1, n)
```
Performance:
Worst Case -> O(n) *最大值*
Best Case -> O(1) *array第一筆*
average performance -> O(n/2)
### 2. Binary Search
Bianary 又是二進制的意思
只能在 sorted array 下運用,一直往中間數切
```
function binarySearch(arr, num) {
let min = 0
let max = arr.length-1
while(min <= max) {
let middle = Math.floor((max+min)/2)
if (num > arr[middle]) {
min = middle+1
} else if (num < arr[middle]) {
max = middle-1
} else {
console.log(`find number ${num} at position ${middle}, totel step: ${step}`)
return middle
}
}
return -1
}
```
Performance:
Worst Case -> O(logn) **<- form f(n)=log₂n*
Best Case -> O(1) *sored array的正中央*
average performance -> O(logn)
### 3. Counter
常見的技巧 (general skill)
A counter object will reduce the complexity of algorithms
**Intersection 找出重複值**
Without Counter -> O(n^2) because of double for loop
```
// O(n^2)
function intersectionWithoutCounter(array1, array2, finalArray) {
for(let i=0; i<array2.length; i++) {
for(let j=0; j<array1.length; j++) {
if(array1[j] == array2[i]) {
finalArray.push(array1[j])
}
}
}
console.log(finalArray.sort((a, b) => {return a-b}))
}
```
With Counter -> O(n)
計算每個值出現的次數,將次數大於 1 的印出
```
function intersectionWithCounter(array1, array2) {
let arr3 = array1.concat(array2)
let count = {}
for(let i=0; i<arr3.length; i++) {
if(!count[arr3[i]]) {
count[arr3[i]] = 1
} else {
count[arr3[i]] ++
}
}
for(key in count) {
if(count[key] > 1) {
console.log(key)
}
}
}
```
### 4. Pointer
有不同的稱呼名字但idea一樣,用於 reduce the complexity of algorithms
例子 averagePair(arr, num),num是平均值,將 array 的值從頭到尾相加一次,若兩個數值相加除2等於平均值,記錄兩個數值
e.g. averagePair([-11,0,1,2,3,14,88],1.5) -> -11跟14、0跟3、1跟2
```
function averagePair(arr, num) {
let result = []
for(let i=0; i<arr.length-1; i++) {
for(let j= i+1; j<arr.length; j++) {
if((arr[i] + arr[j])/2 == num) {
result.push(arr[i]}","arr[j])
}
}
}
console.log(result)
}
```
以上是沒使用 Pointer,使用兩個 for loop,因此為 O(n^2),接下來使用 Pointer
```
function averagePairWithPointer(arr, num) {
let result = []
let left = 0;
let right = arr.length-1
while(right > left) {
let average = (arr[left]+arr[right])/2
console.log(`left: ${arr[left]}, right: ${arr[right]}, average: ${average}`)
if(average > num) {
right--
} else if(average < num) {
left++
} else if(average == num) {
result.push(arr[left]+","+arr[right])
right--
left++
}
}
console.log(result)
}
```
其運算方式為,比對Sorted Array 最前值 && 最後值相加並除以 2,若大於平均值,最後值往前移一個;若小於則最前值往後移一個,最終兩個數經過推算會碰到,While loop就結束~
Pointer practice: palindrome(string的正順序及反順序是否一致)
```
function isPalindrome(str) {
let left=0
let right=str.length-1
while(right > left) {
if(str[left] !== str[right]) return false
// console.log(`left: ${str[left]}, right: ${str[right]}`)
left++
right--
}
return true
}
```
Pointer Practice2: subsequence problem,比對兩個 Array,分別是 Main Array跟 Sub Array,按著順序由左到右看,只要 Main Array 有出現在 Sub Array 過,即便中間有其他字,都一律接受
例子:`// ("bye","byze") 跟 ("frank","frjanlk")` 都會回傳 true,前者跳過"y"最終也會組成"bye",後者跳過中間的"j"跟"l",最終也會組成"frank",但若是 `("abc","cba")`,就不一樣囉,因為按順序來說它是"cba",而非"abc",因此回傳 false。
```
function isSubsequence(mainStr, subStr) {
if (mainStr.length === 0) return true
let arr = mainStr.split("")
let cArray = [] //comparisonArray
let main=0
let sub=0
while(sub < subStr.length) {
console.log(`mainStr: ${mainStr[main]}, subStr: ${subStr[sub]}`)
if(mainStr[main] === subStr[sub]) {
cArray.push(mainStr[main])
main++
sub++
} else {
sub++
}
}
if (cArray.length !== arr.length) return false
if (JSON.stringify(cArray) !== JSON.stringify(arr)) return false
return true
}
```
上述是一開始自己的做法,但仔細查看會發現,其實不用等全部跑完再用 stringify 的方式做比對,以下是更簡潔的方式:
```
function isSubsequenceUdemy(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-1) return true
pointer2++
}
return false
}
```
只要滿足 pointer1 的長度(length) 等於 str1 的長度,就可以 return true,某則一律 false
### 5. Sliding Window
滑行的視窗
### 6. Recursion 遞回