Leetcode題解 Python & C#:六月挑戰DAY5 Random Pick with Weight

給一串正整數權重 w ,w[i] 代表 i 的權重。然後要寫一個 pickIndex 方法,以加權的機率抽出一個 i 。
雖然說 Python 的 random 模組有方法可以模擬做加權抽樣,但是速度不夠快,以致於超時,所以要減少 random 的使用,靠自己寫更有效率的代碼。
(這也是很少見的專門模組效率會遜於自撰的)
加權抽樣若以 1 為單位,把所有 index 放入一個假想的箱子,權重為 1 放1個,權重為 3 放3個,最後從中隨機抽取一個出來。
抽中 i 的機率為 w[i] / total(總權重) ,所有的機率加起來會等於 1 。若把機率從頭逐一累加,保存累進序列,這序列以權重把[0,1]之間切割區間。
用 random.random() ,可以得到一個 [0, 1] 之間的 float ,然後我們要用這個 [0, 1]之間的 random數 去對應出一個 i。
以輸入[1,4]為例,對照累進機率,若 random數 位於 [0, 0.2],那代表抽到 0 。若 random數 位於 [0.2, 1],那代表抽到 1。
要找出 random數 落於哪個區間,可以用二分法去搜尋,預期 O(logN) Time 。(不知道random的方法是用到 O(N) Time嗎?至少二分法比O(N)快)
如果剛好落在區間界線(如 == 0.2)要算前面的 i 還是後面的 i ?其實都沒差,根據「積分」,剛好落在 0.2 的機率為 0 ,所以不會影響到權重。

Python

class Solution:

def __init__(self, w: List[int]):
total = sum(w)
self.w = []
accu = 0
for i in range(1, len(w)):
accu += w[i] / total
self.w.append(accu)

def pickIndex(self) -> int:
l, r = 0, len(self.w)
t = random.random()
while l < r:
m = (r - l)//2 + l
if self.w[m] < t:
l = m + 1
else:
r = m
return l

C#

public class Solution {

double[] ws;
Random random = new Random();

public Solution(int[] w) {
var total = w.Sum() * 1.0;
ws = new double[w.Length];
ws[0] = w[0] / total;
for(int i = 1; i < w.Length; i++)
{
ws[i] = ws[i-1] + w[i] / total;
}
}

public int PickIndex() {
var t = random.NextDouble();
int l = 0; int r = ws.Length;
while(l < r)
{
int m = (r-l)/2 + l;
if(ws[m] < t)
l = m + 1;
else
r = m;
}
return l;
}
}

Leetcode題解 Python & C#:六月挑戰DAY4 Reverse String

寫一個功能反轉 char 陣列,在內部修改,不用回傳值。
對於初學物件導向的人,很容易用新的實例來取代原本的,不過如果是 C# 等,就得宣告才會產生新的,自然不會有混淆的問題。(比較少)
反轉字串的方式,就是最前與最後開始兩兩交換,換到 len()//2 之前停止。

Python

