2020年5月30日 星期六

Leetcode題解 Python & C#:五月挑戰DAY30 K Closest Points to Origin

給一串座標 Points ,回傳 K 個離(0, 0)最近的點。

要知道距離的算法,因為是找跟(0, 0)的距離,所以用 P[0]**2 + P[1]**2,就能作為比較值。(其開根號是距離)

不難,重點要放在如何排序。

使用內建的 sort() 功能就能快速排序,如同官方方法,用 sorted(points, key:lambda x: x[0]**2+x[1]**2),能實現排序,
又不會多使用空間,是最佳解之一。

時間是 O(nlogn),就是排序法(sort)的時間吧。

換個想法,有什麼排序法夠快,最好可以快速找到最小值? min heap。
不然用hashtable以距離作分類,由小開始,加到K個就回傳,這也不用把整個points重新排序。

2020年5月29日 星期五

Leetcode題解 Python & C#:五月挑戰DAY29 Course Schedule

給一個 numCourses 代表從編號 0 到 numCourses 的課程。有先修要求 prerequisites,其中元素為[a, b]代表要先修完 b 才能修 a 課程。
回傳 true 如果可以修完所有課程,否則 false。

一旦先修課程形成了迴圈,如:[a, b][b, a],那就無法完成所有課程。

這裡的課程數最多有 100000 個,數目很大,要是使用暴力破解會有超時的可能。

如果使用先修數作排列,少的先修,多的後修,但這樣可能用上 O(N^2) 的時間,直接放棄。

先串起 N 個課程,像是N-ary Tree或是Linked List等,再檢查有無迴圈產生,會用 O(N) 時 O(N) 空。

用 memo 紀錄到過的 node,如果往下的過程碰到的 node 出現在 memo 裡面,則代表有接回頭,頭尾相接就回傳 false。

用 DFS 去搜尋,如果到底,也就是沒有後續課程,那代表該條搜尋路線是沒有迴圈產生,是安全路線,之後若 DFS 來到安全路線,也不必再往下尋找。

我在想那個題目所講的方式到底是什麼?後來看懂了,大部分的解答都是用提示的想法出發。

那就是做拓樸排序,也就是把有向圖(有方向關係,如先修),轉換成排序。如果有環出現,就無法轉換。(參考)

找第一點開始,也就是沒有先修課程的課程。往下尋找,每個點到訪次數不能超過 numCourses。這樣算暴力破解法嗎?

2020年5月28日 星期四

Leetcode題解 Python & C#:五月挑戰DAY28 Counting Bits

給一個非負整數 num,要找出 [0, num] 之間(包含兩端)的所有整數在二進位下有的 1 個數。

因為很簡單,所以有限制條件,在 O(n) 時空內解決。

如果是 0,那其二進位不含 1 ,是 0 個。
如果是 1,那其二進位只有 1 個 1 。
如果是 2,那進位,二進位變成 10,只有 1 個 1。
如果是 3,那二進位是 11,有 2 個 1。
如果是 4,那進位,二進位是 100,只有 1 個 1。 

Leetcode題解 Python & C#:五月挑戰DAY27 Possible Bipartition

有 N 個人(編號 1, 2, ..., N),要依照 dislikes 去分成二組,在 dislikes 中,每組 [a, b] 代表該編號的二人不能在同一組。
回傳 True 如果可以順利分成二組,否則 False。

這題蠻有意思的,我第一念頭想到 backtrace 可以硬解,但太慢,我放棄用 DP 與 遞迴函數。

說到兩兩成對,就想到最近的表格式DP,這題一樣可以用表格式DP,最後用 DFS 的遞迴函數可以比 backtrace 快上不少。
只要表格上可以選成每列每欄都只有一組配對,那就有可行的分組,也就是True。

抛開遞迴,因為平面化的話,一有答案可以馬上回傳,而遞迴函數會一路收斂到最上方,才能停止。要是深度深,會多花上一些時間。

被自我侷限,得用別的方法來。

2020年5月26日 星期二

Leetcode題解 C#:五月挑戰DAY26 Contiguous Array

給一個二進位串列,回傳 0 與 1 數量相等的連續子陣列的最長長度。


這次補寫 C# 版的 code。

Leetcode題解 Python & C#:五月挑戰DAY26 Uncrossed Lines


給兩個數列 A B 分別寫成上下平行,如果 A[i] == B[j] ,那可以用一直線相連,要找出最大沒有交叉的連線數。

做完上一個 contest ,再看這個問題,很自然就會以相似的解法去進行。

這裡有一個特色「兩數列,各取一個兩兩配對,配對後不能重複再用到。」這樣想到可能會有一個表格狀的DP,再應題目做處理。

這題也是這樣。

DP[i][j] 定為該位置上的最大的沒交叉的相連數。

