---
# System prepended metadata

title: 二元樹 - HashMap & Heap
tags: [資料結構]

---

---
title: '二元樹 - HashMap & Heap'
disqus: kyleAlien
---

二元樹 - HashMap & Heap
===

## OverView of Content

[TOC]

## 二元樹拓展

**透過二元樹可以來製作 其他類型的 數據結構**

## 二元樹 HashMap

使用二元樹完成 HashMap，其中需要修改的主要就是 Node 節點，**Node 要同時儲存 Key/Value 數據**，其餘操作與二原樹相同

```java=
public static final class HashMapTreeNode<V> {
    // Key/Value 數據
    private final int key;
    private V value;

    // 左右子樹
    public HashMapTreeNode<V> left, right;

    public HashMapTreeNode(int key, V value) {
        this.key = key;
        this.value = value;
    }
}
```
    
### 二元樹 HashMap - 基礎操作

* 其中操作需要改名，與 HashMap 操作相同名稱，以下舉例兩種

    | 二元樹函數 | 修改 Map 使用的函數名稱 |
    | - | - |
    | `insert(k, v)` | `put(k, v)` |
    | `get(k)` | `get(k)` |

    1. `insert(k, v)` 操作改為 `put(k, v)`
    
        ```java=
        private final HashMapTreeNode<V> header;

        public HashMapTree(HashMapTreeNode<V> header) {
            this.header = header;
        }

        public HashMapTreeNode<V> put(int key, V val) {
            _put(header, key, val);
            return header;
        }

        private HashMapTreeNode<V> _put(HashMapTreeNode<V> root, int key, V val) {
            if(root == null) {
                return new HashMapTreeNode<>(key, val);
            }

            if(root.key >= key) {
                root.left = _put(root.left, key, val);
            } else if (root.key < key) {
                root.right = _put(root.right, key, val);
            }

            return root;
        }
        ```
    
    
    2. 取操作改為 `get(k)` (並沒有移除操作)

        ```java=
        public V get(int key) {
            // 使用呼叫函數 _get
            HashMapTreeNode<V> result = _get(header, key);

            return result == null ? null : result.value;
        }

        public HashMapTreeNode<V> _get(HashMapTreeNode<V> root, int key) {
            if(root == null) {
                return null;
            }

            if(root.key == key) {
                return root;
            }

            if(root.key >= key) {
                return _get(root.left, key);
            } else if(root.key < key) {
                return _get(root.right, key);
            }

            return null;
        }
        ```

