Acme Design is a full service design agency.

圖論

Sarah Levin, CEO

表達兩個東東之間的關係

ex: 血緣/人物關係圖、捷運路線圖

甚麼是圖?

  • 我們可以將上圖簡化為如下圖:

一些名詞(無向圖)

點vertex
邊edge
  • 注意方向,可能只有單向(單箭頭)

一些名詞(有向圖)

  • 鄰接矩陣adjacency matrix

開一個二維矩陣Graph

若兩點有關係就將Graph[u][v]標記為1

否則為0

無向圖時Graph[v][u]也要記錄!

空間複雜度 O(     )

 

如何記錄?

n^2

假設有ABCDE五個點,其中{A, B}, {A, C}, {B, D}, {C, D}, {D, E}

間有關係,可以用下列鄰接矩陣進行表示:

\text{Adjacency Matrix} = \begin{bmatrix} 0 & 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 1 & 0 \\ 0 & 1 & 1 & 0 & 1 \\ 0 & 0 & 0 & 1 & 0 \\ \end{bmatrix}
  • 鄰接陣列adjacency list

對於每個點開一個vector,在後面紀錄與其相鄰的點

adj[u].push_back(v);

同樣若是無向圖的時候也要記錄 v-> u

空間複雜度 O(m)

 

如何記錄?

vector<int> adj[M];
int main()
{
	int n, m;
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
    	int a, b;
        cin >> a >> b;
        adj[a].push_back(b);
        adj[b].push_back(a);
    }
}
  • 分為BFS(廣度優先搜尋法) 以及 DFS(深度優先搜尋)

  • BFS (想成水在流)
    使用queue存下可以走到但尚未走過的點
    然後將走過的點pop(刪除), 繼續走與他相鄰的點
    可以用來記錄每個點與原點的距離!
    假設每一點的權重均為1(後面會遇到權重不一樣的情況!)

 

圖的遍歷

vector <int> adj[M];
bool vis [M];  //紀錄是否走過
int d[M];     //紀錄距離
queue <int> que;

d[1] = 0;
que.push(1);

while (que.size())
{
    int now = que.front();
    que.pop();
    vis[now] = 1;
    for (int v:adj[now])  //對於adj[now]中的每一個點都跑一遍
    {
       if (!vis[v])
       {
           que.push(v); // 還沒跑過就加入queue
           d[v] = d[now] + 1;
           vis[v] = 1  //並紀錄已經跑過
       }        
    }
}
  • 走目前能走的位置,一樣記錄每個點是否走過

DFS

vector <int>adj[M];
bool vis[M];
DFS(1);
void DFS(int n)
{
    vis[n] = 1;
    for (int v:adj[n])
    {
        if (!vis[v])
            DFS(v);
    }
}
  • 想成走迷宮,一條路先走到底,不行了再換另外一條

題目練習

Hint: 用區塊分房間,該用dfs or bfs?

  • 做事情有先後順序的時候該如何排序?

拓撲排序

  • 思路:能做的就先做完!
  • 將事情的先後順序看成有向圖,當作過該事件後刪除此點

  • 實際演示(白板)

     

     

     

     

     

     

     

     

     

     

     

     

     

拓撲排序

  • 思路:能做的就先做完!
  • 將事情的先後順序看成有向圖,當作過該事件後刪除此點

  • 實際演示(白板)

     

     

     

     

     

     

     

     

     

     

     

     

     

7 1
7 8
4 2
10 5
9 2
3 5
6 8
1 5
10 8
7 10
vector <int> adj[M];
int deg[M];  //紀錄每個點從哪裡來的! 0即為前面沒有要先做的
int main()
{
	memset(deg, 0, sizeof(deg));
    for (int i=0; i<m; i++)
    {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        deg[v]++;
    }
    queue <int> que;
    for (int i=1; i<=n; i++)
        if (deg[i]==0)  //若事情前面沒有要先做的事就先跑
            que.push(i);
    while (que.size())
    {
        int now = que.front();
        que.pop();
        for (int v:adj[now])
        {
            deg[v]--;
            if (deg[v] == 0)
                que.push(v);
        }
    }   
}
  • 想法1
    將與當前節點相鄰的點,全部塗為另一個顏色,若遇到相同顏色則代表不合法
  • 想法2
    將要是相同顏色的節點設做一個可能的集合
    用兩個相同的AB圖表示
    若同一個節點在A和B處於同一個集合則失敗

二分圖

數獨

  • 有n個點,n-1個邊的連通圖

  • 除了根節點外,每個點都有父節點

Credit: tree-演算法筆記

  • 深度:當前點與根的距離

一些名詞

  • 子節點/父節點:兩個相連的節點,距離根較近的為父節點,另一個為子節點
  • 要用甚麼來記錄深度呢?
  • DFS!

遍歷實作

void dfs(int n, int par)
{
    for (int v:adj[n])
        if (v != par)
            dfs(v, n);
}

遍歷實作

int d[M];
d[1] = 0;  //此處先假設根節點為1, 要將其換成根節點喔!
void dfs(int n, int par)
{
    for (int v:adj[n])
        if (v != par)
        {
            d[v] = d[n] +1;
            dfs(v, n);
        }
}

如何記錄深度呢?

  • 滿足性質:左子樹以下的所有節點均比當前節點小
  • 右子樹以下的所有節點均比當前節點大

二元搜尋樹

  • 思考:如何插入和搜尋的實作

二元搜尋樹

struct Node
{
	int val;
    Node* l;
    Node* r;
}

Desk

By yujuan

Desk

  • 243