如果位置 A[i] == B[j] ,那 DP[i][j] = 1 再加右下方 DP[i+1][j+1] ,即加上後方的最大沒交叉相連數。
然後 DP[i][j]  = 從自身、右、下三者取最大值,這樣 DP[i][j] 會是最大的沒交叉的相連數。

最後回傳 DP[0][0] 即可。

2020年5月25日 星期一

Leetcode題解 Python:Max Dot Product of Two Subsequences

給兩個陣列 nums1 和 nums2。回傳兩陣列中非空子序列(皆相同長度)的最大的點積(Dot Product)。
子序列可以是以原陣列刪除部分(也可以不刪),但元素的相對順序不變。

dot product的運算:
假設從 nums1 跟 nums2 取出 A 序列 跟 B序列。
A1、A2、A3
B1、B2、B3
=> A1*B1 + A2*B2 + A3*B3 => value

我又沒在時限內做完contest,QQ。

這題使用 DP 的觀念,用 p1 p2 代表兩陣列的點乘位置並加上後續的最大值,同時也是規劃 DFS 的遞迴函數。

由於不能有空值,一定要有個兩位置相乘作為開頭。即 nums1[p1] * nums2[p2] 。

面對 DFS 的設計,要從底開始想,也就是到底的情形。即 nums1[len(nums1)-1] * nums2[len(nums2)-1]。因為不能為空串列,該位置只有 nums1[p1] * nums2[p2] 的可能。

DP[len(nums1)-1][len(nums2)-1] ==  nums1[len(nums1)-1] * nums2[len(nums2)-1]

往上回傳,上面會是 p1-1 或是 p2-1 的,這樣不是 p2 會被重複乘到,不然就是 p1 。有共用的情況下,應該要取最大的可能,自身點乘跟下一個(下方、右方)取最大值,然後決定當前位置的 DP 值。

重點來了,那要如何再取更多,也就是要變成多組的情況。

每一對 p1, p2 都是一個點乘,其左上一個 p1-1,  p2-1 。
對 p1-1,  p2-1 來說,除了乘積外,還要加上 DP[p1][p2]的值,也就是後面的最大可能,如果後方最大值小於零,則不加。

這樣就能用到多組,從多組中找出最大的數值。
 
整理一下順序。

先算出 nums1[p1] * nums2[p2] ,視狀況加上 DP[p1+1][p2+1] 的值,成為該位置的基礎值。
跟下方(共用一個)的取最大值,即 DP[p1][p2] = max(DP[p1][p2], DP[p1+1][p2], DP[p1][p2+1])。

2020年5月24日 星期日

Leetcode題解 Python & C#:五月挑戰DAY24 Construct Binary Search Tree from Preorder Traversal

給一個以「Preorder」得到的值陣列,重構出二元樹,並回傳 root 。

這是一個嚴謹的二元樹,所以左邊子樹皆小於自身,右邊皆大於自身。

preorder 是以 自身 → 左 → 右 ,因此陣列的第一個元素會是root。
接著下一個會是左邊第一個,直到比root大。
第一個比root大的是右方第一個,直到最後。

因此可用從陣列中篩選(比第一個)大小,並把 solution 當遞迴函式用,完成二元樹的重構。

(因為本題重複過,所以更多說明請點這裡。)

2020年5月23日 星期六

Leetcode題解 Python & C#:五月挑戰DAY23 Interval List Intersections

要找出 A 與 B 的區間,因為不一定會按順序交疊,所以用 indexA , indexB 來記錄目前 Traverse 的位置。

如果有交疊,範圍會是從二者的左側取大,右側取小的範圍。

那要怎麼移動 index ? 

如果有一個的右側很大,對方的下一個右側可能還沒超過它。因此,此時應該是要繼續移動右側較小的。

2020年5月22日 星期五

Leetcode題解 Python & C#:五月挑戰DAY22 Sort Characters By Frequency

給一個字串,要以字母的出現次數重新排列。

首先要有可以統計字母的方式,用 hash table 可以很容易記錄哪些字的有出現與其次數。

python 有 collections.Counter 可以快速轉換。

2020年5月21日 星期四

Leetcode題解 Python & C#:五月挑戰DAY21 Count Square Submatrices with All Ones

給一個由 0 1 組成的矩陣,回傳有多少個正方形全部都是 1 。

這題蠻容易的,就算用暴力法 + dp ,也不會被判超時。

如果本身是 1 ,自己能形成一個正方形,然後往四角延伸去看是否能構成更大的正方形。

用 y x 由上至下,由左至右,所以選右下作基準點。那麼能構成更大的正方形,是其左上、左、上都是同一個數字。

如構成 2 * 2:
[[1,1],
[1,2]] 

位置[1][1]的點,其左上、左、上都是 1 ,所以可以構成 2(1+1)。