class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
ls = len(s)
for i in range(ls//2):
s[i], s[ls-1-i] = s[ls-1-i], s[i]

C#

public class Solution {
public void ReverseString(char[] s) {
int l = s.Length-1;
for(int i = 0; i < (l+1)/2; i++)
{
char lc = s[i];
s[i] = s[l-i];
s[l-i] = lc;
}
}
}

Leetcode題解 Python & C#:六月挑戰DAY3 Two City Scheduling

要把 2n 個人,平分給 cityA 和 cityB,costs[i] 代表第 i 個人,costs[i][0] 代表第 i 個人到 cityA 的花費,而 costs[i][1] 則是到 cittyB。要回傳最小總花費。※去 cityA 和 去 cityB 都應該有 n 個人。
一看到題目,馬上用DP解,因為我解的上一題是也是分配的問題。不過一看到用時,就知道這不是好的寫法。
對總體來說,如果每個人都以「相對代價」較低的為目的地,就可以找到最佳分配的情形。
相對代價 costs[i][0] – costs[i][1],我們讓前 n 個相對代價較小的到 cityA ,後面的到 cityB,就是最佳方案。  
(若有 3 個以上的選擇,就不能用這算法。)

Python(DP)

class Solution:
def twoCitySchedCost(self, costs: List[List[int]]) -> int:

n = len(costs)//2

@lru_cache(None)
def dfs(n1, n2):
if n1 + n2 == n*2:
return 0

i = n1 + n2
path = set()
if n1 < n:
path.add(costs[i][0] + dfs(n1+1, n2))
if n2 < n:
path.add(costs[i][1] + dfs(n1, n2+1))
return min(path)

return dfs(0,0)

Python

class Solution:
def twoCitySchedCost(self, costs: List[List[int]]) -> int:
costs.sort(key = lambda x: x[0] - x[1])
n = len(costs)//2
result = 0
for i in range(n):
result += costs[i][0] + costs[i+n][1]
return result

C#

public class Solution {
public int TwoCitySchedCost(int[][] costs) {
int n = costs.Length / 2;
int result = 0;
int[][] sorted_costs = costs.OrderBy(x => (x[0]-x[1])).ToArray();
for(int i = 0; i < n; i++)
result += sorted_costs[i][0] + sorted_costs[i+n][1];
return result;
}
}

Leetcode題解 Python:Probability of a Two Boxes Having The Same Number of Distinct Balls

有 2n 個球,球有 k 個顏色,寫成一串 k 長度 balls 陣列,balls[i] 代表第 i 顏色有多少顆球。
今天要隨機分到兩箱子,分完打開看,回傳最後兩箱子球的顏色數目相同的機率。
因為是隨機,所以拿到顏色順序不會按照顏色種類排列,例:[1,3] [3,1] ,而二者都是合規則的可能情形。 
按照真實隨機分配的方式,即一顆顆隨機抽出,運算次數會隨 k 與 sum(balls) 呈指數爆炸性成長。
如:[6,6,6,6,6,6],會作 36! 次探討,自然會超時,要想辦法改進。
對於隨機排列組合,像是抽籤等情況,要掌握到這是「邊抽邊開」還是「抽完再開」?二者在計算上是不同的。
「抽完再開」的如此題,同顏色的順序就不重要,如:白1白3、白3白1,都視作 白白,視為同一種。

如果有6顆球白白白白紅紅,那抽完再看的組合數有 6!/4!/2! 個,因同顏色間排列都視為一個,所以用除去 4!(白) 2!(紅) 來排除同色順序的影響。
為了要加速計算,勢必得抛棄直觀的一顆顆隨機分配,朝著直接抽一把起來的方向分配會快個多。
照順序一次次抽,這裡的順序選擇「顏色」,從第 0 位抽到第 k-1 位顏色,最後再把組合數 n! / C0balls! / C1balls! / … / Ck-1balls! 算出來。
拿 [6,6,6,6,6,6] 為例,在 [0] 位顏色時, box1 可拿 0-6 顆,算作 7 種拿法,那麼最糟要探討次數為 7**6 ,感覺很多,但相比 36!,
算快上非常顯著的。
接下來有二種DP:
1.紀錄BOX1的各色球數,最後可反推BOX2。 (i, tuple([B1C0balls, B1C1balls, …, B1Ck-1balls]))
2.紀錄BOX1與BOX2的目前的球數與顏色數。 (i, color1, balls1, factorial1, color2, balls2, factorial2)
我用 2. 。 (2. 是 1. 的攤平化,用tuple是非常沒有效率的)
寫遞迴函數時,先思考中止條件,也就是分完了 k 色。此時算組合數的 n! 是已知,但後面的除數是上層分球時決定的,
因此最深層的DFS需要把除數一路往深傳遞。
算出 box1 組合數,再乘上 box2 組合數,就能得到該分配下的隨機總可能數,納入總數。要是 c1 == c2,就納入合格數。
回傳 合格數  /  總數

Python

class Solution:
def getProbability(self, balls: List[int]) -> float:
n = sum(balls) // 2
bs = len(balls)

@lru_cache(None)
def fl(num):
if num <= 1: return 1
return num * fl(num - 1)

@lru_cache(None)
def dfs(i, c1, n1, f1, c2, n2, f2):
if i == bs:
combo = fl(n)*2 / f1 / f2
return combo * (c1 == c2), combo
maxTake = min(balls[i], n - n1)
minTake = max(balls[i] - (n - n2), 0)
s, p = 0, 0
for b in range(minTake, maxTake + 1):
res = dfs(i+1, c1+(1 if b > 0 else 0), n1+b, f1*fl(b), c2+(1 if b < balls[i] else 0), n2 + balls[i] - b, f2*fl(balls[i] - b))
s += res[0]
p += res[1]
return s, p

return dfs(0, 0, 0, 1, 0, 0, 1)[0] / dfs(0, 0, 0, 1, 0, 0, 1)[1]

Leetcode題解 Python & C#:六月挑戰DAY2 Delete Node in a Linked List

寫一個功能去從一 linked list 中刪除一個 node,會給那個要被刪除的 node。
這題真的難到我了,在第一眼看到的時候。明明有兩個input,怎麼只傳入一個node?一旦看懂之後就很簡單了。
如果要刪除一個node,那最簡單的方式就是把它上一個跟它下一個接起來,但是這裡沒有上一個,而是直接給要刪除的node。
沒辦法修改實例位置完成,那就修改屬性。

原:A「B」CD -> A「」CD -> ACD
此:A「B」CD -> A「C」CD (修改值) -> AC「C」D -> AC「」D -> ACD
要刪除的node的值改成下一個的值,next改成下一個的next,即可。(但是無法刪最後一個node,題目也有說不會刪最後一個)

Python

class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
node.val = node.next.val
node.next = node.next.next

C#

public class Solution {
public void DeleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}

Leetcode題解 Python & C#:六月挑戰DAY1 Invert Binary Tree

反轉二元樹。

這題被啟發的述說蠻好笑的,會寫軟體,但不會在白板寫出反轉二元樹。
有時候會被簡單卡關是難免的。

要反轉二元樹,也就是左右對調,node.left, node.right = node.right, node.left,如此運作下去到底。

我也沒有一次到位,不過很快就察覺到該怎麼寫。


Python

class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
stack = [root]
while stack:
node = stack.pop()
if node:
node.left, node.right = node.right, node.left
stack.extend([node.left, node.right])
return root

C#

public class Solution {
public TreeNode InvertTree(TreeNode root) {
if(root is null){return root;}
var stack = new Stack();
stack.Push(root);
while(stack.Count > 0)
{
var node = stack.Pop();
var temNode = node.right;
node.right = node.left;
node.left = temNode;
if (!(node.right is null))
stack.Push(node.right);
if (!(node.left is null))
stack.Push(node.left);
}
return root;
}
}

Leetcode題解 Python & C#:五月挑戰DAY31 Edit Distance

給兩個字串,要找最小的編輯次數讓 word1 變成 word2。編輯只有三種操作:insert、delete、replace。
這是hard題,蠻有意思的,可以找出像是基因序列相似的程度,或是字典中找相似的詞。
如果用人的概念去想,要怎麼選,要用哪種換法,基準點在哪裡,似乎不是那麼好想像的。
但是能確定朝著每個操作,是 word1 和 word2 之間的交換。
舉例:word1 = ABC, word2 = BABC。
可以想到把 word2[0]做delete,或 word1 insert(0,”B”),就可以得到 word1 == word2。
為了好講,這裡不探討 word2 上做操作,統一以 word1 當基準,也就是只看 word1 -> word2。
像是 word1[0] == word2[1] == “A”,在這位置上不用操作,因為二者相等。說到「位置」這裡換個思考 DP[p1][p2] ,
二者的方向關係是一個表格DP,若 word1[p1] == word2[p2] , 則 dp[p1][2] 的基本值等於0,即不需要任何操作,反之則 1。
  
題目要找出最小操作次數,也可以想像把操作數往右下角,一路取最小壘加,最後的結果就是最小操作次數。
如果 word1[p1] != word2[p2],代表要進行操作,在 DP[p1][p2] 取 {DP[p1-1][p2-1], DP[p1-1][p2], DP[p1][p2-1]}三者最小值加 1 (基本值)。
分別對應{replace, delete, insert}三種操作中取最佳方案到DP[p1][p2] ,看 DP[p1][p2] 的「相對位置」來決定是哪種操作,本身基本值 1 有三種操作的可能,至少一定是其中一個。

 BABC
A11
B1X
在位置[0][1],[0][0] 是 [p1][p2-1],即[0][0] insert(0, Char) (B)
在位置[1][0],[0][0] 是 [p1-1][p2],即[0][0] del word1[0] (A)
在X位置[1][1],對X位置來[0][0]位罝是 replace (A -> B) ,[0][1]位置是 del word1[0],而 [1][0] insert(1, Char)。
那 word1[p1] == word2[p2] 時呢? DP[p1][p2] = ?
我們取右下角代表經過操作次數使從頭到p1,p2位置都對上,最右下角即從頭到尾都對上的操作次數。
一旦 word1[p1] == word2[p2],所需要的次數,即前面都對上所需要的次數,DP[p1][p2] = DP[p1-1][p2-1]。(左上那個 相當於子問題(二字串編輯)的解)
如果在 row = 0 或 col = 0 時 word1[p1] == word2[p2] 相等, DP[p1-1][p2-1] 會超出範圍。那該等於多少?
要是相等,那麼要加上使前面相等的次數。如果是[0][0],那麼就是0 次, 如果是 [1][0] 或 [0][1],那麼前面要一樣,不是 delete 就是 insert 前面的,為 1 次。
同理[p1][0]、[0][p2],邊際無法做 replace,建議獨立出來處理掉。
像這種有邊際問題的,可以增加表格範圍(邊際欄列),又或者先拿出來處理。如果把邊際的條件寫在一起,不僅不方便讀,也拖累效率。

Python(邊際欄列)

class Solution:
def minDistance(self, word1: str, word2: str) -> int:
from functools import lru_cache
rows, cols = len(word1)+1, len(word2)+1
@lru_cache(None)
def dfs(row, col):
if row == 0 or col == 0:
return max(row, col)
other = set()
if row > 0 or col > 0:
if word1[row-1] == word2[col-1]:
ans = dfs(row-1, col-1)
else:
if row > 0 and col > 0:
other.add(dfs(row-1, col-1))
if row > 0:
other.add(dfs(row-1, col))
if col > 0:
other.add(dfs(row, col-1))
ans = min(other)+1
return int(ans)

return dfs(rows-1, cols-1)

C#

public class Solution {
public int MinDistance(string word1, string word2) {
var memo = new int[word1.Length+1, word2.Length+1];
for(int i = 0; i < word1.Length+1; i++)
memo[i, 0] = i;
for(int j = 0; j < word2.Length+1; j++)
memo[0, j] = j;
for(int i = 1; i < word1.Length + 1; i++)
{
for(int j = 1; j < word2.Length + 1; j++)
{
if(word1[i-1] == word2[j-1])
memo[i, j] = memo[i-1, j-1];
else
{
memo[i, j] = Math.Min(memo[i-1, j], memo[i, j-1]);
memo[i, j] = Math.Min(memo[i, j], memo[i-1, j-1]) + 1;
}
}
}
return memo[word1.Length, word2.Length];
}
}

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重新排序。

Python

class Solution:
def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
import heapq
heap = []
for p in points:
heapq.heappush(heap, [p[0]**2+p[1]**2, p])
return [heapq.heappop(heap)[1] for _ in range(K)]

C#

public class Solution {
public int[][] KClosest(int[][] points, int K) {
var rank = new Dictionary>();
foreach(var p in points)
{
int dis = p[0]*p[0] + p[1]*p[1];
if(!(rank.ContainsKey(dis)))
rank[dis] = new List();
rank[dis].Add(p);
}
var sortedKeys = new List(rank.Keys);
sortedKeys.Sort();
int count = 0;
List result = new List();
foreach(int key in sortedKeys)
{
foreach(var p in rank[key])
{
result.Add(p);
count += 1;
if(count == K)
{
return result.ToArray();
}
}
}

return result.ToArray();
}
}

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。這樣算暴力破解法嗎?

Python

class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
memo = set()
this_memo = set()
nodeMap = defaultdict(list)

for c, p in prerequisites:
nodeMap[p].append(c)

def dfs(node):
if node in this_memo: return False
this_memo.add(node)
if not all([dfs(each) for each in nodeMap[node] if each not in memo]):
return False
memo.add(node)
return True

for root in range(numCourses):
if root not in memo and not dfs(root): return False

return True

C#

public class Solution {
public bool CanFinish(int numCourses, int[][] prerequisites) {
var safe_nodes = new HashSet();
var path = new HashSet();
var nodeMap = new Dictionary>();

for(int i = 0; i < numCourses; i++)
nodeMap[i] = new List();
foreach(var pair in prerequisites)
nodeMap[pair[1]].Add(pair[0]);

bool DFS(int node)
{
if(path.Contains(node)){return false;}
path.Add(node);
for(int i = 0; i < nodeMap[node].Count; i++)
if(!(safe_nodes.Contains(nodeMap[node][i])))
{
if(!(DFS(nodeMap[node][i]))){return false;}
}
safe_nodes.Add(node);
return true;
}

for(int i = 0; i < numCourses; i++)
{
if(!(safe_nodes.Contains(i)))
{
if (!DFS(i))
{
return false;
}
}
}

return true;
}
}

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。 

一旦進位,接下來的 1 個數,會是將前面的所有的 1 個數再加 1。可把前面視作最高位為 0 的版本,而進位是變成最高位為 1 的版本,
000
001 ( _ + 1)
010 (0的答案 + 1))
011 (1的答案 + 1))
100 (00的答案 + 1)
101 (01的答案 + 1)
110 (10的答案 + 1)
111 (11的答案 + 1)
接下來換奇偶方法。
若用長除法,數字不斷除以 2 ,最後可以用長除法的結果把該數字(10進位)轉換成 2 進位的型式。
若過程有餘數出現,相當於有二進位的某位有 1 的出現。
0 (長除法) 出現餘數 1 次數: 0
1 (長除法) 出現餘數 1 次數: 1
2 (長除法) 出現餘數 1 次數: 1
3 (長除法) 出現餘數 1 次數: 2
4 (長除法) 出現餘數 1 次數: 1
5 (長除法) 出現餘數 1 次數: 2
6 (長除法) 出現餘數 1 次數: 2
7 (長除法) 出現餘數 1 次數: 3
因為數字被除2之後是計算是之前有過的,也就是重複計算。
舉個例好懂:
像 7 / 2 = 3 … 1 , 後面還要拿 3 繼續長除法,但 3 的計算是出現過的,可以直接取 3 的答案,所以奇數就是整除2的答案+1,而偶數是整除2的答案。

