化繁為簡: 映射化簡的演算法技巧

閱讀時間約 16 分鐘
raw-image

演算法映射化簡的核心觀念

在面對新題目的時候,除了重頭想一個新的演算法之外;
還有另一個方法,想看看有沒有核心觀念彼此相同的問題與演算法
如果有,就可以把新的題目映射化簡到已知解法的問題,用已知的演算法去解開

接著,我們會介紹幾個範例,並且使用映射化簡的技巧來解題,透過化簡框架的復現,幫助讀者掌握這個知識點。


組合數之和 Combination Sum

題目給定目標值target,和一群整數candidates。
請問從中挑任意個數字做組合,允許重複選,有多少種方法可以讓組合數之和= target ?

把所有可能的組合方法列出來。

這題常見的方法是用DFS+回溯法去解,之前在專欄文章也解析過。


還有沒有別的解法呢? 其實有喔!

化簡到找零錢模型 Coin Change II

仔細觀察,用一群整數去湊出目標值target
這個動作就相當於,用銅板去湊出目標金額amount!
整數就相當於銅板,每個整數對應到銅板的面額,例如一元、五元、十元。
目標值target就相當於目標金額amount
那麼,這題組合數之和的問題,就被我們映射化簡到找零錢問題II Coin Change II
只要把所有能湊出target元的找零組合列出來,就是最終答案


原本已知的Coin Change II的找零錢演算法和解題程式碼

class Solution:

def change(self, amount: int, coins: List[int]) -> int:

# base case:
# amount 0's method count = 1 (by taking no coins)

dp = [1] + [ 0 for _ in range(amount)]

# make change with current coin, from small coin to large coin
# 請注意看,等等我們會用 找零錢的演算法 去解開 組合數之和!
for cur_coin in coins:

# update change method count from small amount to large amount
for small_amount in range(cur_coin, amount+1):

# current small amount can make changed with current coin
dp[small_amount] += dp[small_amount - cur_coin]

return dp[amount]


用已知 Coin Change II的演算法,去解開組合數之和。

唯一的小差異就是原本只需要計算方法數,所以用int去累加方法數即可。

現在需要知道方法的具體組合情況,因此,我們用list [] 去記錄具體的組合數

class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

dp = defaultdict(list)

## Base case:
# $0 is reached by taking nothing
dp[0] = [ [ ] ]

coins = candidates

## General cases:
# 請注意看,剛剛介紹的找零錢演算法就出現在這裡!
for coin in coins:
for amount in range(coin, target+1):

# satisfy $amount from ($amount-$coin) + $coin
for prev_coin_change in dp[ amount - coin ]:
dp[amount] += [ prev_coin_change + [coin] ]

return dp[ target ]

幾乎九成像,對吧!


最長回文子序列 Longest Palindromic Subsequence

給定一個字串s,請計算s的最長回文子序列的長度

這題常見的方法是用DP動態規劃配合回文框架去解,之前在專欄文章也解析過。


還有沒有別的解法呢? 其實有喔!

化簡到最長共同子序列 Longest Common Subsequence, aka LCS


仔細觀察,s的最長回文子序列
這個動作就相當於,s 和 逆序的s 的最長共同子序列!
s逆序的s 找共同序列,相當於共同子序列
s 和 逆序的s 子序列如果有相同,那一定是回文的形式(因為彼此頭尾順序相反)
那麼,這題最長回文子序列的問題,就被我們映射化簡到最長共同子序列LCS
只要計算s 和 逆序的s 的最長共同子序列的長度,就是最終答案


原本已知的LCS的最長共同子序列演算法和解題程式碼

class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:

# 請注意看,等等我們會用 最長共同子序列的演算法 去解開 最長回文子序列!
dp = {}
def LCS(i, j):

if (i, j) in dp:
return dp[i, j]

if i == -1 or j == -1:
dp[i, j] = 0
return 0

elif text1[i] == text2[j]:
dp[i, j] = LCS(i-1, j-1)+1
return dp[i, j]

else:
dp[i, j] = max( LCS(i, j-1), LCS(i-1, j) )
return dp[i, j]

# ------------------------------
return LCS( len(text1)-1, len(text2)-1 )


用已知 LCS最長共同子序列的演算法,去解開 最長回文子序列。

唯一的小差異就是傳入的參數,一個是原本的字串s,另一個是逆序的s

class Solution:
def longestPalindromeSubseq(self, s: str) -> int:

# -------------------------------------------------
# 請注意看,剛剛介紹的 最長共同子序列 就出現在這裡!
dp = {}
def LCS(i, j):

if (i, j) in dp:
return dp[i, j]

if i == -1 or j == -1:
dp[i, j] = 0
return 0

elif text1[i] == text2[j]:
dp[i, j] = LCS(i-1, j-1)+1
return dp[i, j]

else:
dp[i, j] = max( LCS(i, j-1), LCS(i-1, j) )
return dp[i, j]

# -------------------------------------------------

# Original s as text1
text1 = s

# Reversed s as text2
text2 = s[::-1]

# Longest palindromic subsequence of s = Longest common subsequence(s, s[::-1])
return LCS( len(text1)-1, len(text2)-1 )

幾乎九成像,對吧! 我們只要傳入 正序的s 和 逆序的s 即可。


合併k條已排序的Linked list Merge k Sorted Lists

給定k條已排序的串列,要求我們把k條串列合併成一條已排序的串列

這題常見的方法是動態維護最小堆minHeap去解。


還有沒有別的解法呢? 其實有喔!

化簡到合併兩條已排序的串列 Merge Two Sorted Lists


仔細觀察,合併k條已排序的串列
這個動作就相當於,前面k/2條的串列 和 後面k/2條的串列做合併!
前面和後面又可以如此反覆分割,一直到剩下一條串列或者空串列為止。
兩條已排序的串列合併我們已經會做了。
那麼,這題合併k條已排序串列的問題,就被我們映射化簡到合併兩條已排序串列。
只要不斷分割,倆倆先合併成一條,中間再不斷倆倆合併成一條,最後合併結果就是最終答案


原本已知的合併兩條已排序串列的演算法和解題程式碼

class Solution:

# 請注意看,等等我們會用 合併兩條已排序串列的演算法 去解開 合併k條已排序串列!
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:

# List 1 is empty, then result is decided by List 2
if not list1:
return list2

# List 2 is empty, then result is decided by List 1
if not list2:
return list1

# General cases: compare node value, and update linkage
if list1.val < list2.val:
list1.next = self.mergeTwoLists(list1.next, list2)
return list1

else:
list2.next = self.mergeTwoLists(list1, list2.next)
return list2


用已知 合併兩條已排序串列的演算法,去解開 合併k條已排序串列。

唯一的小差異就是多了k條之間,兩兩先分割再合併的動作。

class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
#----------------------------------
# 請注意看,剛剛介紹的 合併兩條已排序串列的演算法 就出現在這裡!
def merge(l1, l2) -> ListNode:
'''
input: two sorted list l1, l2
output: merged sorted list
'''

dummy_head = ListNode('#')
cur = dummy_head

while l1 and l2:
if l1.val <= l2.val:
cur.next = l1
cur, l1 = cur.next, l1.next
else:
cur.next = l2
cur, l2 = cur.next, l2.next

if l1:
cur.next = l1
else:
cur.next = l2

return dummy_head.next
#----------------------------------

if not lists:
# base case:
# empty list
return None

elif len(lists) == 1:
# base case:
# only one list
return lists[0]

# general case:
# divide-and-conquer

# divide:
mid = len(lists)//2
left = self.mergeKLists( lists[:mid] )
right = self.mergeKLists( lists[mid:] )

# conquer:
return merge(left,right)

核心相同,只是多了兩兩分割和合併的外衣而已。


重點回顧: 化簡的關鍵步驟

找出新問題的核心是在問什麼?

觀察新問題已知會解的問題、我們已經會的演算法,有沒有共通之處?

