LeeShoWdian
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- type: slide --- <style> .reveal .slides { text-align: left; font-size:35px } </style> # Graph Theory & DSU ---- - Graph Introducion - Disjoint Set(Union-Find) - Minimum Spanning Tree (kruskal) --- ## 名詞介紹 (以下內容也都會在資工其他領域學習到 ---- - 圖(graph):由一些點與一些邊所組成,通常以 $G=(V,E)$ 表示 - 點(vertex):節點,通常以 $V$ 表示 - 邊(edge):連接兩點,通常以 $E$ 表示,$e=(u,v)$ 代表邊 $e$ 連接 $u,v$ 兩點,也就是 $u,v$ 為 $e$ 的端點 ---- ## 相關用語定義 - 權重(weight):指圖的點/邊附帶的數值 - 點數/邊數:點/邊的數量,通常記為 $V/E(n/m)$ - 有向邊:邊可以分為無向邊(雙向)與有向邊(單向) - 重邊(multiple edge):指兩點之間有多條邊連接 - 自環(self loop):指兩端為同一點的邊 $e=(u,u)$ - 度數(degree):一個點所連接的邊的數量,若是有向邊則又分為出度與入度 - 相鄰(adjacent):指兩個點間有無向邊相連 - 指向(consecutive):有向邊的起點"指向"終點 ---- ## 更多的定義 - 路徑(path):從起始點到目標點上所經過的所有點,路徑上的點/邊皆可重複 - 簡單路徑(track):點不重複的路徑 - 行跡(trace):邊不重複的路徑 - 迴路(circuit):邊不重複且起終點相同的路徑 - 環(cycle):點不重複且起終點相同的路徑 - 連通(connected):$u,v$ 連通若且唯若存在 $u$ 到 $v$ 或 $v$ 到 $u$ 的路徑,一群點連通代表這群點兩兩連通 ---- ## 各種圖的定義 - 簡單圖(simple graph):沒有重邊與自環的圖 - 無向圖(undirected graph):由無向邊組成的圖 - 有向圖(directed graph):由有向邊組成的圖 - 連通圖(connected graph):任兩點皆連通的圖 - 樹(tree):無向無環連通圖(其實也可以有向) - 森林(forest):只由樹組成的圖。按照定義,一棵樹也是森林 - 完全圖(complete graph):任兩點都相鄰的圖 ---- ## 圖之間關係的定義 - 子圖(subgraph):邊/點皆為原圖的子集 - 補圖(complement graph):若兩張圖點集相同,邊集互斥且聯集為完全圖,則兩張圖互為補圖 - 同構(isomorphic):不考慮編號長的完全相同的圖 - 生成樹(spanning tree):點集相同且為樹的子圖 ---- ## 對於有根樹的定義 - 父節點(parent node):對於除了根以外的每個節點,定義為從該節點到根路徑上的第二個節點。 根節點沒有父節點 - 祖先(ancestor):一個節點到根節點的路徑上,除了它本身外的節點。 根節點的祖先集合為空 - 後代(descendant):子節點和子節點的後代 - 子節點(child node):如果 $u$ 是 $v$ 的父親,那麽 $v$ 是 $u$ 的子節點 - 深度(depth):到根節點的路徑上的邊數 - 高度(height):所有節點的深度的最大值 - 子樹(subtree):刪掉與父親相連的邊後,該節點所在的子圖 --- ## Disjoint Set (Union-Find) (DSU) 並查集,又稱不相交集資料結構 ---- 並查集是一種樹狀結構 <!-- 大概說一下樹是什麼 --> 處理集合問題,主要有以下兩個操作 * 查詢元素所在集合(find) * 合併兩個集合(union) ---- 並查集只需要用一個長度為 $n$ 的陣列即可, 陣列內第 $i$ 格存的值為第 $i$ 個節點的父節點編號 ```cpp= int f[n]; f[3] // 節點 3 的父節點編號 f[5] // 節點 5 的父節點編號 ``` ---- ### 根節點 如果一個點為根節點,他的父節點為自己 (f[x] == x) 以下圖為例, 1、6 為根節點 ![](https://i.imgur.com/BeMQ981.png =450x) ---- ## 判斷集合 定義兩個相異元素如果屬於同一個集合,則兩個元素會在並查集的同一棵樹上 ---- 如何判斷在同一棵樹上? ---- ## find 函數 find 函數會找到某個節點 x 的根節點 如果兩個點的根節點相同,代表在同一棵樹上, 也就是屬於同一個集合 ``` find(x) == find(y); ``` 則 x 與 y 所屬同一個集合 ---- ## find 函數 ```cpp= int find(int x){ if ( x == f[x] ) // 如果當前節點為 f[x]==x return x; // 則為根節點 return find(f[x]); } ``` ---- ## union 函數 如果有兩個節點 a, b 所在的集合想要合併成一個集合 做法為找到兩集合的根節點 將其中一個集合的根節點連到另外一個的根節點 以下圖為例 ![](https://i.imgur.com/BeMQ981.png =400x) ---- ## union 函數 合併 2 所在的的集合 跟 7 所在的集合 則先找到 2 的根節點 =1, 7 的根節點 =6 將其中一個根節點的父節點設為另一個根節點 ![](https://i.imgur.com/uNrfG79.png =350x) 則 7 所在的集合所有元素的根節點都會變成 1 ---- ## union 函數 這邊合併的函數名稱不使用 因為 union 為關鍵字 (撞名內建函數) 因此名稱改用 merge ```cpp= void merge(int x,int y) // 合併 x, y 所在集合 { x = find(x); // x 找到其根節點 y = find(y); // y 找到其根節點 if(x != y) // 如果 x,y 原本就屬於同一個集合則不需合併 f[y]=x; // 將其中一個根節點連到另一個 } ``` ---- ## 初始化 一開始每個元素皆屬於屬於不同集合 因此會將節點指向自己,因為這時候每個元素都是根節點 ```cpp= int f[N]; void init(){ for(int i=0;i<n;i++) f[i] = i; // 將每個元素根節點設為自己 //或者也可以使用 iota(f, f+n, 0); 代替迴圈 } ``` <!-- --> ---- ## 完整程式碼 ```cpp= void init(){ for(int i=0;i<n;i++) f[i]=i; } int find(int x){ if ( x == f[x] ) // 如果當前節點為 f[x]==x return x; // 則為根節點 return find(f[x]); // 否則繼續往父節點方向找根節點 } void merge(int x,int y){ x=find(x), y=find(y); if(x!=y) f[y]=x; } ``` ---- ## 優化 在最差的情況下 合併後的集合的樹形有可能會變成一條鏈 $\rightarrow$ find() 複雜度退化成 $O(N)$ ![](https://i.imgur.com/38mxLz3.png) 以上圖為例,find(5) 需要跑完全部節點才能找到根節點 ---- ## 啟發式合併 ### Union by Rank 記錄每棵樹的大小,並在每次合併的時候,將小的集合合併到大的集合。 ---- ### 做法 宣告 sz 陣列,紀錄每個節點的為根的集合大小 在初始化的時候將每個集合大小 sz[i] 都設成 1 ```cpp= void init(){ for(int i = 0; i < n; i++){ f[i] = i; sz[i] = 1; } } ``` ---- 當遇到合併操作時,將兩個集合合併成一個時 把小的集合往大的集合合併 ---- 找到根節點,根節點儲存整個集合的資訊 合併時,把小的集合的資訊加給大的集合 ```cpp= int f[N], sz[N]; void merge(int x, int y) { x = find(x), y = find(y); if(x==y) return; if(sz[x] < sz[y]) swap(x, y); // 將 x 變成大的 sz[x] += sz[y]; // 把集合 y 的大小加到集合 x f[y] = x; } ``` ---- ### 分析 如果是原本的做法 假設有 $n$ 個節點,合併 $n - 1$ 次 每次合併都由大小為 $i$ 的合併到大小為 $1$ 的,樹就會長成最差的情況(鏈) 每次查詢會退化到 $O(n)$ ![](https://i.imgur.com/38mxLz3.png) ---- ### 分析 合併兩棵樹高不同的樹 ![](https://hackmd.io/_uploads/SkPPcX4-a.png) ---- 兩個不同的集合合併,如果把樹高比較矮的連往比較高的, 合併後的樹高不會改變 ![](https://hackmd.io/_uploads/rJ9NjmEba.png) ---- 而如果合併的兩個集合樹高相同, 或者高的往矮的合併,則合併後樹高會+1 ![](https://hackmd.io/_uploads/Hyy_3QV-6.png =x400) ![](https://hackmd.io/_uploads/Bkio3QVZa.png =x340) ---- 每次把小的合併到大的方法, 稱之為啟發式合併 使用此方法的合併的樹,在最差情況下 為每次合併時,兩棵樹的樹高都相同 ---- 在樹高相同的情況下,會發現每次樹高要 + 1 所需節點數量會變 2 倍 ![](https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExZnZremlsb3U0azFhM2I0YnRmNjNpNWRyNGZxeG1iaTE1b3NlbHJ3NiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/HEp4DH9XYsUXwozTtR/giphy.gif) 因此 $n$ 個節點時,使用啟發式合併樹高最高為 $O(\log n)$ ---- ## 優化 2 ### 路徑壓縮 ---- ## 路徑壓縮 在每次 find() 的時候 把經過節點的父節點 全部設成根節點 ```cpp= int find(int x){ if(f[x] == x) return x; f[x] = find(f[x]); // 直接將 x 的父節點設成根節點 return f[x]; } ``` <!--可以再模擬一下 f[x] = find(f[x]) 的地方 --> ---- 呼叫 find(5) 會經過節點 5 4 3 2 1 將中間每個節點的父節點直接設為根節點 ```cpp= find(5) ``` <div style="position: absolute; right: 70%; top:100%;"> ![](https://i.imgur.com/rlVcpOJ.png) </div> <div style="position: absolute; right: 60%; top:200%;"> $\rightarrow$ </div> <div style="position: absolute; right: 10%; top:150%;"> ![](https://i.imgur.com/KfwSYzi.png) </div> 修改後,之後詢問這些節點時,只需要 O(1) 就會找到根節點 ---- 使用啟發式合併+路徑壓縮 能使得單次操作複雜度降到 $O(\alpha(N))$ $O(\alpha(N))$ 趨近於 $O(1)$ ($\alpha( 2^{2^{10^{19729}}} ) = 5$) 因此並查集操作複雜度 幾乎是常數時間 ---- ## 完整程式碼 要特別注意合併時,如果兩個元素本來就在同一個集合 要直接回傳,不要重複加到 sz (15行) ```cpp= int f[N], sz[N]; void init(){ for(int i=0;i<n;i++) { f[i] = i; sz[i] = 1; } } int find(int x){ if ( x == f[x] ) // 如果當前節點為 f[x]==x return x; // 則為根節點 f[x] = find(f[x]); return f[x]; } void merge(int x, int y) { x = find(x), y = find(y); if(x==y) return; if(sz[x] < sz[y]) swap(x, y); // 將 x 變成大的 sz[x] += sz[y]; f[y] = x; } ``` <!-- --> ---- ## 紀錄集合資訊 如果題目為給定 $n$ 個元素,每次操作為以下其中一種 : 1. 查詢元素 $x$ 所在的集合有幾個元素 2. 合併元素 $x, y$ 分別所在的集合 ---- ## 查詢集合大小 會發現集合大小在做啟發式合併時, 就已經記錄過此資訊 sz[] 了 查詢元素 $x$ 所在的集合大小只需要找到 $x$ 的集合的根節點, ```cpp= sz[find(x)] ``` 即為所在的集合的大小 ---- 如果題目需要求其他資訊,如集合內編號最小/大值等等, 則多為一個陣列 mn[]/mx[] 之類維護每個集合內的資訊 合併集合時,則把兩個集合內的資訊合併 ```cpp= void merge(int x){ x = find(x); y = find(y); if(x == y) return; if(sz[x] < sz[y]) swap(x, y); mn[x] = min(mn[x], mn[y]); mx[x] = max(mx[x], mx[y]); f[y] = x; } ``` ---- ## 相異集合的數量 要找總共有幾個集合, 可以找總共有幾個根節點即可 ```cpp= int countComponent(){ int ret = 0; for(int i = 0; i < n; i++){ if(find(i) == i) ret++; } return ret; } ``` ---- ## 並查集在圖論上的意義 ---- 給一張 $n$ 個節點的圖,一開始有 $m$ 條邊, 接下來有 $k$ 次操作,每次操作為新增一條邊, 每次操作完輸出最大的連通塊大小 ? - $n, m, k\le 10^5$ ---- 下圖為例,黑色為一開始的邊, 紅色為依序要加入的邊 ![](https://hackmd.io/_uploads/HyB1Qw4ZT.png) 當加完 1 號邊之後,最大連通塊大小為 5 當加完 2 號邊之後,最大連通塊大小為 5 當加完 3 號邊之後,最大連通塊大小為 6 ---- ## 連通塊意義 在同一個連通塊中,在並查集中代表在同一個集合。 因此我們可以用並查集維護整張圖誰跟誰連通。 在圖上邊 (x, y) ,相對於 merge(x, y) 操作 ---- ## 並查集應用 ---- ### Almost-union-find 題序:一個有三種操作的並查集 1. Union(x, y): 把元素 x , y 分別所在集合合併成同一集合 2. Move(x, y): 將元素 x 移動到元素 y 所在的集合 3. Return(x):詢問元素 x 所在的集合內元素個數與元素編號總和 - $1\le n, m\le 10^5$ ---- 可以發現操作 1、3 都是正常的並查集操作 只需要在 merge 的時候維護總和(sum)跟大小(sz) ```cpp void merge(int x, int y){ x = find(x); y = find(y); if(x == y) return; if(sz[x] < sz[y]) swap(x, y); sz[x] += sz[y]; num[x] += num[y]; f[y] = x; } ``` 而操作 2 比較不一樣,需要做到刪除的操作。 ---- 我們可以從兩種情況來看刪除 1. 移除的是葉節點 2. 移除的不是葉節點 ---- ### 1. 移除的是葉節點 在這個情況下,我們只要將根節點記錄大小跟總和的變數把這個節點減掉即可。 ---- ### 2. 移除的不是葉節點 感覺很麻煩,我也不會 ---- ### 捨棄原本的節點 每次詢問,需要回傳集合內元素個數與元素編號總和 把元素 $x$ 移除,其實只需把 $x$ 儲存在集合內的資訊移除即可 ```cpp= void remove(int x){ root = find(x); sz[root]--; sum[root] -= x; } ``` 移除後,原本的節點就不重要了。 ---- ### 加入到新的集合中 要將 $x$ 加入新集合,可以先給 $x$ 一個新的編號 $newid$ 來代表數字 $x$ 因此我們需要開一個陣列 id[] 維護每個元素當前代表的編號, 並初始化新編號的 sz[], num[] ---- ### 換新編號 & 合併 x 與 y 的集合 ```cpp void add(int x){ f[id[x]] = id[x] = newid++; sz[id[x]] = 1; sum[id[x]] = x; } remove(x); add(x); merge(id[x], id[y]); ``` ---- ## 反著做回來的題目 ---- ## 例題 題序:給一張 $n$ 個點、$m$ 條邊的圖, <!-- 講圖是什麼--> 要做 $q$ 次操作,每次將一條邊拔掉 求每次拔掉後還有多少塊連通塊?<!-- 這邊要解釋什麼是連通塊--> 範圍: $n, m, q\le 10^6$ <!-- 不確定範圍要多少 --> ---- ### 做法 每次刪除後看有幾個連通塊需要花 $O(n + m)$ 的時間 而我們有 $q$ 次詢問,很明顯不能這樣做 那這題要怎麼用並查集維護連通塊呢? ---- ### 做法 如果將刪除變成合併,就跟原本 3-34 的題目類似了? 因此我們可以將操作反過來做 從後往前做,可以發現這樣子操作就從刪除變成合併了! --- ## 最小生成樹 :evergreen_tree: 「生成樹」。從一張圖取出一棵樹,包含圖上所有點。可能有許多種。 而最小生成樹是其中所有的生成樹中,權重總和最小的。 <div style="position: absolute; right: 60%; top:100%;"> ![](https://i.imgur.com/UdEIk6Z.png =500x) </div> <div style="position: absolute; left: 57%; top:100%;"> ![](https://i.imgur.com/Tn3xRfH.png =450x) </div> ---- ## Kruskal' algorithm greedy method , 將所有邊照權重大小排序,從權重小的邊開始窮舉,依序窮舉到大, 當邊兩側的節點原本不連通就加邊,否則就捨棄這條邊 這個做法是對的,但為什麼是對的 🤔 ---- ## 證明 生成樹的一個性質: :::info 對於兩個生成樹 $T_1$ 與 $T_2$ 和一條邊 $e \in T_1 \backslash T_2$ 存在 $e_2 \in T_2 \backslash T_1$ 使得 $(T_2\backslash \{e_2\})\cup \{e_1\}$ 依然是生成樹。 ::: 從 $T_1$ 拿出一條邊加入 $T_2$ 後,$T_2$ 會形成一個環,此時移除環上任一邊即可讓 $T_2$ 有 $n-1$ 條邊連通,這個時候 $T_2$ 也還會是一棵樹。 ---- Kruskal' algorithm 的證明: 令Kruskal演算法找到的生成樹為 $T$,而最小生成樹為 $T^*$ 如果有多個最佳解,令 $T^*$ 為與 $T$ 交集最大的一個。 如果 $T=T^*$ 就結束了,否則,令 $e_i$ 是只出現在 $T$ 的邊且編號最小 根據上面的性質,存在 $e_j \in T^* \backslash T$ 使得 $T^*$ 把 $e_j$ 換出去再把 $e_i$ 放進來仍是一棵生成樹。 ---- 假如 $i < j$,那 $e_i$ 的權重 $\leq e_j$ 的權重。但由於$T^*$是最小生成樹,這樣做出來的 $T$ 的權重會跟 $T^*$ 一樣(或更小),但是與$T$的交集比 $T^*$ 大,矛盾。 假如 $i > j$,由於比 $j$ 前面的邊都在 $T$ 與 $T^*$ 中,根據 Kruskal 演算法的特性,在遇到 $e_j$ 時就會把 $e_j$ 加入 $T$ 中了,矛盾。 故得證 $T=T^*$ ---- <div style="background-color:white"> ![image alt](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/MST_kruskal_en.gif/600px-MST_kruskal_en.gif) </div> ---- ## 加入邊 使用並查集,一開始所有點都沒連任何邊 因此所有點都屬於自己的集合 當兩個點有連邊,代表他們屬於同一個集合 因此可以用並查集判斷,判斷是否已經為同一個集合 ---- ## 結構 使用 struct 儲存邊 (邊的兩個端點 u, v、權重 w) 多載 < 小於運算子,使用邊權重比較兩條邊的大小關係 ```cpp= struct Edge{ int u, v, w; // 點 u 連到點 v 並且邊權為 w bool operator<(const Edge& rhs){ return w < rhs.w; //兩條邊比較大小用邊權比較 } }; Edge graph[200005]; // 宣告 Edge 型態的陣列 graph ``` ---- ## 程式碼 將邊照大小依序嘗試加入圖中, 如果邊的兩點未連通,則連通兩點 ```cpp= sort(graph,graph+m); // 將邊照大小排序 int ans=0; for(int i=0;i<m;i++){ //>:) if(find(graph[i].u) != find(graph[i].v)){ // 如果兩點未聯通 merge(graph[i].u,graph[i].v); // 將兩點設成同一個集合 ans += graph[i].w; // 權重加進答案 if(sz[find(graph[i].u)]==n) break; //當並查集大小等價於樹內點的數量 } } cout<<ans<<endl; ``` ---- ## 瓶頸生成樹 令 $T_i$ 是這張圖的所有生成樹,會有樹 $T^*$ 它的最大邊權值為所有 $T_i$ 的最小 性質: 最小生成樹是瓶頸生成樹的充分不必要條件。 即最小生成樹一定是瓶頸生成樹,而瓶頸生成樹不一定是最小生成樹。 ---- ## 複雜度分析 依照權重排序所有邊 $O(M\log M)$ 窮舉每條邊加入 $O(M\cdot \alpha (N))$ -> 總複雜度 $O(M\log M)$ --- ## Question Time and Practice https://vjudge.net/contest/661401

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully