標籤:
一、前言一直以來,都對樹有關的東西望而卻步。以前每次說要看一看,都因為惰性,時間就那麼荒廢掉了。今天下個決心,決定好好的資料結構中的東西看一下。不知道看這篇文章的你,是不是和我有同樣的感受,空有一顆努力的心,卻遲遲沒有付出行動。如果是的話,如果也想好好的把樹的知識鞏固一下的話,就讓我們一起好好兒地把知識點過一遍吧。本文爭取讓看完的每一個沒有基礎的同學,都能有所收穫。在本文開始前,先給自己加個油。加油(^ω^) 二、二叉搜尋樹的定義二叉搜尋樹是指,對於某一個節點而言,它左邊的節點都小於或等於它。而它右邊的節點都大於或等於它。借用一下網上的圖片(懶得自己畫圖了,請見諒)。如所示,是一個二叉搜尋樹,大家可以自己找幾個節點試一實驗證一下這個定義。三、二叉搜尋樹有關的常見的操作1、建立;2、插入一個節點;3、刪除一個節點;4、找出最小和最大的節點;5、找出某個節點的前繼節點和後繼節點。 前面4條所做的工作比較好理解,但要理解第一條所做的工作,則需要明白什麼是前繼節點和後繼節點。下面我們就一起回顧一下什麼是前繼節點,什麼是後繼節點把。\(^o^)/~,加油!前繼節點:某個節點的前繼節點,是指比該節點小的所有節點中的最大的一個節點。如在中,比節點“8”小的節點有很多個,但是最大的一個節點為“7”,那麼節點“7”則為節點“8”的前繼節點;後繼節點:某個節點的後繼節點,是指比該節點大的所有節點中的最小的一個節點。如在上如中,比節點“1”大的節點有很多歌,但是最小的一個節點為“3”,那麼節點“3”則為節點“1”的後繼節點。 【插入】下面我們來分別理解一下,每種操作實現的具體步驟。建立一個二叉搜尋樹,其實是插入n個節點。而插入一個節點,我們是這麼做的:
- 如果一個節點小於根節點,且根節點的左孩子為空白的話,那麼直接將該節點作為根節點的左孩子;
- 如果一個節點小於根節點,但是根節點的左孩子不為的話,那麼我們將根節點的左孩子作為新的根節點,遞迴進行1、2兩步。
- 同理,我們可以知道該怎麼處理一個節點大於根節點的情況。
- 在這裡有一個特殊的情況需要說明一下,那就是如果碰到重複的節點,本文採用軟插入和虛刪除的辦法。即我們設定一個變數用來表示該節點出現的次數,如果要插入的節點已經在二叉搜尋樹中存在的話,那麼我們只需要把這個節點出現的次數變數加1。
如所示【最大值&最小值】找一個二叉樹的最大值和最小值比較簡單,因為二叉樹的最小值肯定在二叉樹的最左邊,最大值肯定在二叉樹的最右邊。我們只需要使用一個迴圈,一直分別迴圈到節點的左節點或右節點為空白,則可以得到二叉樹的最大值和最小值了。如中,最小節點為2,最大節點為18.【前繼節點】找一個節點的前繼節點,我們分為兩種情況
- 若該節點有左孩子,那麼前繼節點則為以該節點的左孩子為根節點所尋找到的最大值;
- 若該節點沒有左孩子,那麼前繼節點需要往上尋找節點為右孩子。
如中,12的前繼節點為10。【後繼節點】找一個節點的後繼節點,我們分為兩種情況
- 若該節點有右孩子,那麼前繼節點則為以該節點的左孩子為根節點所尋找到的最小值;
- 若該節點沒有右孩子,那麼前繼節點需要往上尋找節點為左孩子。
如中,3的後繼節點為4。【刪除】刪除一個節點,我們也分為三種情況
- 如果待刪除的節點即沒有左孩子,也沒有右孩子的話,則直接刪除該節點,但是請注意需要將相應的父節點的修改。如:如果待刪除的節點是父節點的左孩子的話,那麼需要將父節點的左孩子節點置為空白;
- 如果待刪除的節點只有一個孩子,那麼直接用該孩子代替這個節點。這裡,同樣需要注意修改父節點。即,如果待刪除是父節點的左孩子,且它只有一個右孩子,那麼刪除這個節點需要將父節點的左節點指向待刪除節點的右孩子;
- 如果待刪除的節點有兩個孩子的話,我們可以選擇使用該節點的前繼節點或者後繼節點來代替該節點。大家可以在筆下試一試,就會發現兩種替代方式,都能保證更新之後的二叉樹依然滿足二叉搜尋樹的定義。
如所示 完整代碼
#include "stdafx.h"#include <string>#include <stdlib.h>#include <iostream>using namespace std;typedef struct Node{int key; //索引值struct Node *left;//左節點struct Node *right; //右節點struct Node *father;//父節點int times; //節點出現的次數} Node, *pNode;void creatBinarySearchTree(pNode &pBSTTree, int *ptr, int len);void insertNode(pNode &pBSTTree, int value);void mallocInitNode(pNode &pInsertNode, int value);pNode findMinNode(pNode &pTree);pNode findMaxNode(pNode &pTree);pNode findPredecessor(pNode &pSearchNode);pNode findSuccessor(pNode &pSearchNode);void deleteNode(pNode& pdeleteNode);void changeFatherChildNode(pNode& pdeleteNode, pNode& pNewChildNode);int main(){int a[] = {15,15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};int len = sizeof(a) / sizeof(int);pNode pBSTTree = NULL;pNode pPreNode = NULL;pNode pSuccessor = NULL;/* 建立二叉尋找樹 */creatBinarySearchTree(pBSTTree, a, len);/* 尋找二叉尋找樹中的最大值 */cout << "最小值的節點為:" << findMinNode(pBSTTree)->key << endl;cout << "最大值的節點為:" << findMaxNode(pBSTTree)->key << endl;/* 尋找某個節點的前驅節點 */pPreNode = findPredecessor(pBSTTree->left->right);if (NULL != pPreNode){cout << "該節點的前驅節點為:" << pPreNode->key << endl;}else{cout << "該節點無前驅節點" << endl;}/* 尋找某個節點的後繼節點 */pSuccessor = findSuccessor(pBSTTree->left->left->left);if (NULL != pPreNode){cout << "該節點的後繼節點為:" << pSuccessor->key << endl;}else{cout << "該節點無後繼節點" << endl;}/* 刪除某個節點 */deleteNode(pBSTTree->right->right);deleteNode(pBSTTree->left->left);cout << "最小值的節點為:" << findMinNode(pBSTTree)->key << endl;pSuccessor = findSuccessor(pBSTTree->left->left);if (NULL != pPreNode){cout << "該節點的後繼節點為:" << pSuccessor->key << endl;}else{cout << "該節點無後繼節點" << endl;}free(pBSTTree);pBSTTree = NULL;return 0;}/* 建立一個二叉尋找樹 */void creatBinarySearchTree(pNode &pBSTTree, int *ptr, int len){for (int i = 0; i < len; i++){insertNode(pBSTTree, *(ptr + i));}}/* 插入一個節點,複雜度為O(nlogn) */void insertNode(pNode &pBSTTree, int value){pNode pInsertNode;/* 第一個節點,直接插入 */if (NULL == pBSTTree){mallocInitNode(pInsertNode, value);pBSTTree = pInsertNode;return;}/* 如果索引值已經存在的話,只需要times++ */if (value == pBSTTree->key){pBSTTree->times++;return;}/* 如果小於本節點的值,且該節點無左孩子 */if ((NULL == pBSTTree->left) && (value < pBSTTree->key)){mallocInitNode(pInsertNode, value);pInsertNode->father = pBSTTree;pBSTTree->left = pInsertNode;return;}/* 如果大於本節點的值,且該節點無右孩子 */if ((NULL == pBSTTree->right) && (value > pBSTTree->key)){mallocInitNode(pInsertNode, value);pInsertNode->father = pBSTTree;pBSTTree->right = pInsertNode;return;}/* 如果小於本節點的值,但是該節點已經有左孩子,那麼就繼續遞迴 */if ((NULL != pBSTTree->left) && (value < pBSTTree->key)){insertNode(pBSTTree->left, value);}/* 如果大於本節點的值,但是該節點已經有右孩子,那麼就繼續遞迴 */if ((NULL != pBSTTree->right) && (value > pBSTTree->key)){insertNode(pBSTTree->right, value);}}/* 建立新節點並初始化 */void mallocInitNode(pNode &pInsertNode, int value){pInsertNode = (pNode)malloc(sizeof(Node));pInsertNode->key = value;pInsertNode->father = NULL;pInsertNode->left = NULL;pInsertNode->right = NULL;pInsertNode->times = 1;}/* 尋找二叉樹中最小的節點和最大的節點 */pNode findMinNode(pNode &pTree){pNode pTemp = pTree;while (NULL != pTemp->left){pTemp = pTemp->left;}return pTemp;}pNode findMaxNode(pNode &pTree){pNode pTemp = pTree;while (NULL != pTemp->right){pTemp = pTemp->right;}return pTemp;}/* 找出前驅節點 */pNode findPredecessor(pNode &pSearchNode){/* 如果左子樹存在的話,則返回左子樹中最大的節點,即為比它小的之中的最大的節點 */if (NULL != pSearchNode->left){return findMaxNode(pSearchNode->left);}/* 如果左子樹不存在的話,則需要往上找,直到找到目標節點是目標節點父親節點的右孩子 */pNode pTemp = pSearchNode;while(pTemp != pTemp->father->right){pTemp = pTemp->father;}return pTemp->father;}/* 找出後繼節點 */pNode findSuccessor(pNode &pSearchNode){/* 如果右子樹存在的話,則返回右子樹中最小的節點,即為比它大的之中的最小的節點 */if (NULL != pSearchNode->right){return findMinNode(pSearchNode->right);}/* 如果左子樹不存在的話,則需要往上找,直到找到目標節點是目標節點父親節點的右孩子 */pNode pTemp = pSearchNode;while(pTemp != pTemp->father->left){pTemp = pTemp->father;}return pTemp->father;}void deleteNode(pNode& pdeleteNode){/* 1.判斷該節點的個數,如果節點的個數大於或等於1,則直接將該節點的個數-1 */if (1 < pdeleteNode->times){pdeleteNode->times--;return;}/* 2.如果該節點只有一個,那麼考慮刪除 *//* 2.1 如果該節點沒有孩子,則直接刪除 */pNode pTemp = NULL;if ((NULL == pdeleteNode->left) && (NULL == pdeleteNode->right)){changeFatherChildNode(pdeleteNode, pTemp);}/* 2.2 如果該節點只有一個孩子,那麼直接用該孩子代替該節點 */else if ((NULL == pdeleteNode->left) && (NULL != pdeleteNode->right)){changeFatherChildNode(pdeleteNode, pdeleteNode->right);}else if ((NULL == pdeleteNode->right) && (NULL != pdeleteNode->left)){changeFatherChildNode(pdeleteNode, pdeleteNode->left);}/* 2.3 如果該節點有兩個孩子,那麼考慮用該節點的前驅或者後繼來代替該節點。在此,我們選擇用前驅代替該節點 */else{pTemp = findPredecessor(pdeleteNode);pNode pRightChild = pdeleteNode->right;changeFatherChildNode(pdeleteNode, pTemp);pTemp->right = pRightChild;}}void changeFatherChildNode(pNode& pdeleteNode, pNode& pNewChildNode){if (pdeleteNode == pdeleteNode->father->right){pdeleteNode->father->right = pNewChildNode;}else{pdeleteNode->father->left = pNewChildNode;}}代碼運行結果:
二叉搜尋樹建立、插入、刪除、前繼節點、後繼節點之c++實現