# LC 454, 15, 18 Hash table 2
---
Second part of hash table.
## LC 454. 4Sum II
[第454题.四数相加II](https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html#)
___
### Logic
The logic is about the same as the "3 sum", "2 sum" problems. What need to be done is just record the current sum in a `dict`, and upon the time the complement to the target, 0, is found in the `dict`, we can imncrease the accumulator.
```python
def fourSumCount(
self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]
) -> int:
dict1 = defaultdict(int)
count = 0
for i in nums1:
for j in nums2:
dict1[i + j] += 1
for i in nums3:
for j in nums4:
key = 0 - i - j
count += dict1[key]
return count
```
## LC 15. 3Sum
---
[第15题. 三数之和](https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html)
### Naive solution - brute force
#### Logic
+ Literally check every triplets in three `for` loop, which result in a time complexity of $O(n^3)$.
+ We can fix the first number for the second `for` loop and fix the first pair of sum for the third `for` loop, which helps a little.
+ There are more things to add, for example, one could skip some pairs which are seen before. I only added the `tup` set for **removing duplicate**.
```python
def threeSum(self, nums: List[int]) -> List[List[int]]:
# naive
nums.sort()
ret = []
tup = set()
for i in range(len(nums)-2):
diff = 0 - nums[i]
for j in range(i+1, len(nums)-1):
diff2 = diff - nums[j]
for k in range(j+1, len(nums)):
if diff2 == nums[k]:
temp = tuple((nums[i], nums[j], nums[k]))
if temp not in tup:
ret.append([nums[i], nums[j],nums[k]])
tup.add(temp)
return ret
```
### Dict
+ The source code could be found here: [第15题. 三数之和](https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html).
### Duo Ptrs
#### Logic
To use duo-pointers to solve this problem, one could think of each pairs as a prefix sum, then linearly search for the complement of the target.
```python
def threeSum2(self, nums: List[int]) -> List[List[int]]:
nums.sort()
ret = set()
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i - 1]:
continue
j = i + 1
k = len(nums) - 1
while j < k:
s = nums[j] + nums[k]
if s + nums[i] < 0:
j += 1
elif s + nums[i] > 0:
k -= 1
else:
ret.add((nums[i], nums[j], nums[k]))
j += 1
k -= 1
while j < k and nums[j] == nums[j - 1]:
j += 1
while j < k and nums[k] == nums[k + 1]:
k -= 1
return [list(i) for i in ret]
```
## LC 18 4Sum
___
[第18题. 四数之和](https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html)
### Dict method
#### Logic
+ Using the `dict`, we could save the sum so far for each pair of nums, then check for other pairs if their complement are already in the dict.
+ To reduce some of the duplicates, we limit the maximum occurance of an item to a maximum of 4, because we are doing a "4Sum", and seeing 5 same "2" won't change the result as 4 "2".
```python
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
dict1 = defaultdict(list)
visited = set()
ct = Counter(nums)
for key in ct:
ct[key] = min(4, ct[key])
nums = sorted(ct.elements())
for i in range(len(nums) - 1):
for j in range(i + 1, len(nums)):
key = nums[i] + nums[j]
dict1[key].append([i, j])
if target - key in dict1:
for pair in dict1[target - key]:
x, y = pair
keyLst = [nums[i], nums[j], nums[x], nums[y]]
keyLst = tuple(keyLst)
if i not in pair and j not in pair and keyLst not in visited:
visited.add(keyLst)
return list(visited)
```
### Duo ptrs
#### Logic
+ Like other sum problems, we could fix the sum of the first two numbers, then using two pointers to find the last two numbers which reduce the brute force **runtime** from $O(n^4)$ to $O(n^3)$.
```python
def fourSum2(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
ret = []
for i in range(len(nums) - 1):
if nums[i] > target and nums[i] >= 0:
break
if i > 0 and nums[i - 1] == nums[i]:
continue
for j in range(i + 1, len(nums)):
fixed_sum = nums[i] + nums[j]
if fixed_sum > target and fixed_sum >= 0:
break
if j > i + 1 and nums[j] == nums[j - 1]:
continue
left = j + 1
right = len(nums) - 1
while left < right:
if fixed_sum + nums[left] + nums[right] > target:
right -= 1
elif fixed_sum + nums[left] + nums[right] < target:
left += 1
else:
ret.append([nums[i], nums[j], nums[left], nums[right]])
right -= 1
left += 1
return ret
```
## Summary:
___
+ When dealing with the "3Sum", "4Sum" problems, we may consider using duo-pointers rather than the hash table to reduce a bit of the **runtime**.
+ Meanwhile, we need to figure out when it is best to reduce the duplicates and skip some condition that we have already faced.
+ Total time: About 3 hrs.