Python(計數)

class Solution:
def countBits(self, num):
result = [0]
i = 0
while True:
for number in range(len(result)):
if i == num: return result
result.append(result[number] + 1)
i += 1

Python(奇偶)

class Solution:
def countBits(self, num: int) -> List[int]:
result = [0]
memo = {0:0}

def ones(number):
if number in memo: return memo[number]
ans = number % 2 + ones(number//2)
result.append(ans)
memo[number] = ans

for number in range(num+1):
ones(number)

return result

C#(奇偶)

public class Solution {
public int[] CountBits(int num) {
var result = new List();
result.Add(0);
for(int i = 1; i <= num; i++)
{
result.Add(result[i/2] + i % 2);
}
return result.ToArray();
}
}

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

有 N 個人(編號 1, 2, …, N),要依照 dislikes 去分成二組,在 dislikes 中,每組 [a, b] 代表該編號的二人不能在同一組。
回傳 True 如果可以順利分成二組,否則 False。
這題蠻有意思的,我第一念頭想到 backtrace 可以硬解,但太慢,我放棄用 DP 與 遞迴函數。
說到兩兩成對,就想到最近的表格式DP,這題一樣可以用表格式DP,最後用 DFS 的遞迴函數可以比 backtrace 快上不少。
只要表格上可以選成每列每欄都只有一組配對,那就有可行的分組,也就是True。
抛開遞迴,因為平面化的話,一有答案可以馬上回傳,而遞迴函數會一路收斂到最上方,才能停止。要是深度深,會多花上一些時間。
被自我侷限,得用別的方法來。

如果 [a,b] 是 [1, 3],代表 1, 3 不能同組。這個分組沒有順序,也就是 組1、組2 對調是沒差的。
但如果用人的思維去硬分,肯定要選一組當基準去分配,就會產生順序的問題。
[1,3],[2,4],[3,4],答案是 True 分成 [1,4],[2,3] 。但如果都選 組1 當基準,輪到 [2,4] 分配的時候,要是組別分成 [1,2],[3,4],
那下一個遇到分[3,4],就會無法依規則分,然後回傳 False 。
要是換個順序 [1,3],[3,4],[2,4],選基準去分,就會得到一組可行的,回傳 True。
[1,3] 後面接 [3,4] ,頭尾相接,可以想是一條討厭鏈,不同討厭鏈之間任意分配是沒關係的,但在同一條就該按順序來,如果可以串起來,就該優先分配。
依題目所限,dislikes 已經有排列過,所會先拿到討厭鏈的頭,想辦法後面依序先分配。
如果開頭是 [1,3] ,下一個要以 3 為開頭,[3, 4],再以 4 開頭,再以 2 開頭,這不是一個有固定順序的,但要把 N 個開頭都找過。
討厭鏈一定要順利分完,依討厭鏈去分配是很簡單的,二人不要在同一組,分不下去就一定是回傳 False。
首先全部人都先在 組1 ,如果 [a, b] 都在 組1,把 b 移到 組2。如果 [a,b] 都在 組2 ,即無法分,回傳 False。
(用這思考看看:N = 3, dislikes = [[1,2],[1,3],[2,3]])
要依照討厭鏈順序去分的方法︰
用一個 set(1,2,…,N) ,跟 stack,遇到數字就從 set 中取到 stack。若 stack 為空,就從 set 任取一個放入stack,直到把 set 取光。

Python

class Solution:
def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool:

memo = defaultdict(list)
for me, you in dislikes:
memo[me].append(you)
memo[you].append(me)

group1 = set(range(1,N+1))
group2 = set()
candidate = set(range(1, N+1))
stack = []

while candidate:
stack.append(candidate.pop())
while stack:
i = stack.pop(0)
for hate in memo[i]:
if i in group1 and hate in group1:
group1.discard(hate)
group2.add(hate)
elif i in group2 and hate in group2:
return False
if hate in candidate:
stack.append(hate)
candidate.discard(hate)

return True

C#

public class Solution {
public bool PossibleBipartition(int N, int[][] dislikes) {
var relation = new Dictionary>();
var group1 = new HashSet();
var group2 = new HashSet();
var candidate = new HashSet();
for(int i = 1; i <= N; i++)
{
relation[i] = new HashSet();
group1.Add(i);
candidate.Add(i);
}
for(int i = 0; i < dislikes.Length; i++)
{
relation[dislikes[i][0]].Add(dislikes[i][1]);
relation[dislikes[i][1]].Add(dislikes[i][0]);
}
var queue = new Queue();
while(candidate.Count > 0)
{
queue.Enqueue(candidate.First());
candidate.Remove(candidate.First());
while(queue.Count > 0)
{
int head = queue.Dequeue();
foreach(int node in relation[head])
{
if(group1.Contains(head) && group1.Contains(node))
{
group1.Remove(node);
group2.Add(node);
}
else if(group2.Contains(head) && group2.Contains(node))
{
return false;
}
if(candidate.Contains(node))
{
candidate.Remove(node);
queue.Enqueue(node);
}

}
}

}
return true;
}
}

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

給一個二進位串列,回傳 0 與 1 數量相等的連續子陣列的最長長度。
這次補寫 C# 版的 code。

C# 
public class Solution {
public int FindMaxLength(int[] nums) {
int result = 0;
int counter0 = 0;
var dp = new Dictionary();
dp.Add(0,-1);
for(int i = 0; i < nums.Length; i++)
{
counter0 += nums[i] == 0 ? 1 : -1;
if(dp.ContainsKey(counter0))
result = Math.Max(result, i - dp[counter0]);
else
dp[counter0] = i;
}
return result;
}
}