# 1. Arrays & Hashing (9) - Python
> 紀錄 NeetCode-150 與 LeetCode 的解題想法、刷題過程與錯誤,以 Python 或 C++ 兩種語言為主
>
> 其他 NeetCode-150 或 LeetCode 的主題請見 [Here](https://hackmd.io/-ovTFlzLRWaSgzDGhbSwsA)
<!-- 常用色碼紀錄
Easy: <font color="#00ad5f">**Easy**</font>
Medium: <font color="#ffbb00">**Medium**</font>
Hard: <font color="#ee2f56">**Hard**</font>
-->
* Hash
* 廣義概念,將任意長度的輸入值轉換為固定長度的輸出值,此過程稱為 hashing
* 輸出後的數值又稱為 hash value(雜湊值)
* Hash function
* 進行 hashing 時,將輸入映射到固定長度輸出的函數。
* 通常在加密領域會用到
* 在 hash table 或 hash set 等資料結構中,用來計算元素儲存的位置
* Hash table
* 一種資料結構,以鍵值對(key-value pair)的形式儲存資料
* hash table 中的每個 key 都是唯一的
* 通過 hash function 將 key 映射到表格中的某個對應位置
* Python 中的 dictionary 就是 hash table 實作
* 用來實現高效率的查找與插入
* Hash set
* 一種根據 hash table 所建立的資料結構,用來儲存不重複的元素集合
* 不在意元素的順序與鍵值對的關係
* Python 中的 set 就是 hash set 的實作
## 1. Contains Duplicate
<font color="#00ad5f">**Easy**</font>
> Given an integer array `nums`, return `True` if any value appears **more than once** in the array, otherwise return `False`
### Example 1:
```java
Input: nums = [1, 2, 3, 3]
Output: True
```
### Example 2:
```java
Input: nums = [1, 2, 3, 4]
Output: False
```
### Recommended complexity
* Time complexity: $O(n)$
* where n is the size of the input array
* Space complexity: $O(n)$
### Solution
**(1) Brute force**
#### 1. Brute force
```python=
class Solution:
def hasDuplicate(self, nums: List[int]) -> bool:
for i in range(len(nums)-1):
for j in range(i+1, len(nums)):
if nums[i] == nums[j]:
return True
return False
```
#### 2. Sort
```python=
class Solution:
def hasDuplicate(self, nums: List[int]) -> bool:
nums.sort()
for i in range(len(nums)-1):
if nums[i] == nums[i+1]:
return True
return False
```
#### 3. Hash set
```python=
class Solution:
def hasDuplicate(self, nums: List[int]) -> bool:
hash_set = set()
for i in nums:
if i in hash_set:
return True
else:
hash_set.add(i)
return False
```
## 2. Is Anagram
<font color="#00ad5f">**Easy**</font>
> Given two strings `s` and `t`, return `True` if the two strings are anagrams of each other, otherwise return `False`
>
> An **anagram** is a string that contains the exact same characters as another string, but the order of the characters can be different.
### Example 1:
```java
Input: s = "racecar", t = "carrace"
Output: Ture
```
### Example 2:
```java
Input: s = "jar", t = "jam"
Output: False
```
### Constraints
* `s` and `t` consist of lowercase English letters.
###
### Solution
#### 1. Sorting
```python=
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
s = sorted(s)
t = sorted(t)
if len(s) != len (t):
return False
for i in range(len(s)):
if s[i] == t[i]:
continue
else:
return False
return True
```
#### 2. Hash table
```python=
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
dict_s = dict()
dict_t = dict()
for i in range(len(s)):
if s[i] in dict_s:
dict_s[s[i]] += 1
else:
dict_s[s[i]] = 0
if t[i] in dict_t:
dict_t[t[i]] += 1
else:
dict_t[t[i]] = 0
if dict_s == dict_t:
return True
else:
return False
```
## 3. Two Integer Sum
<font color="#00ad5f">**Easy**</font>
> Given an array of integers `nums` and an integer `target`, return the indices `i` and `j` such that `nums[i] + nums[j] == target` and `i != j`.
>
> You may assume that every input has exactly one pair of indices `i` and `j` that satisfy the condition.
>
> Return the answer with the smaller index first.
### Example 1:
```java
Input: nums = [3, 4, 5, 6], target = 7
Output: [0,1]
```
### Example 2:
```java
Input: nums = [5, 5], taeget = 10
Output: [0, 2]
```
### Example 3:
```java
Input: nums = [5, 5], target = 10
Output: [0, 1]
```
### Constraints
* `2 <= nums.length <= 1000`
* `-10,000,000 <= nums[i] <= 10,000,000`
* `-10,000,000 <= target <= 10,000,000`
### Solution
#### 1. Brute force
```python=
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
```
## 4. Encode and Decode Strings
<font color="#ffbb00">**Medium**</font>
> Design an algorithm to encode a list of strings to a single string. The encoded string is then decoded back to the original list of strings.
>
> Please implement `encode` and `decode`
### Example 1:
```java
Input: ["neet","code","love","you"]
Output:["neet","code","love","you"]
```
### Example 2:
```java
Input: ["we","say",":","yes"]
Output: ["we","say",":","yes"]
```
### Constraints
* `0 <= strs.length < 100`
* `0 <= strs[i].length < 200`
* `strs[i]` contains only UTF-8 characters.
以下程式碼**測資成功但提交失敗**。因為 while 迴圈中儲存每個字串的長度時,只儲存第一個數字(`num = int(s[i])`),如果某個字串有 9 個字元以上就會出錯,例如:
### Solution
#### 1. My (False)
* `["neet","code","love","you"]` 編碼後為 `"4#neet4#code4#love4#you"`,可以正確解碼
* `["ThisIsProblem", "ha"]` 編碼後為 `13#ThisIsProblem2#ha` 無法正確解碼,因為第一個字串的長度只會讀取到 `1` 而不是 `13`
```python=
class Solution:
def encode(self, strs: List[str]) -> str:
encode = ""
for s in strs:
encode += f"{len(s)}#{s}"
return encode
def decode(self, s: str) -> List[str]:
decode = []
i = 0
while (i < len(s)):
num = int(s[i]) # problem is here !!
decode.append(s[i+2:i+2+num])
i += (2+num)
return decode
```
### 2. Correct version
```python=
class Solution:
def encode(self, strs: List[str]) -> str:
encode = ""
for s in strs:
encode += f"{len(s)}#{s}"
return encode
def decode(self, s: str) -> List[str]:
decode = []
i = 0
while (i < len(s)):
j = i
while (s[j] != "#"):
j += 1
length = int(s[i:j])
i = j + 1
decode.append(s[i:i+length])
i = i + length
return decode
```
## 5. Product of Array Except Self
<font color="#ffbb00">**Medium**</font>
> Given an integer array `nums`m return an array `output` where `output[i]` is the product of all the elements of `nums` except `nums[i]`.
>
> Each product is **guaranteed** to fit in a **32-bit** integer.
>
> Follow-up: Could you solve it in $O(n)$ time without using the division operation ?
### Example 1:
```java
Input: nums = [1, 2, 4, 6]
Output: [48, 24, 12, 8]
```
### Example 2:
```java
Input: nums = [-1, 0, 1, 2, 3]
Output: [0, -6, 0, 0, 0]
```
### Constraints
* `2 <= nums.length <= 1000`
* `20 <= nums[i] <= 20`
### Solution
#### 1. My (Division ver.)
* 有使用除法進行運算
* 時間複雜度為: $O(n^2)$
```python=
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
product = 1
for item in nums:
product *= item
output = []
for i in range(len(nums)):
if nums[i] != 0:
res = product // nums[i]
output.append(res)
else:
res = 1
for j in range(len(nums)):
if j == i:
pass
else:
res *= nums[j]
output.append(res)
return output
```
#### 2. Correct (Non-division ver.)
* 注意迴圈不能針對串列的元素迭代,應該要迭代 `range()`,否則如果串列有相同元素就會出錯
* 時間複雜度為: $O(n^2)$
```python=
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
res = []
for i in range(len(nums)):
product = 1
for j in range(len(nums)):
if j == i:
pass
else:
product *= nums[j]
res.append(product)
return res
```
#### 3. Correct (Prefix & postfix)
* 時間複雜度 $O(n)$
* 空間複雜度 $O(n)$,因為使用兩個 array 來儲存 prefix 與 postfix
```python=
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
pre = [1] * len(nums)
post = [1] * len(nums)
for i in range(len(nums)):
if i == 0:
pre[i] = nums[i]
post[-1-i] = nums[-1-i]
else:
pre[i] = pre[i-1] * nums[i]
post[-1-i] = post[-i] * nums[-1-i]
res = []
for i in range(len(nums)):
if i == 0:
res.append(post[i+1])
elif i == (len(nums)-1):
res.append(pre[i-1])
else:
res.append(pre[i-1] * post[i+1])
return res
```
* 時間複雜度 $O(n)$
* 空間複雜度 $O(n)$
```pytohn=
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
left_default = right_default = 1
n = len(nums)
res = [1] * n
# prefix produt
for i in range(1, n):
res[i] = res[i] * nums[i-1] * left_default
left_default *= nums[i-1]
# postfix product
for i in range(1, n):
res[-1-i] = res[-1-i] * nums[-i] * right_default
right_default *= nums[-i]
return res
```
## 6. Valid Sudoku
<font color="#ffbb00">**Medium**</font>
> You are given a `9 x 9` Sudoku board `board`. A Sudoku board is valid of the following rules are followed:
> 1. Each row must contain the digits `1-9` without duplicates.
> 2. Each column must contain the digits `1-9` without duplicates.
> 3. Each of the nine `3x3` sub-boxes of the grid must contain the digits `1-9` without duplicates.
>
> Return `True` if the Sudoku board is valid, otherwise return `False`
> Note: A board does not need to be full or be solvable to be valid.
### Example 1:

```java
Input: board =
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","8",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: true
```
### Example 2:
```java
Input: board =
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","1",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: false
```
### Constraints
* `board.length == 9`
* `board[i].length == 9`
* `board[i][j]` is a digit `1-9` or `"."`.
### Solution
#### 1. Brute force
```python=
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# Brute force solution
# search row
for row in range(9):
numbers = set()
for idx in range(9):
if board[row][idx] == ".":
continue
if board[row][idx] in numbers:
return False
else:
numbers.add(board[row][idx])
# search column
for col in range(9):
numbers = set()
for idx in range(9):
if board[idx][col] == ".":
continue
if board[idx][col] in numbers:
return False
else:
numbers.add(board[idx][col])
# search sub-boxes
for box in range(9):
numbers = set()
for i in range(3):
for j in range(3):
row = (box // 3) * 3 + i
col = (box % 3) * 3 + j
if board[row][col] == ".":
continue
if board[row][col] in numbers:
return False
else:
numbers.add(board[row][col])
return True
```
#### 2. Hash set
```python=
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# Hash set
rows = defaultdict(set)
cols = defaultdict(set)
squares = defaultdict(set)
for r in range(9):
for c in range(9):
if board[r][c] == ".":
continue
if ((board[r][c] in rows[r]) or \
(board[r][c] in cols[c]) or \
(board[r][c] in squares[(r//3, c//3)])):
return False
else:
rows[r].add(board[r][c])
cols[c].add(board[r][c])
squares[(r//3, c//3)].add(board[r][c])
return True
```
## . Longest Conseccutive Sequence
<font color="#ffbb00">**Medium**</font>
> Given an array of integers `nums`, return the length of the longest consecutive seauence of elements.
>
> A consecutive sequence is a sequence of elements in which each element is exactly `1` greater than the previous element.
>
> You must write an algorithm that runs in $O(n)$ time.
### Example 1:
```java
Input: nums = [2, 20, 4, 10, 3, 4, 5]
Output: 4
```
The longest consecutive sequence is `[2, 3, 4, 5]`
### Example 2:
```java
Input: nums = [0, 3, 2, 5, 4, 6, 1, 1]
Output: 7
```
### Constraints
* `0 <= nums.length <= 1000`
* `-10^9 <= nums[i] <= 10^9`
### Solution
#### 1. My version
結合了 sorted 與 hash set 的概念
```python=
# My version
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
if not nums:
return 0
HashSet = set(nums)
HashSet_sort = sorted(HashSet)
length = []
count = 1
for i in range(len(HashSet_sort)-1):
if list(HashSet_sort)[i+1] - list(HashSet_sort)[i] == 1:
count += 1
else:
length.append(count)
count = 1
length.append(count)
return max(length)
```
##### 2.