如果有,建立映射關係,把新問題化簡到已經會解的問題

由已經會的演算法框架,構建出新問題的演算法與答案!

(通常只要很少量的修改即可,相當於搭橋、映射的動作)


結語

今天介紹了很靈活的演算法化簡技巧,
希望藉由思考流程、演算法化簡框架、經典例題的復現,
幫助讀者掌握這個重要的知識點。

我們下篇文章再見囉!

avatar-img
88會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
給定一個Linked list鏈結串列的Head node, 請判斷這條Linked list是否存在環路(Cycle)? 如果有環路,回傳True。 如果沒有,回傳False。
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 組合數之和 Combination Sum 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所以可能的情況,
這篇文章,會帶著大家複習以前學過的 格子點DP框架, 並且以最小成本的下降路徑的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 最小成本下降路徑的形式 每個格子點的值代表經過的成本。 要求從最上面那排往下方走,落到最下一排的最小成本的下降路徑。
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
這篇文章,會帶著大家複習以前學過的二進位DP框架, 並且以0~N的整數有幾個bit1,有幾個bit0的概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 常見的考法 請問整數k有幾個bit1? 有幾個bit0? 請問整數0到整數N分別各有幾個bit1? 有幾個
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
給定一個Linked list鏈結串列的Head node, 請判斷這條Linked list是否存在環路(Cycle)? 如果有環路,回傳True。 如果沒有,回傳False。
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 組合數之和 Combination Sum 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所以可能的情況,
這篇文章,會帶著大家複習以前學過的 格子點DP框架, 並且以最小成本的下降路徑的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 最小成本下降路徑的形式 每個格子點的值代表經過的成本。 要求從最上面那排往下方走,落到最下一排的最小成本的下降路徑。
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
這篇文章,會帶著大家複習以前學過的二進位DP框架, 並且以0~N的整數有幾個bit1,有幾個bit0的概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 常見的考法 請問整數k有幾個bit1? 有幾個bit0? 請問整數0到整數N分別各有幾個bit1? 有幾個
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
化繁為簡是在追尋華麗繁複的生產與輸出後應該討求的能力
Thumbnail
工作只是讓妳實現自我的路上做的其中一件事而已 ,不要因為他人無心評論而抹滅自己的光芒。
Thumbnail
當我們持續的學習成長,在某個領域當中越來越專業, 因此被邀請擔任講師或是公司當中的教導角色, 那一刻,我們從一個學習者變成了教導者, 這時可能會開始意識到,專業知識的傳達與教學其實充滿挑戰。
小女孩的主張是~錢非萬能,但糖果是萬能的,早點睡,注意日益成長的兒童玩具市場。 神鵰俠侶有一個橋段~周伯通對小龍女說,你看,黃蓉丫頭聰明絕頂,反之郭靖這傻小子資質平平,怎麼看都不是一個練功奇才,怎麼就怪了,偏偏郭靖卻有辦法練成奇功,蓉丫頭卻怎麼學也學不來。小龍女,你會一手畫圓,一手畫方嗎?小龍女馬上
Thumbnail
4 步驟讓你學會如何將吸收的資訊轉化為產出!本週一口氣整理了5本和產出相關的書籍,包含了:《output最高學以致用法》、《1枝筆+1張紙,說服各種人》、《韓國企劃女神CCC思考整理術》、《高產出的本事》及《15分鐘寫出爆紅千字文》。身為一個文字及內容行銷及產出工作者,先前買了不少相關的書,延續著前
Thumbnail
Hi親愛的大家,「化繁為簡」的功夫你自認為如何? 「In character, in manner, in style, in all things, the supreme excellence is simplicity.」 Henry Wadsworth Longfellow 在性格、方式、風
Thumbnail
「錢真的就是從台股流出去了。」 這句話是個人三月升息循環以來一直觀察的最重點跟提醒,在三月升息前就一直提醒的,要觀察升息跟縮表的實際影響狀況,才能決定未來操作應對的大方向。 事實上呢? 三月的大盤日均量是3266億。 四月的大盤日均量是2709億。 五月的大盤日均量是2380億。 要承認這件事實。
Thumbnail
高爾夫是一個對自己的挑戰,也是一條漫漫長路,打到後面、你會有自己的解釋和定義!對你而言什麼是一個句號?每個人的答案都不同!這才是最屌的地方,也符合世界的基本原理。什麼是答案?本來就因人而異。為什麼一定要追求桿數呢?我又不打職業,就是運動和交朋友、喇賽而已!更多在釋放平時的壓力。 先說對工具,20年
Thumbnail
簡報講求溝通效率,無論使用言語或文字說明,在溝通過程中都仰賴聽眾在腦中多一層的轉換功夫。「一張圖勝過千言萬語,」根據研究發現,訊息圖像化可減少 25%~40% 的溝通時間,因此若能在簡報中善加運用圖表、表格、圖片等視覺要素,維持「圖像 80% 、文字 20% 」的比例,將有助觀眾在黃金 10 秒內
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
化繁為簡是在追尋華麗繁複的生產與輸出後應該討求的能力
Thumbnail
工作只是讓妳實現自我的路上做的其中一件事而已 ,不要因為他人無心評論而抹滅自己的光芒。
Thumbnail
當我們持續的學習成長,在某個領域當中越來越專業, 因此被邀請擔任講師或是公司當中的教導角色, 那一刻,我們從一個學習者變成了教導者, 這時可能會開始意識到,專業知識的傳達與教學其實充滿挑戰。
小女孩的主張是~錢非萬能,但糖果是萬能的,早點睡,注意日益成長的兒童玩具市場。 神鵰俠侶有一個橋段~周伯通對小龍女說,你看,黃蓉丫頭聰明絕頂,反之郭靖這傻小子資質平平,怎麼看都不是一個練功奇才,怎麼就怪了,偏偏郭靖卻有辦法練成奇功,蓉丫頭卻怎麼學也學不來。小龍女,你會一手畫圓,一手畫方嗎?小龍女馬上
Thumbnail
4 步驟讓你學會如何將吸收的資訊轉化為產出!本週一口氣整理了5本和產出相關的書籍,包含了:《output最高學以致用法》、《1枝筆+1張紙,說服各種人》、《韓國企劃女神CCC思考整理術》、《高產出的本事》及《15分鐘寫出爆紅千字文》。身為一個文字及內容行銷及產出工作者,先前買了不少相關的書,延續著前
Thumbnail
Hi親愛的大家,「化繁為簡」的功夫你自認為如何? 「In character, in manner, in style, in all things, the supreme excellence is simplicity.」 Henry Wadsworth Longfellow 在性格、方式、風
Thumbnail
「錢真的就是從台股流出去了。」 這句話是個人三月升息循環以來一直觀察的最重點跟提醒,在三月升息前就一直提醒的,要觀察升息跟縮表的實際影響狀況,才能決定未來操作應對的大方向。 事實上呢? 三月的大盤日均量是3266億。 四月的大盤日均量是2709億。 五月的大盤日均量是2380億。 要承認這件事實。
Thumbnail
高爾夫是一個對自己的挑戰,也是一條漫漫長路,打到後面、你會有自己的解釋和定義!對你而言什麼是一個句號?每個人的答案都不同!這才是最屌的地方,也符合世界的基本原理。什麼是答案?本來就因人而異。為什麼一定要追求桿數呢?我又不打職業,就是運動和交朋友、喇賽而已!更多在釋放平時的壓力。 先說對工具,20年
Thumbnail
簡報講求溝通效率,無論使用言語或文字說明,在溝通過程中都仰賴聽眾在腦中多一層的轉換功夫。「一張圖勝過千言萬語,」根據研究發現,訊息圖像化可減少 25%~40% 的溝通時間,因此若能在簡報中善加運用圖表、表格、圖片等視覺要素,維持「圖像 80% 、文字 20% 」的比例,將有助觀眾在黃金 10 秒內