學習二叉搜尋樹首先來看看二分查詢。什麼是二分查詢呢?在乙個有序數列中,找到某一元素的索引。沒錯只有乙個有序數列, 才能使用二分查詢法(排序的作用),二分查詢法的思想在2023年提出,但是第乙個沒有bug的二分查詢法在2023年才出現。
其思想就是每次將查詢元素與陣列中間位置比較,如果查詢元素比中間元素小,說明查詢元素在左邊,然後遞迴下去查詢左邊,否則查詢右邊。
// 二分查詢法, 在有序陣列arr中, 查詢target
// 如果找到target, 返回相應的索引index
// 如果找不到target, 返回-1
template int binarysearch(t arr, int n, t target)
}
2.1 二分搜尋樹的優勢
高效:不僅可以查詢資料;還可以高效地插入,刪除資料-動態維護資料;
還可以方便地回答很多資料之間關係問題,比如最大值、最小值、排名第幾的元素、num排名第幾等等問題
2.2 二分搜尋樹的定義1. 二分搜尋樹是乙個二叉樹
2. 每乙個節點的鍵值大於左孩子,每乙個節點的鍵值小於右孩子,以左右孩子為根的子樹仍為二分搜尋樹
template class bst
};node *root;
int count;//size of bst
public:
bst()
int size()
bool isenpty()
};
假設插入元素為e, 首先將e與根節點比較大小,如果e小於根節點的值,此時如果左孩子不存在,則元素e為根節點左孩子,否則再與左孩子比較;如果e大於根節點的值,如果右孩子不存在,則元素e為根節點的右孩子,否則再與右孩子的值比較;如果e等於根節點的值,則覆蓋根節點。
//插入乙個節點
void insert(key key, value value)
private:
//向以node為根的二叉搜尋樹中插入節點(key, value)
//返回插入新節點後的二叉搜尋樹的根節點
node* insert(node *node, key key, value value)
4. 查詢乙個節點
假設查詢元素為e, 首先將e與根節點比較大小,如果e小於根節點的值,如果左孩子不存在,則查詢失敗,否則再與左孩子比較;如果e大於根節點的值,如果右孩子不存在,則查詢失敗,否則再與右孩子的值比較;如果e等於根節點的值,則查詢成功。
//搜尋鍵為key的值
//search的返回值可以為節點即node *,但是node不能為private,不符合封裝的特性
//返回值value,可能會出現查詢不到這個問題
//所以最後使用value *這種方法,這樣當查詢不到,可以返會null
value* search(key key)
//搜尋以node為根節點的二叉搜尋樹中鍵值為key的值
value* search(node* node, key key)
前序遍歷:先訪問當前節點,再依次遞迴訪問左右子樹
中序遍歷:先遞迴訪問左子樹,再訪問自身,再遞迴訪問右子樹
後續遍歷:先遞迴訪問左右子樹,再訪問自身
5.1 前序遍歷
//二叉搜尋樹的前序遍歷
//二叉搜尋樹的中序遍歷
void inorder()
// 以node為根的二叉搜尋樹的中序遍歷
void inorder(node *node)
}
5.2 中序遍歷//二叉搜尋樹的中序遍歷
void inorder()
// 以node為根的二叉搜尋樹的中序遍歷
void inorder(node *node)
}
5.3 後序遍歷//二叉搜尋樹的後續遍歷
void postorder()
// 以node為根的二叉搜尋樹的後序遍歷
void postorder(node *node)
}
5.4 析構函式
析構函式是為了釋放二分搜尋樹每個節點的記憶體,必須要遍歷所有的節點, 三種遍歷方式,只有後序遍歷可以按照順序釋放空間,由於其餘遍歷均會釋放父親節點內存在子節點之前,導致無法找到子節點的記憶體位置。
~bst()
//析構函式呼叫, 釋放二叉搜尋樹的記憶體, 通過後序遍歷來實現
void destroy(node *node)
}
對於二叉樹的廣度優先遍歷就是層序遍歷,使用佇列實現。首先將根節點入隊,此時佇列有乙個元素,然後pop出根節點,將根節點的左右子節點入隊,判斷佇列是否為空,不為空繼續pop節點,依次遞迴下去。
//二叉搜尋樹的層序遍歷
void levelorder()
}
7.1 刪除最大值和最小值
這裡以最小值為例,從根節點出發,向其左子樹遞迴遍歷,一旦某一節點的左孩子為null, 說明該節點為最小值,此時只需要將該節點的父節點的左孩子指標指向該節點的右孩子(可能為null)即可。
//刪除最小值
void removemin()
//刪除最大值
void removemax()
//刪除最小值
node* removemin(node* node)
node->left = removemin(node->left);
return node;
}//刪除最大值
node* removemax(node* node)
node->right = removemax(node->right);
return node;
}
7.2 刪除任意節點
在刪除最大值最小值中已經可以看出,刪除只有乙個孩子的節點或沒有孩子的節點是非常容易的,比如,如果刪除節點只有右孩子,那麼只需要將右孩子放在當前節點的位置即可完成刪除。
當遇到刪除節點兩個孩子都存在時,我們可以找到
右子樹的最小值(左子樹的最大值)來代替被刪除的節點,該過程描述如下:
通過minimum函式找到右子樹最小節點
將最小節點放在被刪除的位置
將最小節點(已經在被刪除的位置)的左孩子指向被刪除節點的左孩子
將最小節點右孩子指向removemin(node->right),node為被刪除指標
//刪除乙個值
void remove(key key)
//刪除掉以node為根的二分搜尋樹中鍵值為key的節點
//返回刪除節點後新的二分搜尋樹的根
node* remove(node* node, key key)
if(key < node->key)
else if(key > node->key)
else
if(node->right == null)
//這裡新建乙個node的原因在於removemin(node->right)會刪除該節點
node *successor = new node(minimum(node->right));
successor->right = removemin(node->right);
successor->left = node->left;
delete node;//這裡沒有count--的原因在與,removemin(node->right)會減
return successor;}}
同樣的資料,可以對應不同的二分搜尋樹中,二分搜尋樹可能退化為鍊錶
當二分搜尋樹退化為鍊錶會比鍊錶實現效率低,原因在於
當二分搜尋樹退化為鍊錶會比鍊錶實現效率低,原因在於
同樣的資料,可以對應不同的二分搜尋樹中,二分搜尋樹可能退化為鍊錶。當二分搜尋樹退化為鍊錶會比鍊錶實現效率低,原因在於二分搜尋樹處理節點時會處理兩個指標而且二分搜尋樹採用了遞迴的形式。
二叉搜尋樹 二叉搜尋樹
題目 二叉搜尋樹 time limit 2000 1000 ms j a others memory limit 32768 32768 k j a others total submission s 6945 accepted submission s 3077 problem descripti...
二叉搜尋樹學習
二叉搜尋樹是指具有以下性質的二叉樹 1.其父節點的左孩子一定大於父節點,右孩子一定小於父節點 2.具有普通二叉樹的性質 二叉搜尋樹執行各種操作的時間複雜度與樹的高度成正比,而線性鍊錶的操作則與鍊錶長度成正比。因此,對於有相同結點數的二叉搜尋樹和鍊錶,樹的操作花費代價 小於鍊錶,但實現略複雜於鍊錶。下...
二叉搜尋樹 修剪二叉搜尋樹
第一反應是重構,看來別人的解答發現,其實不用重構那麼複雜。treenode trimbst treenode root,int low,int high if root val high 下一層處理完左子樹的結果賦給root left,處理完右子樹的結果賦給root right。root left ...