3 * 3:
[[1,1,1],
[1,2,2],
[1,2,3]]

位置[2][2]的點,其左上、左、上都是 2 ,所以可以構成 3(2+1)。3 表示該位置有(1*1、2*2、3*3)的正方形。
位置[2][1]的點,其左上2、左1、上1,所以可以構成 2(1+1)。

也就是說,是左上、左、上中取最小值+1。前提也要該位置本來是 1 才可以這樣處理。

2020年5月20日 星期三

Leetcode題解 Python & C#:五月挑戰DAY20 Smallest Element in a BST

給一串二元樹,在找出第 k 個小的元素。

這題目給的二元樹是比較嚴謹的,子樹左邊皆小於自身,右邊皆大於自身。

因此,左邊最底層的是最小的,接著其父,其父之右子樹,這與「in-order」的 traversal 方式是一樣的。

所以用上這樣的方法,並且加入「第幾小的序數」就能得到題目所求。

對於二元樹的題目,各種traversal模式都要熟練,才會很快地想到寫法。

2020年5月19日 星期二

Leetcode題解 Python & C#:五月挑戰DAY19 Online Stock Span

做一個 StockSpanner 收集每日價格資訊 price (int),並有設方法 next() 輸入 price ,回傳當前 price 是幾日新高。

對於這樣的問題,要想會不會用上 遞增數列 的觀念,這是這類題的一種數學思維,想通就會做。
(遞增數列的典型倒是沒有想到有什麼特徵可以快速抓出該用這個概念)

如果前面不超過price(<= price),那麼就把前方的紀錄丟掉。如果沒資料可丟,代表目前是紀錄中最大的。

用一個計數 count,把用 next() 次數保留下來,就可以其減出當前price是幾日新高。(若沒紀錄,則 count - 0)

並且把 price 跟 count 添加到紀錄中,price跟count的紀錄代表前方沒有比price更高的。

官方寫法用 weight ,省下了 1 空間,但功能跟 count 都是記錄次數的。

2020年5月18日 星期一

Leetcode題解 Python & C#:五月挑戰DAY18 Permutation in String

給二字串 s1 s2,問 s2 是否有含 s1 的排列。

這題跟昨天的原理一樣,就不多說了。 DAY17

Python
class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        count = Counter(s1)
        match_len = 0
        p_len = len(s1)
        for i, c in enumerate(s2):
            while match_len and (c not in count or count[c] == 0):
                count[s2[i - match_len]] += 1
                match_len -= 1
            if c in count and count[c] > 0:
                match_len += 1
                count[c] -= 1
                if match_len == p_len:
                    return True
        return False

2020年5月17日 星期日

Leetcode題解 Python & C#:五月挑戰DAY17 Find All Anagrams in a String

給一個字串 s 跟 非空字串 p 。要從 s 中找出所有 p 字謎的的起始位置。
(字謎是所有字母都出現一次,連續出現但順序可以變動)

如果順序不重要,那麼用 hashtable 記下哪些字母出現與次數及可。

從 s 開頭,依序匹配,是否在hashtable中及目前有可匹配的次數嗎?如果有,該字母的可匹配的次數 - 1,匹配長度 +1。
如果沒有,用匹配長度把最前方的已匹配字母放回hashtable所對應的次數,直到匹配或是匹配長度歸零為止。

如果匹配長度與 p 長度相同,則回推開始位置放入 result 中。

Leetcode題解 Python:Form Largest Integer With Digits That Add up to Target

給一串數字[1-9]的花費 cost,跟 總花費 target,回傳總花費相等的最大可能數字。

這題是DP,我本來感覺沒很難,結果敗在這裡 QQ 。考驗對DP與遞迴函數的熟練度。(我用了一些時間才精簡到可行)

對於規劃,函數要使用 t 也就是剩餘的花費,在同一個 t 之下,結果只會有一個最大值。
(我一開始用三個、二個都會超時,睡起再改成一個)

於是要朝著 dfs(t) 的方向前進。

在[1-9]花費有可能一樣的,為了加速,二者花費相等,只要保留較大的數字,結果也只會用到最大的數字。

如果 t == 0 做中止條件,那麼就無法知道是哪個數字讓其歸零。
得用 t - each_cost == 0,這樣就可以知道哪個數字了。

回傳值是最大的數字,若 t - each_cost > 0,那麼應該用 dfs(t - each_cost)去找出後面的最大數字,
並與 each_cost 代表的數字結合。

而 each_cost 有數種,同個 t 之下也有不同的組合,所以用 max() 去選出最大的數字,才能符合設計。

2020年5月16日 星期六

Leetcode題解 Python & C#:五月挑戰DAY16 Odd Even Linked List

給一個 linked list,要以 偶數位 奇數位 重新排列。

要求在 O(1) Space、 O(n) Time 解決。

