# Graph + Social Netwrok Analyze ## Graph ![](https://hackmd.io/_uploads/S1WNtggzp.png) ![](https://hackmd.io/_uploads/ry4LYegMp.png) ![](https://hackmd.io/_uploads/SyZDFxlzp.png) ## Complete Graph(完整圖) ![](https://hackmd.io/_uploads/HkMBEBzG6.png) ![](https://hackmd.io/_uploads/B1AcYggM6.png) ## Subgraph(子圖) ![](https://hackmd.io/_uploads/ryc2tleM6.png) ## Path (路徑) ![](https://hackmd.io/_uploads/BJfRYgxG6.png) ## Simple Path (簡單路徑) ![](https://hackmd.io/_uploads/Sk2y5lxGT.png) ## Cycle (迴圈) ![](https://hackmd.io/_uploads/HJuW5lgG6.png) ## Connected (連通) ![](https://hackmd.io/_uploads/B1hEjlgzT.png) - Tree也是connected、no cycle、edge = (n-1)條 [原因]: Tree 的 root 到每個node都有路徑 ![](https://hackmd.io/_uploads/rk7Ujllfp.png) ## 分支度(Degree) ![](https://hackmd.io/_uploads/HylcjllzT.png) ![](https://hackmd.io/_uploads/ryQojggG6.png) ## Degree 與 Edge 間的關係:公式 ![](https://hackmd.io/_uploads/HyfAilxzT.png) ![](https://hackmd.io/_uploads/r1XJ3gxzT.png) ## Adjacency Matrix (相鄰矩陣) #### 無向圖-Adjacency Matrix,必對稱 ![](https://hackmd.io/_uploads/BJRQ3gxG6.png) ![](https://hackmd.io/_uploads/S1W93llG6.png) #### 有向圖-Adjacency Matrix,不一定對稱 ![](https://hackmd.io/_uploads/HksanxxM6.png) ![](https://hackmd.io/_uploads/B1YyTllGa.png) ## Adjacent list ### 無向圖的Adjacent list ![](https://hackmd.io/_uploads/HkS9eqXzp.png) ![](https://hackmd.io/_uploads/ryj2e97GT.png) ![](https://hackmd.io/_uploads/SyqRl5mzp.png) ### 有向圖的Adjacent list ![](https://hackmd.io/_uploads/B131-qQfT.png) ![](https://hackmd.io/_uploads/rkXmWcQGT.png) ![](https://hackmd.io/_uploads/B1Cubq7GT.png) #### adjacent matrix v.s. adjacent list ![](https://hackmd.io/_uploads/HJ2Y-qXMT.png) # Graph Traversal ![](https://hackmd.io/_uploads/HJ52awzz6.png =90%x) ### Tree 的 edge 種類 >邊的分類(Edge classification) >1. Tree edge : 若可以經由一條邊走訪到新的節點,則稱該邊為Tree edge >2. Foward edge : 可以直接存取到子孫的邊 >3. Backward edge : 可以直接存取到祖父的邊 >4. Cross edge : 連接了兩棵子樹的邊 > | 追蹤方式 | 有向圖 DFS | 無向圖 DFS | 有向圖 BFS | 無向圖 BFS | | -------- | ---------- | --------- | --- | --- | | 追蹤的edge種類 | Tree edge、Back edge、Foward edge、Cross edge |Tree edge、Back edge | Tree edge、Back edge、Cross edge |Tree edge、Cross edge | ## DFS (Depth First Search,深度先搜尋) >DFS顧名思義,就是盡量的深入遍歷整張圖。找到一個節點v,遍歷所有v能夠到達的節點,一但v能夠到達的節點都已經被發現,則回朔到v的前驅節點,也就是v的父節點,接著以該節點作為出發的節點,繼續尋找能夠到達的節點,不斷重複這樣的過程,直到整張圖被遍歷 ![](https://hackmd.io/_uploads/SkXC3vGGp.png =90%x) ![](https://hackmd.io/_uploads/H1e9rPzGT.png =50%x) >程式碼走訪完結果 = A B D E F C #### Stack實作DFS ``` def dfs(start_vertex, graph_dict): visited = set() # 使用集合來保存已訪問的節點 stack = [start_vertex] # python的list可做為stack while stack: # 當堆疊不為空時 vertex = stack.pop() # pop()掉最上面的node if vertex is not visited: # 如果這個節點還未訪問 print(vertex, end=" ") # 印出來 visited.add(vertex) # 並標記加入到訪問過的set中 stack.extend(graph_dict[vertex]) # 先對該node的鄰居全都丟入stack,後續會再處理 # 利用list.extend()用於將一個list(或任何可迭代對象)的所有元素添加到另一個list的末端 # 因為是while loop 再回去前面一一pop 檢視是否visited過了 # 測試 dfs_stack 函數 graph_dict = { 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E'] } dfs('A', graph_dict) ``` --- #### Recrusive 版 DFS ``` visited = set() # 建立一個初始化集合 graph_dict = { 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E'] } def dfs(vertex): # vertex為目標走訪值 global visited, graph_dict # 當一個區域變數要修改全域變數的值時,要宣告為Global variable才能真正修改 if vertex in visited: # 如果當前節點已經被訪問過(即它已經在 visited 集合中) return visited # 則回傳 visited 集合 visited.add(vertex) # 若當前節點尚未被訪問,則將其添加到 visited 集合中 print(vertex, end=" ") # 印出該點 # 要去拜訪vertex的鄰居們,利用vertex去比對dict的key,當有找到時就回傳該列表 for neighbor in graph_dict[vertex]: # neighbor會走訪該列表內的node if neighbor not in visited: # 若該點未被拜訪過,則代表它不在visited集合內 dfs(neighbor) # 對它做 dfs 去拜訪他 return visited # 測試 dfs 函數 dfs('A') ``` ## BFS (Breadth First Search, 廣度先搜尋) >BFS是用來遍歷一張圖的最簡單演算法,也是很多在圖論演算法的原型,許多演算法都是基於BFS,像是Prim最小生成樹,Dijkstra演算法等等。 > >給定一張圖G(V,E),和一個節點s,BFS可以走訪s能夠到達的所有節點v,並且能夠紀錄s到各個節點的最少邊數,也就是最短距離,同時會產生出一棵BST Tree。這個數會以s作為樹的根結點,並且包含s能夠到達的所有節點v。BST可以用在有向圖,也可以用在無向圖中。 > - BFS在undirected graph中,邊的種類只有tree 及 cross edge >- 在一個unweighted,undirected graph(無加權無向圖)中,只有BFS 可以找到shortest path > ![](https://hackmd.io/_uploads/r1ulpDfGT.png =90%x) ``` adjacency_list = { 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B'], 'F': ['C'] } def BFS(start_vertex): visited = set() queue = [start_vertex] # 利用list可實現Queue while queue: vertex = queue.pop(0) # FIFO 利用pop(0)取出最前端元素 if vertex not in visited: print(vertex, end=" ") visited.add(vertex) queue.extend( neighbor for neighbor in adjacency_list[vertex] if neighbor not in visited) BFS('A') ``` ## AOV Network (Activity On Vertex) ![](https://hackmd.io/_uploads/B1vvz9Qza.png) ## Topological Order >給定一個不具備cycle的AOV network,則至少可以找到>=1組的頂點拜訪順序,滿足若在AOV network中,頂點i有路徑到達頂點j,則在此拜訪順序中,i必定出現在j之前 >常用於AOV網路來確保所有的任務都按照正確的先後順序進行 ![](https://hackmd.io/_uploads/Sy8qzc7zT.png) ![](https://hackmd.io/_uploads/BkP3M5mG6.png) ############################################### ![](https://hackmd.io/_uploads/BJOafq7f6.png) ![](https://hackmd.io/_uploads/BJt1Q5mf6.png) ![](https://hackmd.io/_uploads/SJAxX9XMa.png) ![](https://hackmd.io/_uploads/H1hWX5QGT.png) ### Topology Sort > 1. 拓撲排序是有向非循環圖(DAG)的節點的線性排序,使得對於圖中的每一條有向邊(u, v),節點u都出現在節點v之前。 > > 2. 拓撲排序的一個常見算法是使用DFS。當DFS訪問一個節點並完成訪問其所有鄰居後,該節點被推入一個堆疊。因此,最後一個被訪問的節點是最先被推入堆疊的,這實際上給了我們一個拓撲排序 > 3. 最重要的一點:只有directed acyclic graph(DAG)的Topological Sort(拓撲排序)才有意義。 ``` import networkx as nx import matplotlib.pyplot as plt from collections import defaultdict # 有包好的資料結構 class Graph: def __init__(self, vertices): # 類別初始化 self.adjacencyList = defaultdict(list) # 相鄰列表,用於儲存邊 self.vertices = vertices #  儲存圖的節點數 self.visited = [False] * vertices # 使用bool值=False,來記錄DFS所訪問過的node self.stack = [] # 儲存Topology Sort完的結果 self.G = nx.DiGraph() # 是 networkx 的有向圖物件,將用它來畫圖 def createEdge(self, u, v): # 目的是在圖中添加一條邊,從節點 u 指向節點 v # 將node v 添加到node u 的adjacent list中。代表圖中會存在一條從 u 到 v 的邊 self.adjacencyList[u].append(v) self.G.add_edge(u, v) #  視覺化用,將邊(u, v)添加到 networkx 的圖中 def dfs(self, v): # 先定義DFS,等等要給Topology Sort使用 self.visited[v] = True # 將拜訪的該點先設為True for i in self.adjacencyList[v]: # 遍歷該點v的所有相鄰節點 if not self.visited[i]: # 若有相鄰節點未被訪問 self.dfs(i) # Recrusive,訪問他! self.stack.insert(0, v) # 使用模仿stack 的行為,但透過insert(0, v)以確保最後完成DFS的節點在拓撲排序中的位置是正確的。如果使用 append,那麼在返回拓撲排序結果之前,我們還需要反轉這個列表 # 使用 self.stack.insert(0, v),會將節點 v 放在 "堆疊" 的最底部(也就是list的開頭)。隨著更多的節點完成DFS,它們會持續被放到這個 "堆疊" 的最底部。 # 所以,最後當DFS完成,self.stack 的第一個元素(也就是最底部)是最後一個完成DFS的節點,而最後一個元素(也就是最頂部)是第一個完成DFS的節點。 # 這是拓撲排序的核心步驟,因為最依賴於所有其他節點的節點,應該先被處理。所以把它插入到list前端 def topologicalSort(self): for i in range(self.vertices): # 遍歷Graph中所有點 if not self.visited[i]: # 檢查節點 i 是否已被訪問 self.dfs(i) # 如果還沒有,則執行 DFS return self.stack # 返回結果堆疊,這是拓撲排序的結果。 def drawGraph(self): pos = nx.spring_layout(self.G) nx.draw(self.G, pos, with_labels=True, node_size=700, node_color="skyblue", font_size=15, font_weight='bold', width=2.0, edge_color="gray") plt.title("Directed Acyclic Graph (DAG)") plt.show() # 測試 G = Graph(6) G.createEdge(0, 1) G.createEdge(0, 2) G.createEdge(1, 3) G.createEdge(1, 5) G.createEdge(2, 3) G.createEdge(2, 5) G.createEdge(3, 4) G.createEdge(5, 4) print("Topological Sort Order:", G.topologicalSort()) G.drawGraph() ``` ![](https://hackmd.io/_uploads/Sy8NBLHz6.png =60%x) >>輸出 :Topological Sort Order: [0, 2, 1, 5, 3, 4] >> >>[解釋] >>從In-degree=0開始,每個節點的排序位置都在其父節點之後。 >>結果不唯一,可能會得到不同的結果,例如[0, 1, 2, 5, 3, 4] ## AOE Network (Activity On Edge) ★★★(資管常用) ![](https://hackmd.io/_uploads/SkUDmcQfT.png) ![](https://hackmd.io/_uploads/SJ6um57f6.png) ![](https://hackmd.io/_uploads/BJsYm5QfT.png) EX. ![](https://hackmd.io/_uploads/ByybYqXf6.png) ![](https://hackmd.io/_uploads/Hy8Qtq7Ma.png) ![](https://hackmd.io/_uploads/ryDLJh7fT.png) ### Articulation Point(關節點 or 切點) >在一個connected無向圖中,若將某個頂點及其他連結的邊刪除後,剩下的Graph會變成unconnected > ![](https://hackmd.io/_uploads/BkS8ju9Mp.png =70%x) ### Biconnected Graph (二連通圖) >一個不具有Articulation Point 的 connected undirected grapg 即是。 > >特點: 斷了一條還是有其他路徑可到,不會造成不連通。 >應用: 通訊網路布局(不想因某個server 或基地台掛了,而造成通訊中斷) ![](https://hackmd.io/_uploads/r1912ucf6.png =30%x) ### Biconnected Component (二連通子圖) > 令G=(V,E)是connected無向圖,令G'是G的一個子圖,且滿足: > 1. G'是Biconnected Graph (沒有關節點) > 2. G'是Max component (代表無其他子圖可包含G',且此子圖是Biconnected) ![](https://hackmd.io/_uploads/r1HFT_9Ma.png =70%x) EX. ![](https://hackmd.io/_uploads/H1yYCuqzp.jpg) ### Vertex Cover(頂點覆蓋) >又稱node cover。Node集合之所有連結的邊會涵蓋(等於)Graph所有的邊集合 >![image.png](https://hackmd.io/_uploads/ryAAOE77p.png) ### Minimum vertex cover >採用最少頂點數的vertex cover。是NP-Hard Problem >![image.png](https://hackmd.io/_uploads/BkfwYV7X6.png) --- ### Bipartite graph (二裂圖) ![image.png](https://hackmd.io/_uploads/SkF2E1G7T.png) >Graph中vertex集合會分解成兩個disjoint set,使得在兩個集合中都沒有原先Graph中互相相鄰的頂點 >著色問題:給兩個顏色,相鄰頂點必不同色 >A bipartite graph must be two-colorable >Bipartite graphs are equivalent to two-colorable graphs. ### Clique >是某無向圖的子圖,且子圖為Complete Graph(具有最多邊數的子圖) >![S__3072019.jpg](https://hackmd.io/_uploads/SkOBrEDQ6.jpg) ![螢幕擷取畫面 2023-11-04 110607.png](https://hackmd.io/_uploads/r1guIDN7X6.png) ![螢幕擷取畫面 2023-11-04 110728.png](https://hackmd.io/_uploads/BJWO8PV7Qa.png) ![螢幕擷取畫面 2023-11-04 110740.png](https://hackmd.io/_uploads/Syx_LPE7ma.png) ![螢幕擷取畫面 2023-11-04 110800.png](https://hackmd.io/_uploads/SJ_IP4mXa.png) ![螢幕擷取畫面 2023-11-04 110848.png](https://hackmd.io/_uploads/B1_LPNXXT.png) ![螢幕擷取畫面 2023-11-04 110954.png](https://hackmd.io/_uploads/Sye_Lw47Qp.png) ![螢幕擷取畫面 2023-11-04 111008.png](https://hackmd.io/_uploads/B1_UwEXm6.png) ![螢幕擷取畫面 2023-11-04 111024.png](https://hackmd.io/_uploads/HJxdUw47Qa.png) ![螢幕擷取畫面 2023-11-04 111030.png](https://hackmd.io/_uploads/BkuLv4Q7a.png) ![螢幕擷取畫面 2023-11-04 111116.png](https://hackmd.io/_uploads/BJuUDNQm6.png) ![螢幕擷取畫面 2023-11-04 111152.png](https://hackmd.io/_uploads/BkOLv4Q7a.png) ![螢幕擷取畫面 2023-11-04 111220.png](https://hackmd.io/_uploads/H1gu8PE77T.png) ![螢幕擷取畫面 2023-11-04 111239.png](https://hackmd.io/_uploads/rJZuUwN7X6.png) ![螢幕擷取畫面 2023-11-04 111258.png](https://hackmd.io/_uploads/B1gqANX7T.png) ![螢幕擷取畫面 2023-11-04 111324.png](https://hackmd.io/_uploads/r1uLw47Q6.png) ![螢幕擷取畫面 2023-11-04 111333.png](https://hackmd.io/_uploads/H18dCNX7a.png) > k代表可以允許少幾個連結 ![螢幕擷取畫面 2023-11-04 114931.png](https://hackmd.io/_uploads/H1gzZH7Xp.png) ![螢幕擷取畫面 2023-11-04 114938.png](https://hackmd.io/_uploads/rkez-S776.png) ![螢幕擷取畫面 2023-11-04 114956.png](https://hackmd.io/_uploads/r1xeGWrQmp.png) ![螢幕擷取畫面 2023-11-04 115016.png](https://hackmd.io/_uploads/HkeGbSQXa.png) ![螢幕擷取畫面 2023-11-04 115034.png](https://hackmd.io/_uploads/S1xGZHm7p.png) >當節點數量呈子數級成長時,節點之前平均距離的成長是呈線性的 ![螢幕擷取畫面 2023-11-04 120253.png](https://hackmd.io/_uploads/BkSS7BXmT.png) ### 如何找 Articulation Points? 難:Leetcode hard ★★★★★ 1. 對Graph實施DFS,求出每個頂點的dfn(DFS number,初值可以從0 or 1開始) 2. 畫出DFS spanning tree, 也標示出back edges 3. 求出每個頂點v的low值 >low(x) = min{ dfn(x) , dfn(w) } //dfn(w)為x之後的所有後代子孫,若有後退邊可透過他往回走,取出較小的DFS值 4. 開始判斷。規則: >1. If x 是 DFS spanning Tree的root: >>如果x的子節點數(degree)>1,則x是articulation point >2. If x 不是root,也不是leaf: >>則針對x的任一個子點W, if low(W) >= dfn(x)成立,則x是articulation point >3. If x 不是root,是leaf: >>絕對不是articulation point >> ![](https://hackmd.io/_uploads/rkrqTIsfa.jpg) Code: https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ ## Social Network data 用來處理關係間的資料 ![](https://hackmd.io/_uploads/ByXLKpyfp.png) 在基本的鄰接矩陣中,無權重的圖用0和1表示節點間是否存在邊。其中1表示兩個節點之間存在邊,0表示不存在。 - 若是加權圖(每條邊都有一個相應的權重或成本),則鄰接矩陣中的值會是邊的權重。在這種情況下,鄰接矩陣可以包含除0和1之外的其他數值。 ![](https://hackmd.io/_uploads/Hkd4F91z6.png) 若資料中呈現很多0,代表有關聯的其實不多,會呈現出稀疏矩陣(sparse matrix)樣子 ![](https://hackmd.io/_uploads/rJoitqJfa.png =60%x) 所以要改用Directed matrix(有向性矩陣)去存,避免空值太多而浪費空間 ---- # Social Network 範疇 ## Graph Data 可以記錄在一個應用場景下,角色之間的關係 eg. Twitter是、Meta等社群媒體也皆採用Graph去存data [優點]: 1. 底層資料儲存與傳統DB不同 快! 2. 實現不犧牲accuray情況下,又可提高precision ![](https://hackmd.io/_uploads/HJ-sq9kGa.png) - 此圖展示測量值的精確度(Precise)和準確度(Accurate)之間的關係 #### 精確(Precise):代表與其他測量值彼此是很接近的,但不一定接近真實值(有可能會不準確) #### 準確(Accurate):代表接近真實值,但可能與其他測量值相距較遠(可能會不精確) 左上:表示精確且準確。 左下: 很精確(兩個測量值皆在相同位置)但不準確(遠離真實值) 右上: 較準確但不精確(接近真實值但兩個測量值結果相距遠) 右下: 既不精確也不準確 ![](https://hackmd.io/_uploads/r1Hia91G6.png) ![](https://hackmd.io/_uploads/HkBAgoyG6.png) ![](https://hackmd.io/_uploads/HJwJ-iJMT.png) ![](https://hackmd.io/_uploads/SJznXoJG6.png) ![](https://hackmd.io/_uploads/H1y67j1M6.png) ![](https://hackmd.io/_uploads/BJHA7jJMa.png) ![](https://hackmd.io/_uploads/HJjAmi1MT.png) ![](https://hackmd.io/_uploads/rkmy4i1GT.png) ![](https://hackmd.io/_uploads/H1cyVokMT.png) ### 名詞定義 ![螢幕擷取畫面 2023-11-03 125255.png](https://hackmd.io/_uploads/Hy2baxfm6.png) ![螢幕擷取畫面 2023-11-03 125323.png](https://hackmd.io/_uploads/BJhWalfmp.png) >在有向網路,兩個node的關係有四種可能(eg. 無關係、你到他、他到你、雙向連結),此圖有3個node,又存在3個邊,共有4^3種關係 ## Introduction to the Formal Analysis of Social Networks [Typers of network data] ## 1. Egocentered Networks (自我中心網路) ![](https://hackmd.io/_uploads/SyZX8syz6.png) ![](https://hackmd.io/_uploads/BkuPdj1Ma.png =40%x) 選某個node作為中心與周邊的node進行分析 ## 2. Complete Networks 所有node都會有連結 eg. 友誼間的關係建立。 但現實中資料取得會是個大問題 ![](https://hackmd.io/_uploads/SJ9Z_jyG6.png =40%x) ![](https://hackmd.io/_uploads/S1BKdj1Mp.png =40%x) ![](https://hackmd.io/_uploads/BJ5uz3yf6.png) ![](https://hackmd.io/_uploads/BJlyjz2kza.png) ![](https://hackmd.io/_uploads/rJTifnyz6.png) ![](https://hackmd.io/_uploads/rJM3fnyGp.png) ![](https://hackmd.io/_uploads/HkDnGnkf6.png) ![](https://hackmd.io/_uploads/H1u6MhJfa.png) #### Eccentricity (離心率) >用來測量所有node在網路中的位置,用來判斷是中心or邊緣節點。利用計算所有node的最大最短路徑,數值愈小就是中心node,愈大就是邊緣node。 ![](https://hackmd.io/_uploads/BkpAfhyfp.png) ### Diameter 直徑 >最大的eccentricity(從所有node的最大最短路徑中)即是直徑 ![](https://hackmd.io/_uploads/S1mgmhyGa.png) ### Radius 半徑 >最小的eccentricity(從所有node的最大最短路徑中)即是半徑 ![](https://hackmd.io/_uploads/HyF8Opyf6.png) ![](https://hackmd.io/_uploads/B13gmhyfa.png) >老師講解:https://tronclass.scu.edu.tw/course/77502/learning-activity/full-screen#/567269 ### 互惠度 ![螢幕擷取畫面 2023-11-03 134829.png](https://hackmd.io/_uploads/SJ7rnSG76.png) ### 結構洞 ![螢幕擷取畫面 2023-11-03 140031.png](https://hackmd.io/_uploads/BJmBnrzXp.png) ![螢幕擷取畫面 2023-11-03 140815.png](https://hackmd.io/_uploads/SkxXrhBM76.png) ![螢幕擷取畫面 2023-11-03 141540.png](https://hackmd.io/_uploads/SJ7BhBGQT.png) #### 冗餘度、有效規模、效率計算方法: ![螢幕擷取畫面 2023-11-03 141822.png](https://hackmd.io/_uploads/rk7r2rfXT.png) >1. 先求出目標node:E的degree = 4 (有四個相互連接的子點) >2. 再計算相鄰的頂點。 >3. A: A的degree = 2,扣掉與E相鄰的一條,剩下1 >4. B: B的degree = 1,扣掉與E相鄰的一條,剩下0 >5. C: C的degree = 2,扣掉與E相鄰的一條,剩下1 >6. D: D的degree = 3,扣掉與E相鄰的一條,剩下2 >7. 全部相鄰節點ABCD相加,再除目標節點的degree即為冗餘度(Redundancy) >8. 有效規模 = 目標E的degree - Redundancy >9. 效率 = 有效規模/(實際規模,就是degree) ![螢幕擷取畫面 2023-11-03 141858.png](https://hackmd.io/_uploads/Bk7BnBzmT.png) ![螢幕擷取畫面 2023-11-03 141922.png](https://hackmd.io/_uploads/ryg7H2rfQp.png) >老師講解: https://www.youtube.com/watch?v=T4hBzxWzXZ4&ab_channel=%E8%83%A1%E7%AD%B1%E8%96%87 ### Transitivity 傳遞性 ![螢幕擷取畫面 2023-11-03 183643.png](https://hackmd.io/_uploads/rkqIpBfQT.png) ![螢幕擷取畫面 2023-11-03 183103.png](https://hackmd.io/_uploads/HyeQr3Sfm6.png) ![螢幕擷取畫面 2023-11-03 183135.png](https://hackmd.io/_uploads/SJmShHMma.png) 參考來源: 1. 東吳 胡筱薇教授 社群網路分析授課 2. 聯合 陳士杰教授 資料結構授課 3. 洪逸 資料結構