---
# System prepended metadata

title: algorithm & data structure (JS)

---

# 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 遞回