分別拆成偶數奇數頭出來,再把剩下的 linked list 分給兩者,最後合併,回傳偶數頭。這符合題目要求。

或者直接在頭尾開始接,但這預期不如上者,因為上者到 n-1 位置時,就幾乎要完成了;而這方法才剛開始。

Leetcode題解 Python & C#:五月挑戰DAY15 Maximum Sum Circular Subarray

給一個環形陣列 A,要找出總和最大的子陣列。

這題蠻有意思的,讓我沒有順利解出來,有想到好幾種可能,看了別人的解法,發現是用最大最小的方式。
但我想到這方法的當下,沒有去進一步思考。

官方有 Solution ,內有四種解法,但我還是照步調去走從未知去導出解法的思維分析。

如果會做陣列中子陣列最大總和,那環形的也只是稍做變化。

要如何後面接回前面?這是第一個要解決的問題。

2020年5月14日 星期四

Leetcode題解 Python & C#:五月挑戰DAY14 Implement Trie (Prefix Tree)

要實作 trie ,有 insert, search 和 startsWith 的功能。

trie 很實用,但在 algorithm 中,不太好用上這樣的方法解題。昨日在學 cisco ,就使用 trie 來加速指今輸入。

由於 Leetcode 有教學,這裡就不多說如何解題。(如果懂 trie 是怎樣的型態就會知道要怎麼寫)

Leetcode題解 Python & C#:五月挑戰DAY13 Remove K Digits

給一個非負整數以 string 型態表示,移除 k 個數字,讓數字變成最小。

如果把它作DP,會超時,因為 num 的長度最多到10002。

不用 DP ,我們要來想想要如何從頭開始找起,並作篩選。

如果要讓結果是最小,那可想像會在一個範圍中,保留較小的,取走較大的。如此運作,直到挑出 k 個。

仔細觀察,被挑選過後的樣子。
"1432219", 3 -> "1219"
"4321234", 2 -> "21234"
"10200", 1 -> "200"

如果要排成最小,那麼會讓數列盡可能朝著「非升序排列」,也就是若左方大於右方,左方就會被挑走。
若左右相等或小於,則住右方移動。

2020年5月12日 星期二

Leetcode題解 Python & C#:五月挑戰DAY12 Single Element in a Sorted Array


給一個經排序後的數列,找出其中只出現一次的元素。(其他重複二次)

被限制在 時間複雜度 O(log n) 和 空間複雜度 O(1),解決。

說到 O(log n) time ,經典的方式是二分法。

由於其他都出現二次,可想 pos % 2 == 0 的位置會等於其右方的,除非有出現單次的元素在左方。

用二分搜尋法,計算的 m 在比較的時候要用 % 2 修正位置,去讓奇數位比其右方的位罝。

這裡的 r 跟往不同減了 1 ,是因應「超界」做處理,如果出現單次的在最右方,那麼一般的二分搜尋法,會在比較時比 nums[n] 。如果左方都出現兩次,那最後 r 值不變,指向最後一個為單一的元素。

2020年5月11日 星期一

Leetcode題解 Python:Trapping Rain Water

給一個代表(牆)高度的非負整數數列,而下雨之後,水會積在低窪處,回傳水的體積。

水會積多少,取決於左右方的最高高度取較低者減當前高度,若小於零代表沒有積水。

這樣子很好寫出正確寫法,但是超時的問題得考慮進去。

如果每次都重找最大值,時間複雜度會是 O(n**2),得想辦法拉到 O(n) 解決。

最大值一定要重找嗎?如果從左方開始,左方的最大值會一路遞增,而右方會一路遞減。過了數列的最大值,消漲就會互換。

也就是說,若能使用 TwoPointer 去從左右方往中間靠近,邊找邊算所求,就能把時間複雜度用在 O(n)。

當然可以做到,官方也有範例可供參考。

Leetcode題解 Python & C#:五月挑戰DAY11 Flood Fill

給一個二維陣列,要把[sr][sc]位置上的顏色(值)換成 newColor,像是油漆桶工具,回傳取代過後的二維陣列。

這題是「遞迴函數」的基本題。

遇到在這種二維陣列的題型,選一個點上下左右找,為了避免同一位置重複找,用hash類紀錄找過的位置作檢查。

如果用 for迴圈 要思考整體是由左上至右下找,會不會因此有漏網之魚,如果有,還是得回歸 遞迴函數。

Leetcode題解 Python:Combination Sum II

給一個數列 candidates,要從中選元素加總等於target,每一個元素只能選一次,回傳有幾種不同的組合。

由於要不重複的組合,所以用 hash 類可避免重複,又或者以計數每個各(不重複)元素的使用次數去推組合。

前不久說這類在「一數列中」的題目會有一些特點去知道該用什麼方法去解。
這一題則是「實際組合」,因此得保存用了什麼數字去嘗試,變成由上到下。(沒有用hash比較)

