Created
November 27, 2020 11:36
-
-
Save frakw/903cab8f10d66b2c0bf0083fcf971b42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <sstream> | |
| #include <string> | |
| using namespace std; | |
| template<typename T> | |
| class BST_node {//BST節點的class | |
| public: | |
| BST_node() {} | |
| BST_node(T _data):data(_data) {} | |
| bool operator>(const BST_node& i) { return data > i.data; } | |
| bool operator>(const T& i) { return data > i; } | |
| bool operator>=(const BST_node& i) { return data >= i.data; } | |
| bool operator>=(const T& i) { return data >= i; } | |
| bool operator<(const BST_node& i) { return data < i.data; } | |
| bool operator<(const T& i) { return data < i; } | |
| bool operator<=(const BST_node& i) { return data <= i.data; } | |
| bool operator<=(const T& i) { return data <= i; } | |
| void del() {//遞迴刪除所有子孫節點,最後刪除自己 | |
| if (left != nullptr) { | |
| left->del(); | |
| } | |
| if (right != nullptr) { | |
| right->del(); | |
| } | |
| delete this; | |
| } | |
| private: | |
| T data; | |
| BST_node<T>* father = nullptr;//爸爸 | |
| BST_node<T>* left = nullptr;//左邊的小孩 | |
| BST_node<T>* right = nullptr;//右邊的小孩 | |
| template<typename> friend class BST; | |
| }; | |
| template<typename T> | |
| class BST {//BST的class | |
| private: | |
| BST_node<T>* root = nullptr;//根節點的指標 | |
| public: | |
| BST() {} | |
| ~BST() {//釋放記憶體 | |
| root->del(); | |
| root = nullptr; | |
| } | |
| void insert(T new_data) {//這個是從根結點出發的insert,另有重載一個可從任意指標insert的遞迴function | |
| if (root == nullptr) {//如果甚麼都沒有 | |
| root = new BST_node<T>(new_data);//配置記憶體並賦值 | |
| } | |
| else { | |
| if (*root > new_data) {//新資料較小,往左走 | |
| insert(&root->left, new_data,root);//呼叫真正的遞迴insert | |
| } | |
| else {//新資料較大,往右走 | |
| insert(&root->right, new_data,root);//呼叫真正的遞迴insert | |
| } | |
| } | |
| } | |
| void erase(T data) {//呼叫search,找到指標後,呼叫erase的重載function | |
| erase(search(data)); | |
| } | |
| //以下函式是為了簡化介面,真正實作的function重載在private裡 | |
| BST_node<T>* search(T data) { return search(root, data); }//搜尋節點,回傳目標節點指標 | |
| BST_node<T>* largest() { return largest(root); }//回傳樹最大節點的指標 | |
| BST_node<T>* smallest() { return smallest(root); }//回傳樹最小節點的指標 | |
| void pre_order_output() { pre_order_output(root); }//輸出前序 | |
| void in_order_output() { in_order_output(root); }//輸出中序 | |
| void post_order_output() { post_order_output(root); }//輸出後序 | |
| int height() { return height(root); }//回傳樹高 | |
| void cut_half() {//Thanos Finger Snap 找到最接近切半的水平線後,將水平線以下node全部刪除 | |
| int H = height();//樹高(有幾層) | |
| int* layer_count = new int[H] {};//動態配置陣列,儲存每層的node數 | |
| get_layer_number_of_node(root, layer_count, 0);//遞迴計算每層的node數,結果存於layer_count | |
| int min_diff = 2147483647;//水平線上數量與線下數量差,一開始先設很大,避免被超過 | |
| int cut_layer = 0;//儲存最佳的水平線(層數) | |
| for (int i = 0;i < H;i++) {//跑過每條水平線,找最好的 | |
| int up = 0, down = 0;//分別存水平線上數量與線下數量 | |
| for (int j = 0;j < H;j++) {//掃過每層,把數量加到up或down | |
| (j <= i ? up : down) += layer_count[j];//數量加總 <= i 屬於上半層,反之下半層 | |
| } | |
| if (abs(up - down) < min_diff) {//若數量差小於之前所偵測到的最小差 | |
| min_diff = abs(up - down);//更新最小數量差 | |
| cut_layer = i;//更新最佳水平線 | |
| } | |
| } | |
| BST_node<T>** forest = new BST_node<T> * [layer_count[cut_layer + 1]]{};//配置森林記憶體,共有(水平線+1)層之節點數 | |
| int forest_index = 0;//用一個變數才能call by reference,存現在種了幾棵樹 | |
| cut_layer_and_create_forest(root, forest, forest_index, cut_layer + 1, 0);//切除水平線下節點並存到森林中,遞迴函式 | |
| in_order_output();//切完後,輸出中序 | |
| cout << endl; | |
| for (int i = 0;i < layer_count[cut_layer + 1];i++) {//跑過森林裡的每棵樹 | |
| pre_order_output(forest[i]);//輸出前序 | |
| forest[i]->del();//刪除此樹 | |
| } | |
| delete[] layer_count;//釋放每層的node數陣列 | |
| delete[] forest;//釋放森林 | |
| } | |
| //now存現在遞迴到哪個節點,forest存切除後的樹,forest_index存現在是第幾棵樹,target_layer為(最佳水平線+1)層,now_layer為now所在層數(root為0層) | |
| void cut_layer_and_create_forest(BST_node<T>* now, BST_node<T>** forest,int& forest_index,int target_layer, int now_layer) { | |
| if (now == nullptr) return;//防呆 | |
| if (now_layer == target_layer) {//遞迴到正確層數 | |
| forest[forest_index] = now;//切除後的樹存入森林 | |
| forest_index++;//下一棵樹,因為是call by reference,所以同層級的遞迴也能被更改到 | |
| if (now->father->left == now) {//如果自己是爸爸的左小孩 | |
| now->father->left = nullptr;//爸爸的左指標設為空,切除 | |
| } | |
| else{//如果自己是爸爸的右小孩 | |
| now->father->right = nullptr;//爸爸的右指標設為空,切除 | |
| } | |
| } | |
| else { | |
| cut_layer_and_create_forest(now->left, forest, forest_index, target_layer, now_layer + 1);//從左邊開始掃,所以先遞迴左邊 | |
| cut_layer_and_create_forest(now->right, forest, forest_index, target_layer, now_layer + 1);//遞迴右邊 | |
| } | |
| } | |
| private: | |
| //使用指標的指標才能在下次遞迴中配置記憶體並連接上一個node | |
| void insert(BST_node<T>** now,T new_data, BST_node<T>* father) { | |
| if (*now == nullptr) {//如果甚麼都沒有 | |
| *now = new BST_node<T>(new_data);//配置記憶體並賦值 | |
| (*now)->father = father; | |
| } | |
| else { | |
| if (**now > new_data) {//新資料較小,往左走 | |
| insert(&(*now)->left, new_data,*now);//遞迴 | |
| } | |
| else {//新資料較大,往右走 | |
| insert(&(*now)->right, new_data,*now);//遞迴 | |
| } | |
| } | |
| } | |
| void erase(BST_node<T>* target) {//傳入節點指標,刪除該節點 | |
| if (target == nullptr) return;//防呆 | |
| int child_num = (target->left != nullptr) + (target->right != nullptr);//計算小孩數量,nullptr代表沒小孩,回傳0,反之回傳1,因此這2個判斷式相加就是小孩數量 | |
| switch (child_num) | |
| { | |
| case 0: case 1: {//0個或1個小孩 | |
| BST_node<T>* replacement = target->left != nullptr ? target->left : target->right;//替代節點指標,若2邊為空,則為空,若左邊為空,代表右邊有小孩,若右邊為空,代表左邊有小孩 | |
| if (target->father == nullptr) {//如果是根結點,刪除後設root為替代節點指標 | |
| delete target; | |
| root = replacement; | |
| return; | |
| } | |
| if (target->father->left == target) {//如果自己是爸爸的左小孩 | |
| target->father->left = replacement;//爸爸左指標設為替代節點指標 | |
| } | |
| else {//如果自己是爸爸的右小孩 | |
| target->father->right = replacement;//爸爸右指標設為替代節點指標 | |
| } | |
| if (replacement != nullptr) replacement->father = target->father;//如果替代節點指標不為空,將小孩的爸爸設為自己的爸爸 | |
| delete target;//刪除目標 | |
| }break; | |
| case 2: {//左右都有小孩 | |
| BST_node<T>* replacement = largest(target->left);//找左子樹中最大 | |
| target->data = replacement->data;//用左子樹中最大值替代原本的值 | |
| erase(replacement);//刪除左子樹中最大值的node | |
| }break; | |
| default:break; | |
| } | |
| } | |
| BST_node<T>* search(BST_node<T>* now, T data) {//搜尋節點 | |
| if (now == nullptr) return nullptr; | |
| if (*now > data) return search(now->left, data);//要找的資料小於目前節點的資料,往左走 | |
| else if (*now < data) return search(now->right, data);//要找的資料大於目前節點的資料,往右走 | |
| else return now;//既非大於也非小於,那就是等於 | |
| } | |
| BST_node<T>* largest(BST_node<T>* now) {//回傳now以下最大值的節點,一直往右走即可 | |
| if (now == nullptr) return nullptr; | |
| else if (now->right == nullptr) return now; | |
| return largest(now->right); | |
| } | |
| BST_node<T>* smallest(BST_node<T>* now) {//回傳now以下最小值的節點,一直往左走即可 | |
| if (now == nullptr) return nullptr; | |
| else if (now->left == nullptr) return now; | |
| return smallest(now->left); | |
| } | |
| int height(BST_node<T>* now) {//回傳now的高度,leaf node為高度為1 | |
| if (now == nullptr) return 0; | |
| int LH = height(now->left); | |
| int RH = height(now->right); | |
| return (LH > RH ? LH : RH) + 1;//左右子樹高度選大的並加上自己(1) | |
| } | |
| void get_layer_number_of_node(BST_node<T>* now, int* layer_count, int layer) {//計算每層node數 | |
| if (now == nullptr) return;//防呆 | |
| layer_count[layer]++;//該層node數加1 | |
| get_layer_number_of_node(now->left, layer_count, layer + 1);//往左遞迴下去,下一層為layer+1 | |
| get_layer_number_of_node(now->right, layer_count, layer + 1);//往右遞迴下去,下一層為layer+1 | |
| } | |
| void pre_order_output(BST_node<T>* now) {//前序輸出,輸出該節點,然後繼續遞迴 | |
| if (now == nullptr) return; | |
| static bool first_out = true;//static讓每次遞迴都可以用相同的變數 | |
| cout << (first_out ? "" : " ") << now->data;//第一次輸出不用加空白,後面都要 | |
| first_out = false;//輸出完就不是第一次了 | |
| pre_order_output(now->left);//遞迴左子樹 | |
| pre_order_output(now->right);//遞迴右子樹 | |
| } | |
| void in_order_output(BST_node<T>* now) {//中序輸出,先遞迴左子樹,然後輸出該節點,遞迴右子樹 | |
| if (now == nullptr) return; | |
| in_order_output(now->left);//遞迴左子樹 | |
| static bool first_out = true;//static讓每次遞迴都可以用相同的變數 | |
| cout << (first_out ? "" : " ") << now->data;//第一次輸出不用加空白,後面都要 | |
| first_out = false;//輸出完就不是第一次了 | |
| in_order_output(now->right);//遞迴右子樹 | |
| } | |
| void post_order_output(BST_node<T>* now) {//後序輸出,遞迴左子樹與右子樹,然後輸出該節點 | |
| if (now == nullptr) return; | |
| post_order_output(now->left);//遞迴左子樹 | |
| post_order_output(now->right);//遞迴右子樹 | |
| static bool first_out = true;//static讓每次遞迴都可以用相同的變數 | |
| cout << (first_out ? "" : " ") << now->data;//第一次輸出不用加空白,後面都要 | |
| first_out = false;//輸出完就不是第一次了 | |
| } | |
| }; | |
| int main() { | |
| BST<int> tree; | |
| string line;//存一行 | |
| int data;//暫存數字 | |
| getline(cin, line); | |
| stringstream s(line); | |
| while (s >> data) {//用stringstream讀數字 | |
| tree.insert(data);//插入所有數字 | |
| } | |
| tree.post_order_output();//輸出後序 | |
| cout << endl; | |
| getline(cin, line); | |
| s.clear(); | |
| s.str(line); | |
| while (s >> data) {//用stringstream讀數字 | |
| tree.erase(data);//刪除Avengers | |
| } | |
| tree.cut_half();//刪除一半node後輸出上半部(中序)、下半部(前序) | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment