朋友們、伙計們,我們又見面了,本期來給大家解讀一下有關多態的知識點,如果看完之后對你有一定的啟發,那么請留下你的三連,祝大家心想事成!
C 語 言 專 欄:C語言:從入門到精通
數據結構專欄:數據結構
個? 人? 主? 頁?:stackY、
C + + 專 欄? ?:C++
Linux 專?欄? :Linux
??
目錄
1. 搜索二叉樹
1.1 概念
1.2 搜索二叉樹操作
2. 模擬實現搜索二叉樹?
2.1 非遞歸版本
2.1.1 基本構造
2.1.2 插入
2.1.3 刪除
2.1.4 查找
2.2 遞歸版本
2.2.1 插入
2.2.2 刪除
2.2.3 查找
2.2.4?中序遍歷
3. 完整代碼
1. 搜索二叉樹
1.1 概念
二叉搜索樹又稱二叉排序樹,它或者是一棵空樹,或者是具有以下性質的二叉樹:
- 若它的左子樹不為空,則左子樹上所有節點的值都小于根節點的值
- 若它的右子樹不為空,則右子樹上所有節點的值都大于根節點的值
- 它的左右子樹也分別為二叉搜索樹
1.2 搜索二叉樹操作
?
1. 二叉樹的查找
a、從根開始比較,查找,比根大則往右邊走查找,比根小則往左邊走查找。
b、最多查找高度次,走到空,還沒找到,這個值不存在。
2. 二叉樹的插入
插入的具體過程如下:
a. 樹為空,則直接新增節點,賦值給root指針
b. 樹不空,按二叉搜索樹性質查找插入位置,插入新節點
3. 二叉樹的刪除?
首先查找元素是否在二叉搜索樹中,如果不存在,則返回, 否則要刪除的結點可能分下面四種情況:
a. 要刪除的結點無孩子結點
b. 要刪除的結點只有左孩子結點
c. 要刪除的結點只有右孩子結點
d. 要刪除的結點有左、右孩子結點
2. 模擬實現搜索二叉樹?
搜索二叉樹有兩種模型:
1. Key模型:節點中只存在一個值key,并且這個值不可以修改,比如后面學習到的set
2. Key_Value模型:節點中存在兩個值,一個是key,不可修改,另一個是與key對應的value,可以修改,比如后面學習到的map
在這里我們只實現Key模型的搜索二叉樹
2.1 非遞歸版本
2.1.1 基本構造
//節點 template<class K> struct BSTreeNode {BSTreeNode* _left;BSTreeNode* _right;K _key;//構造BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){} };//搜索二叉樹 template<class K> class BSTree { public:typedef BSTreeNode<K> Node;//構造BSTree() //給定了缺省值{}//拷貝構造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._root);return *this;}//析構~BSTree(){Destroy(_root);} private://遞歸拷貝左右子樹Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}//后序遍歷刪除void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}private:Node* _root = nullptr; //缺省值給空即可 };
2.1.2 插入
插入時如果為空直接插入即可,若存在節點,需要先進行判斷,比根節點大的插入到它的右子樹,比根節點小的插入左子樹即可,這時需要注意的插入的節點需要與它的父節點進行鏈接,這時在往下比較的過程中就需要記錄一下它的父節點。
//插入bool Insert(const K& key){//如果為空可以直接插入鏈接if (_root == nullptr){_root = new Node(key);return true;}//記錄父節點Node* parent = nullptr;Node* cur = _root;//遍歷找到合適的節點進行插入鏈接while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//鏈接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}
2.1.3 刪除
首先找到需要刪除的節點,刪除的時候需要注意分下面幾種情況:
- 左子樹為空,可以直接刪除
- 右子樹為空,可以直接刪除
- 左右子樹都不為空需要使用替換法刪除
- 替換法:使用左子樹最大的節點替換需要刪除的節點,或者使用右子樹最小的節點替換需要刪除的節點,替換之后直接刪除被替換的節點即可完成刪除。
- 需要注意的是在這個過程中需要記錄父節點,在刪除之后需要及時鏈接,并且要注意的是刪除的節點是根節點的時候可以直接將它的左右子樹直接鏈接。
//刪除bool Erase(const K& key){Node* cur = _root;//記錄父親Node* parent = nullptr;while (cur){//找到要刪除的keyif (cur->_key > key){//更新父親parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{//開始刪除if (cur->_left == nullptr) //左為空 //直接刪除{//先判斷是否為根節點if (cur == _root){_root = cur->_right;}else //不為根節點{if (cur == parent->_left) {parent->_left = cur->_right;}else if (cur == parent->_right){parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr) //右為空{//先判斷是否為根節點if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else if (cur == parent->_right){parent->_right = cur->_left;}}delete cur;}else //左右子樹都不為空 //使用替換法{Node* parent = cur;//右樹的最小節點進行替換或者左樹的最大節點Node* subRight = cur->_right;while (subRight->_left) //找到右樹的最小節點{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key); //替換兩個節點//將刪除節點的右樹鏈接在它的父親if (parent->_left == subRight){parent->_left = subRight->_right;}else {parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}
2.1.4 查找
//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn true;}return false;}
2.2 遞歸版本
2.2.1 插入
遞歸插入時也需要進行一層封裝,在里面傳遞root,在這里采用引用傳參比較好,可以不用額外的鏈接,遇到空直接創建一個節點即可,直接在原數上進行操作。
//插入bool InsertR(const K& key){return _InsertR(key, _root);}bool _InsertR(const K& key, Node*& root){//樹為空直接插入即可if (root == nullptr){root = new Node(key);return true;}//遞歸左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key) //遞歸右return _InsertR(key, root->_right);elsereturn false;}
2.2.2 刪除
還是采用里面封裝一層,在遞歸刪除的時候先遞歸找到要刪除的key,然后判斷它的左右子樹,如果左右子樹只存在一個可以直接進行刪除,然后將它的孩子鏈接在它的節點上,如果左右孩子均存在,使用替換法,用該節點的右子樹的最小節點進行替換,先使用循環找到該最小節點,然后與其交換,然后轉化為遞歸該節點右子樹的刪除問題即可。
//刪除bool EraseR(const K& key){return _EraseR(key, _root);} bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else //找到了進行刪除操作{if (root->_left == nullptr) //作為空直接刪除{Node* del = root;root = root->_right;delete del;return true;}else if (root->_right == nullptr) //右為空也可以直接進行刪除{Node* del = root;root = root->_left;delete del;return true;}else //左右都不為空{Node* subRight = root; //找到右樹的最小節點while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key); //交換return _EraseR(key, root->_right); //轉化為遞歸右子樹的子問題}}}
2.2.3 查找
//查找bool FindR(const K& key){_FindR(key, _root);} bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}
2.2.4?中序遍歷
中序遍歷時需要封裝一層,在外面不好傳遞節點,中序遍歷:左子樹、根、右子樹
//中序遍歷void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return; _InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}
3. 完整代碼
#pragma once #include <iostream>using namespace std;template<class K> struct BSTreeNode {BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){} };template<class K> class BSTree { public:typedef BSTreeNode<K> Node;//構造BSTree() //給定了缺省值{}//拷貝構造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._root);return *this;}//析構~BSTree(){Destroy(_root);}//非遞歸版本//插入bool Insert(const K& key){//如果為空可以直接插入鏈接if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//鏈接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}//刪除bool Erase(const K& key){Node* cur = _root;//記錄父親Node* parent = nullptr;while (cur){//找到要刪除的keyif (cur->_key > key){//更新父親parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{//開始刪除if (cur->_left == nullptr) //左為空 //直接刪除{//先判斷是否為根節點if (cur == _root){_root = cur->_right;}else //不為根節點{if (cur == parent->_left){parent->_left = cur->_right;}else if (cur == parent->_right){parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr) //右為空{//先判斷是否為根節點if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else if (cur == parent->_right){parent->_right = cur->_left;}}delete cur;}else //左右子樹都不為空 //使用替換法{Node* parent = cur;//右樹的最小節點進行替換或者左樹的最大節點Node* subRight = cur->_right;while (subRight->_left) //找到右樹的最小節點{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key); //替換兩個節點//將刪除節點的右樹鏈接在它的父親if (parent->_left == subRight){parent->_left = subRight->_right;}else{parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn true;}return false;}//遞歸版本//插入bool InsertR(const K& key){return _InsertR(key, _root);}//刪除bool EraseR(const K& key){return _EraseR(key, _root);}//查找bool FindR(const K& key){_FindR(key, _root);}//中序遍歷void InOrder(){_InOrder(_root);cout << endl;}private://插入bool _InsertR(const K& key, Node*& root){//樹為空直接插入即可if (root == nullptr){root = new Node(key);return true;}//遞歸左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key) //遞歸右return _InsertR(key, root->_right);elsereturn false;}//刪除bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else //找到了進行刪除操作{if (root->_left == nullptr) //作為空直接刪除{Node* del = root;root = root->_right;delete del;return true;}else if (root->_right == nullptr) //右為空也可以直接進行刪除{Node* del = root;root = root->_left;delete del;return true;}else //左右都不為空{Node* subRight = root; //找到右樹的最小節點while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key); //交換return _EraseR(key, root->_right); //轉化為遞歸右子樹的子問題}}}//查找bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}//中序遍歷void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}//拷貝Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}//銷毀void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;} private:Node* _root = nullptr; };
朋友們、伙計們,美好的時光總是短暫的,我們本期的的分享就到此結束,欲知后事如何,請聽下回分解~,最后看完別忘了留下你們彌足珍貴的三連喔,感謝大家的支持!?????