元素不必相鄰,而且都是正整數,但元素可能會出現重複,練習抓這些規則可以幫助順利寫出解法。

要撈出組合,有好幾種方法,我一開始使用 backtrace,不過這不好,backtrace是相對沒效率的方法,光是移出移回,每層就多耗了二步在搜索上。

將 backtrace 攤平後,用 stack 一層層找。

先 candidates.sort(),因為由小至大,加上避免重複,所以每個可能的組合只能往最後一個選擇位置的右方去挑選。
如 [1,2,3,4], target=3 中,若目前選到 1 ,則接下來只可以往右方,從 2 開始做選取。(若能往左,只會多出很多重複)
不過這沒辦法做到完全不重複,要是在 [1,1,1,1], t=3,[1,1,1]會出現兩次。不過這方法可以明顯加速找組合。

按照這樣的規則,再加上要比較的是總合,所以stack初始要放入的會是,當前總合(int)與每個選取位置(List<int>)。

2020年5月10日 星期日

Leetcode題解 Python:Next Permutation

給一個組合,要回傳(按各組合由小到大)下一個組合,如果是最大的組合,則回傳最小的組合。

這一題是一種數學題,如果數學想通了就很好解,沒想通,那就只好琢磨到理解為止。

這裡要講如何理解。以 [4,2,3,1] 排組為例。

如果要找到下一個,就只要從右方移一個(從尾開始),讓該組合的大小略增即可。

那要跟誰換呢?從右方開始,如果該位置的值比左方的大,那就是目標位置。
如果左方皆 >= 右方,那代表該組合是最大的。如 [4,3,2,1] [5,1,1]
([4,【2,3】,1])※選左右哪方當目標位置都可以。這裡用左右,必免混淆。

接著從目標位置右及右方開始選一個大於目標位置左的最小值,二者互相交換。可以想成進位。
([4,【2(左),3(右)】,1] -> [4,2,【3,1】] -> [4,2,【3】,1] -> [4,3,2,1])

換完之後,目標位置右方,應該要變成小到大的排序﹐才會是下一個組合。
([4,【3】,2,1] -> [4,3,1,2])

在換之前,目標位置右及右方已經是非升序排列(左>=右),若把它看成一個排列組合,那此時的右方排列會是該排列組合中最大的。

最大的組合,只要反序,就會回到最小的組合。

Leetcode題解 Python & C#:五月挑戰DAY10 Find the Town Judge

有 N 個人(編號 1,...,N),若所有人信任第x個,而且第x個不信任任何人,若只有一個符合這樣條件的人,回傳其編號。

把條件一一拿來解讀:
他必須被所有人信任,所以那人得到的信任會是 N-1 個。
他不會信任任何人,所以在 trust[i][0] 的人都不是候選人。

於是在遍歷 trust 的同時,要去統計每個編號的被信任數(t[1]),並刪除候選人(t[0])。

剩下的候選人是不相信任何人的人,再剔除其被信任數不為 N-1 的。

最後符合條件1,2,若只剩一個即符合條件3,回傳其編號。若無則-1。

Leetcode題解 Python:Number of Ways of Cutting a Pizza

給一塊矩陣 pizza,要用k-1刀切出 k 塊,每塊上面都要有至少一個 'A'(蘋果),每刀只能切一直線,而且切完會拿走右方或是上方的那一塊。要回傳有幾種切法。

這算hard中偏簡單的問題,結果我在這題犯小錯卡很久,但正確的主體寫法一下子就寫好了。

因為這題給的拿取規則,使我們隨著每一刀而往右下移動,不用考慮前方的結果。(前不影響後)

因此,使用 dfs(深度優先),以[y][x]做為dp的依據是沒問題的。(數字很大要取餘也是dp題的特色)

有刀數限制,再組合剛才的dp,組合成 dfs(y, x, pieces)的遞迴函數,而終止條件是 pieces == k。(可能有重複參數出現,可以使用 memo 紀錄)

Leetcode題解 Python:Count Triplets That Can Form Two Arrays of Equal XOR

給一列整數數列,問兩相鄰的子數列XOR總值相等的個數。

今天就差這一個沒有解出來。QQ

這種在「一數列」中取「組合」做「比較」的題目,都有一個解題方向,就是用dp的觀念去保存比較值。

比較值,比較值,比較值,因為很重要,所以說三次。而且比較也要朝著在hash類內的做處理。

2020年5月9日 星期六

Leetcode題解 Python:Substring with Concatenation of All Words

給一字串 s 跟 words 字串串列,問哪些位置可以找到全部在words中的詞只出現一次。

思路很簡單,想辦法在一個 n 的時間讓全部都搜過。(用搜尋法倒很隨意)

有一個條件可以利用,那就是每個 word 都是一樣的長度,因此不必逐字元去判斷,可以拿詞去匹配。

於是產生原型的逐詞匹配,但這還不夠!速度還太慢。

每當遇到這種匹配題型,盡可能不要反覆同一位置重複匹配。之前有提到一個方式 KMP,若全照用,這裡可能會在 prefix table 產生太多組合 。

因為 words 的詞沒有指定先後順序,所以要是當前位置沒有匹配到,就可把最前面匹配的放回待匹配的隊伍,繼續匹配,若都沒有可匹配,那就往下移動。

Leetcode題解 Python & C#:五月挑戰DAY9 Valid Perfect Square

給一個正整數,問其是否為整數的平方。

若以不轉換資料型態來計算,即只用 int 去解決,只要判斷 num 是否在(枚舉)整數的平方。
(這是一種挑戰,不然用Python內建的計算,一句就能非常迅速的解決。)

不過枚舉這樣子太慢了,如果是完美,那 num 可以用兩個相同的因數相乘。

用上找因數,就能避免枚舉很多不必要的數字,也不會有怕溢位的問題。

如果使用二分搜尋法加速,又會面臨溢位的問題,但處理好後速度會快不少。

2020年5月8日 星期五

Leetcode題解 Python & C#:五月挑戰DAY8 Check If It Is a Straight Line

檢查是否所有座標都在一條直線上。

這是很簡單的數學題,只要細節不漏掉就好。

寫出方程式: ax + b = y。但這不適用於垂直直線,因為同 x 會出現兩個以上的 y。 

這兩種情況要分開判斷,只要不是垂直直線的,靠解方程式就可以判斷。
若是,則看所有的座標的x是否為相同值。

(打完題解後,發現我本來寫好的是錯的但卻可以接受,只好幫提供testcase,默默改正code。

Leetcode題解 Python:Reverse Nodes in k-Group

給一個 Linked List,從左至右以 k 個節為一組作反轉,若不滿 k 個,則該維持原有排序。

像是在 List 中作位移的題目,多半跟 TwoPointer 的技巧是相關的。

這題也不例外,用 TwoPointer 控制兩個點互換,按題目要求解出所求。

如果由左至右,考量到後方未必有 k 個節,使用「深度優先」的遞迴函數,對於求解會比較好進行。(個人覺得比較好理解。

先檢查目前是否還有 k 個節,沒有則不逆序,有則逆序。

Leetcode題解 Python:Merge k Sorted Lists

合併 k 個 linked list 成一個 linked list,並且值由小到大排序。

說到要保持由小而大,不外乎就想到 min heap,關於 heap 的介紹,這裡就不多說了。

因為每次新的加入就得重新找出最小的,選擇 heap 是快多了。

不過不論是 heap 還是 sort() 都無法對於 ListNode 做比較,所以不是重新生成ListNode,
不然就是得找另外一種的保存方式。

我選擇用 dict() 以Node的值為鍵去存放 ListNode。但可能會出現同值有二個以上的ListNode,所以得用list存放,
同值的順序並不重要,抓哪個都可以。為了方便,使用 collections.defaultdict(list)。

用 heap 的 list 只有放 ListNode.val,只要拿值去對就可以取出下一個要接的 ListNode。

Leetcode題解 Python & C#:4Sum

如果有做出 3Sum 的人,這類題有一套公式,即使用 TwoPointer 的技巧,靠迴圈、加速搜遍數列。

3Sun是選定一個 i(由前往後),4Sum就多選一個 j(由後往前),剩下用 l、r,即 TwoPointer,用總和值來決定移動 l 還是 r。

先把數列排序,若 nums[i] + nums[l] + nums[r] + nums[j] > target,則我們要縮小總和,因此讓 r - 1,反則 l + 1,
這使得每次移動都讓總和與目標值靠近。如果跟總和跟目標值相同,記錄組合,然後移動 l 與 r 。  

由於不能重複,存放組合的資料型態可以選hash家族,這樣就能有效率的避免有同樣組合被計到二次以上。
又或者將重複的狀況跳過,不僅可加速迴圈,也可以直接用 list 放結果,不用轉換資料型態。 

Leetcode題解 Python & C#:五月挑戰DAY7 Cousins in Binary Tree

給一個二元樹的根節,找出 x、y是否為cousin(同深度,不同父)。

遍歷所有的節,再把找到的x、y資料(深度,父節)做對比,就可以辨別是否為cousin 。

要找遍二元樹上的每一個元素,除了可以用遞迴函數,也可以用一層一層的stack(queue皆可)來找。由於每節的值不會重複,不用擔心多個可能。

就效率來看,這題的情況單純,儘可能減少判斷,才是通往高效之路。

2020年5月7日 星期四

專案開發紀錄1:規劃使用環境

雖然我已經自己寫過好幾版的應用程式,不過卻很鬆散,沒有一個明確的SOP,想到什麼就做什麼。

每次都有一點突破,這次也要求自己要用上更多的進階技巧。

我在想打造一個方便跨平台,同時又能減少關卡的資訊傳送方式。不是那種要用IP位置的通訊協定、這太費工夫。

同時最好可以主機用API去修改內容,但不改變網址,如果每次更新都要開權限再重新分享,實在太麻煩。

對於跨平台是第一次嘗試,考量我之後沒辦法總是在我主機上使用,就算出門在外,也要能時時刻刻掌握動態。

線上DB + google sheet 本來是我第一個的組合。之前搭載虛擬機,可以用MSSQL,目前DB類的都不成問題。
不過,google sheet 暫時卡住我,因為我開新帳號,原因是覺得目前我用的帳號太過氣,取一個比較合身分的帳號。

我知道google新帳號,會被限縮很多功能,但主帳有很多私人的資料,我不想因為這次開發開一堆權限出去。
於是我的新帳號沒辦法使用 外掛功能 ,第一個嘗試就被迫停止在這裡。

接著我打算試試看 WordPress ,如果資料可上雲端,那麼我需要的是「隱私性」,只有我能看,不給一般尋常方式(google搜查)可找到。
WordPress有外掛功能,可以連接DB,也有權限設定。但,那需要商業版,一年300$,也就是9000台幣。
行不行?讓我不爽,因為我不知道WordPress,到底值不值,也許用起來跟我想像的不同,因為沒有試用,我的疑慮無法消。

「GIVE UP」換一種吧。

我希望主機要能即時影響所要呈現的資訊,不需要到每秒,但希望也能有每分鐘的更新頻率。

2020年5月6日 星期三

Leetcode題解 Python & C#:五月挑戰DAY6 Majority Element

給一個大小為 n 的陣列,找出出現次數超過 n/2 的元素。(可以預期這個元素一定存在)

這個是計數題,數一遍 n 並且統計出現次數,就能找到 majority element。

 Python
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        import collections
        counter =  collections.Counter(nums)
        n = len(nums)
        for key in counter:
            if counter[key] >= n/2:
                return key

2020年5月5日 星期二

Leetcode題解 Python & C#:五月挑戰DAY5 First Unique Character in a String

給一個字串,找出第一個沒有重複的字符位置,如果沒有則回傳 -1 。

沒有重複,代表該字符的計數為 1,只要找出最早的計數為 1 個字符。

最快的情形是跑一遍 N ,再挑出符合的,沒有那就是 -1 。

Python
class Solution:    
    def firstUniqChar(self, s: str) -> int:    
        import collections
        counter = collections.Counter(s)
        for key in counter:
            if counter[key] == 1: return s.index(key)
        return -1
C#
public class Solution {
    public int FirstUniqChar(string s) {
        var counter = new Dictionary();
        foreach(var c in s)
        {
            counter[c] = 1 + (counter.ContainsKey(c) ? counter[c] : 0);
        }
        foreach(var item in counter)
        {
            if(item.Value == 1)
            {
                return s.IndexOf(item.Key);
            }
        }
        return -1;
    }
}

2020年5月4日 星期一

Leetcode題解 Python & C#:五月挑戰DAY4 Number Complement、Complement of Base 10 Integer

給一個整數,回傳一個bit位互補(即 OR 後的 bit 每一位都是1)的數。
(這題跟 1009 Complement of Base 10 Integer 是相同的,只是換了名)

這題的邏輯很簡單,找出一個與 num 相同 bit 位長度且每一位都是 1 的數,再用 XOR 去求解。

為了必避免C#的int溢位,所以使用推進的方式,而不是由高位減一。

Python
class Solution:
    def findComplement(self, num: int) -> int:
        return ((1 << num.bit_length()) - 1 ) ^ num
C#
public class Solution {
    public int FindComplement(int num) {
        int r = 1;
        while(r < num)
            {r = (r << 1) + 1;}
        return r ^ num;
    }
}

Leetcode題解 Python:Find the Kth Smallest Sum of a Matrix With Sorted Rows

給一個 m*n 的矩陣,跟整數 k,而且每行已經排列。從每行取一個出來作總和,求第 k 小的總和是多少。

這題跟 373. 是很類似的,不過這題多了很多行數列,但並不是什麼難點。

若採用全部枚舉的話,最多40**40個,這樣子會超時。

若分批進行,一樣從全部的索引 0 開始,每輪後索引總和+1,繼續枚舉,因為目標大小 k 最高200個,所以這方法是可行的?

只要每輪枚舉結束,枚舉的總數超過 k 就可以找到目標值?但並不是這麼一回事。

的確一開是從最小值開始枚舉,會得到各索引+1的組合。

若只是單純以索引+1為枚舉的方式,這不能保證一定能包含完整的最小總和順序。

例如:mat = [[1,2,3,4,5,6],[1,1,1,1,1,1]], k = 3

Leetcode題解 Python:Find K Pairs with Smallest Sums

給兩個數列 nums1 nums2 ,要回傳 k 個總和由小排列的組合。參考

關於會作這一題, 是因為有一題更進階的在後頭,所以先把這個處理掉再進階。

之前我碰到 heap 都不是了解,但是又很多人會用,所以這次我得學著用 heap 去解問題。

關於 heap , 是一種「堆積法」,有 maxheap 和 minheap ,可想成是一個二元樹,把根節設為最大或最小。

好處是可以快速找到 max 或是 min ,在要用到這類的計算排列時,heap 就很好用了。

python的 heapq 是以 minheap 為主的,跟一般排序的 maxheap 是不同的。

而 minheap,也是這題會用到的 heap ,要找最小值。

如果用 heapq.heappush() 相當於 append()、sort(),這樣在枚舉時也能加速。

這題要找組合,所以要保存總和由小到大的組合的 result 。

2020年5月3日 星期日

Leetcode題解 Python:Number of Ways to Wear Different Hats to Each Other

給一組二維串列,一維代表第 i 位的人,二維代表該位置上的人可選的帽子種類編號。回傳全部人都戴上帽子且都是不同種類的方法數。

這題是DP,最大的特徵就是數目很大,需要取餘。(以前提過)

但DP的精華就是如何設計DP在放些什麼,而我這次學到了「bit」的方法。(然而我沒有在contest解出來QQ)

由於帽子種類跟人數都是有限制範圍的,若要取DP,可以由帽子做分配,或是由人開始。

要選哪一個?這我當時沒有想清楚,結果用人為主體。然後backtrace都超時。

以人為主體的話:1人可能有40種﹐最多有10人,是 40**10 是嗎?不,最糟是40*39*38*...*30。

以帽子為主體的話:則是 40*10! ,40是帽子的種類,10的階乘是代表沒有戴上帽子的人。只要能提前戴上帽子,就可以提前結束遞迴函數。

Leetcode題解 Python & C#:五月挑戰DAY3 Ransom Note

給兩個字串:ransomNote、magazine,問 ransomNote 是否能從 magazine 裡面的宇符組成。

嗯,這是一個電影會見到的情節吧…

方法很簡單,就是 ransomNote 的每個字符出現次數要比 magazine 的少或等於。

Leetcode題解 Pythonython:Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit

給一串整數陣列 nums 跟整數 limit,求連續子陣列的最大小值相減 <= limit 的最大子陣列個數。

我以前有碰到過這種問題,但我沒有在當下把 deque 的觀念內化,都選擇用dp,終於在今天吃苦頭。
(又或者可以用 two pointers 做解決。)

這種類型的問題有一個特色,就是有「連續子陣列(或串列)」,若條件不合,去掉頭再繼續。

用 時間複雜度 O(N) 解決掉,只給跑一次 nums 的機會。

基本架構是用一個串列,先把遇到的數加進串列後方,(while)若 max() - mix() > limit 則 del[0],在每輪最後檢查len()。

2020年5月2日 星期六

Leetcode題解 Python & C#:五月挑戰DAY2 Jewels and Stones

給兩個字串 J、S ,J裡面的字符是代表jewel,問 S 裡面有幾個 jewel。

這是一個相當簡單的問題,重點會放在用什麼作為搜尋的方式。

為了快速,這裡會使用 hash 家族的資料型態讓搜尋更快速。

如果是Python會用 set 或 dict 都可以,我用 collections.Counter, 只是換一點寫法。collection.Counter 是 dict 類的。

如果是C#,我直接用 string 的 Contains() ,其實做法都大同小異。 

2020年5月1日 星期五

Leetcode題解 Python & C#:五月挑戰DAY1 First Bad Version

給一個 n 代表目前有 n 個版本,要透過 isBadVersion(version) 的函數去找到第一個 True(即BadVersion)的版本號。

這題如果是有使用git去控制版本,那應該是很生活化的搜尋思維。

首先決定搜尋法,這裡使用二分搜尋法去解。

左為 l 右為 r,每次都從 m = (r-l)//2+l 位置找,我之前不太能掌握 l, r 與 m 的替換,到底是 >=, <=, m+1, m 的關係要如何連結起來,當我試著想用 r 去貼齊 target 的位置 ,就比較了解該怎麼寫對。

不過這題也不會有這樣的困擾。

由於版本會從 1 開始, l (L)= 1 ,而 r 要等於 n + 1 ,不過最壞的陷阱來了。若 n = 2147483647 ,那麼就會發生溢位,所以要做一點小修改。(如果是Python就不用擔心了)