引子
看了 Advent of Code 要開始的推文一直猶豫要不要跳進來;因為往年年末無論是自己上 Qiita 開坑,或是鐵人賽到最後不是失敗就是都很累。
不過因為最後東看西看,最近也有在做 LeetCode POTD ,雖然晚了一天還是決定來做了
題目
題目形式
分成兩個部分
- Part 1 - 算出星星之間的距離總和
- Part 2 - 算出相似值
資料整形
藉由觀察,資料源以 \n
分行,並用 三個空格
分隔左右半邊
3 4
4 3
2 5
1 3
3 9
3 3
puzzle
的宣告方式
官方會根據不同的帳號提供不一樣的 puzzle ,這邊我直接複製貼到 Xcode 去執行。請自行替換自己的 puzzle 。
let puzzle = """
3 4
4 3
2 5
1 3
3 9
3 3
"""
程式碼
在這裡就把資料源通過 兩次分割 以及 轉型 整理成兩個整數陣列:
func grouping(with puzzle: String) -> (left: [Int], right: [Int]) {
puzzle
// 分行
.components(separatedBy: "\n")
.map {
/// !!!: 為求文章表示簡潔,這裡不做 error handling
/// 1) 分隔字串 -> 2) 轉型成整數
$0.components(separatedBy: " ").compactMap(Int.init)
}
.reduce(into: (left: [Int](), right: [Int]())) {
$0.left.append($1[0])
$0.right.append($1[1])
}
}
為了可讀性,也可以另外宣告一個結構來替代 tupple 。
第一部分
前半一大段其實都在說故事,沒有解題的關鍵資訊(笑)
題意
數列分 左數列
和 右數列
,題目要求個字排列之後,算出數值之間的總和 - 最小和最小的距離、第二小和第二小的距離,以此類推到最大和最大的距離。
當計算距離的相減後有可能會是 負值,因此在加總前可以使用 abs
方法取絕對值作為距離。
程式碼
func totalDistance(with puzzle: String) -> Int {
var (left, right) = grouping(with: puzzle)
left.sort()
right.sort()
var sum = 0
for index in 0..<left.count {
sum += abs(right[index] - left[index])
}
return sum
}
執行
let puzzle = """
... 省略
"""
let result = totalDistance(with: puzzle)
result
及為結果
複雜度分析
-
時間複雜度 O(nlogn)
- O(nlogn + n) -> O(nlogn)
- 排序 - O(nlogn)
- 走訪 - O(n)
- O(nlogn + n) -> O(nlogn)
-
空間複雜度 O(n)
- 有建立 2 個陣列來存放,省略常數項後為 O(n)
第二部分
題意
計算相對於 左
半邊數列的相似度,算出每一行的 當前值
* 出現次數
後再加總。以官方的範例為例:
3 4
4 3
2 5
1 3
3 9
3 3
左半邊數列
3 => 右數列中出現 3 次 => 3 * 3 = 9
4 => 右數列中出現 1 次 => 4 * 1 = 4
2 => 右數列中出現 0 次 => 2 * 0 = 0
1 => 右數列中出現 0 次 => 1 * 0 = 0
3 => 右數列中出現 3 次 => 3 * 3 = 9
3 => 右數列中出現 3 次 => 3 * 3 = 9
總和:
9 + 4 + 0 + 0 + 9 + 9 = 31
資料結構與想法
首先,先用 hash map 來計數每個數字在右數列中的出現次數。
接著再走訪左數列,再邊用題目提供的算式把總和算出來。
程式碼
func similarity(with puzzle: String) -> Int {
let (left, right) = grouping(with: puzzle)
var map = [Int: Int]()
for number in right {
map[number, default: 0] += 1
}
var sum = 0
for number in left {
sum += number * map[number, default: 0]
}
return sum
}
或是加總的地方也可以用 reduce
func simularity(puzzle: String) -> Int {
let (left, right) = grouping(with: puzzle)
var map = [Int: Int]()
for number in right {
map[number, default: 0] += 1
}
// 改寫的部分
return left.reduce(into: 0) {
$0 += $1 * map[$1, default: 0]
}
}
執行
let puzzle = """
... 省略
"""
let result = similarity(with: puzzle)
result
及為結果
複雜度分析
-
時間複雜度 O(n)
- 這個部分都只有線性走訪
-
空間複雜度 O(n)
- 左右數列: O(2n) -> O(n)
- 計數用的 hash map: 最壞情形就是每個數值都不一樣 -> O(n)
結語
以上,希望能 Advent of Code 2024 能做完。
如果有寫錯或有建議請留言讓我知道。如果有找到更好的解法,我會再多發一篇補充。
Top comments (0)