* 測試：二元樹的剩餘操作其實都沒變，主要要調整 Node

    ```java=
    public static void main(String[] args) {
        HashMapTreeNode<String> root = new HashMapTreeNode<>(53, "Apple");

        HashMapTree<String> tree = new HashMapTree<>(root);

        tree.put(76, "Banana");

        String result = tree.get(76);
        System.out.println("Result: " + result);
    }
    ```
    > ![](https://i.imgur.com/N2jyakc.png)

    
### 二元樹 HashMap - 尋訪

* 使用二元樹的 HashMap 可以使用 **中序走訪** 來達到 **升序結果**，這是跟 HashMap 不同的地方 (二元樹的特性)

    ```java=
    // 中序走訪

    public void inorder() {
        _inorder(header);
    }

    private void _inorder(HashMapTreeNode<V> root) {
        if(root == null) {
            return;
        }
        _inorder(root.left);

        System.out.println("key: " + root.key + ", value: " + root.value);

        _inorder(root.right);
    }
    ```
    
    * 測試：中序走訪二元樹，確實可以得到一個升序結果

        ```java=
        public static void main(String[] args) {
            HashMapTreeNode<String> root = new HashMapTreeNode<>(53, "C");

            HashMapTree<String> tree = new HashMapTree<>(root);

            tree.put(5, "A");
            tree.put(20, "B");
            tree.put(58, "D");
            tree.put(76, "E");
            tree.put(79, "F");

            tree.inorder();
        }
        ```
        > ![](https://i.imgur.com/atc5re6.png)


## 二元樹 Heap


使用二元樹完成 Heap，其中需要修改的主要就是 Node 節點，**Node 要同時儲存 Priority/Value 數據** (Key 換成 Priority)，其餘操作與二原樹相同

```java=
public static final class HeapTreeNode<V> {
    private final int priority;
    private V value;

    public HeapTreeNode<V> left, right;

    public HeapTreeNode(int priority, V value) {
        this.priority = priority;
        this.value = value;
    }
}
```

:::success
* Node 擺放規則，要使用 Heap 還是二元樹 ?


    **使用二元樹規則** ! 使用二元樹完成的 Heap 的排序規則還是與二元樹的排序規則相同，好處是可以無線拓展二元樹大小 (原本使用 Array 會有數量上限問題)
:::

### 二元樹 Heap - 基礎操作

* 其中操作需要改名，與 Heap 操作相同名稱，以下舉例兩種

    | 二元樹函數 | 修改 Map 使用的函數名稱 |
    | - | - |
    | `insert(k, v)` | `enqueue(k, v)` |
    | `remove(k)` | `dequeue()` |

    1. `insert(k, v)` 操作改為 `enqueue(k, v)`：在原本的 Heap 結構中　`enqueue` 操作需要 swim(上浮) 操作；
        
        **二元樹的 Heap 就 ++不需要 swim(上浮) 操作++**

        ```java=
        private int count;
        private HeapTreeNode<V> header;

        public HeapTree(HeapTreeNode<V> header) {
            this.count = 0;
            this.header = header;
        }

        public void enqueue(int priority, V val) {
            _enqueue(header, priority, val);
            count += 1;
        }

        // 輔助函數
        private HeapTreeNode<V> _enqueue(HeapTreeNode<V> root, int priority, V val) {
            if(root == null) {
                return new HeapTreeNode<>(priority, val);
            }

            if(root.priority >= priority) {
                root.left = _enqueue(root.left, priority, val);
            } else if (root.priority < priority) {
                root.right = _enqueue(root.right, priority, val);
            }

            return root;
        }
        ```

    2. `remove(k)` 操作改為 `dequeue()`：**原本 Heap 結構中　`dequeue` 操作會取陣列頂端的節點，該節點是最高的優先度**

        **所以二元樹的 Heap 就需要 ++刪除最大值、取得最大值 的輔助函數++**
        
        :::success
        * **取最大值 - 效率**
            
            Heap 的原本特性可以找到以 O(1) 的效率來找到最大值 (`在 array[1]`)；**使用二元樹的 Heap，最大值在最右下方，可以用 `log(N)` 的速度找到最大值**
        :::
        
        ```java=
        public HeapTreeNode<V> dequeue() {
            // 取最大值
            HeapTreeNode<V> max = _getMax(header);
            
            // 在移除的過程中可能移除的是 Header，所以要更新 Header
            header = _removeMax(header);
            count -= 1;
            return max;
        }

        private HeapTreeNode<V> _getMax(HeapTreeNode<V> root) {
            if(root.right == null) {
                // 沒有右子樹就返回自身 (自身最大
                return root;
            }

            // 最大值會在節點右邊
            return _getMax(root.right);
        }

        private HeapTreeNode<V> _removeMax(HeapTreeNode<V> root) {
            // 找最大值
            if(root.right == null) {
                return root.left;    // 可能還有左子樹，所以需要返回左子樹串接
            }

            root.right = _removeMax(root.right);

            return root;
        }
        ```


* 測試：可以看到結果如同最大 Heap 結構，從 Priority 最高開始 `dequeue`
    ```java=
    public static void main(String[] args) {
        HeapTree<String> heapTree = new HeapTree<>(new HeapTreeNode<>(9, "F"));

        heapTree.enqueue(5, "B");
        heapTree.enqueue(2, "He");
        heapTree.enqueue(8, "O");

        heapTree.enqueue(12, "Mg");
        heapTree.enqueue(10, "Ne");
        heapTree.enqueue(14, "Si");

        int c = heapTree.count;
        for(int i = 0; i < c; i++) {
            HeapTreeNode<String> node = heapTree.dequeue();
            System.out.println("priority: " + node.priority + ", val:" + node.value);
        }

    }
    ```
    > ![](https://i.imgur.com/jzkKUCU.png)

    :::warning
    * 二元樹 Heap 的效能比原來 Heap 結構還要差一點
    :::

## Appendix & FAQ

:::info
:::

###### tags: `資料結構`
