void insert1(Node* pRoot, Node* pAdd) { bool bLeft = pAdd->key < pRoot->key; Node* pNextRoot = bLeft ? pRoot->left : pRoot->right; if(pNextRoot) insert1(pNextRoot, pAdd); else { pAdd->parent = pRoot; if(bLeft) pRoot->left = pAdd; else pRoot->right = pAdd; } }
12.3-2 insert過程途經n個節點後遇到了空節點,便將待插入的元素安放上去了,接下來的search過程先路過一樣的n個節點,最後與第n+1個節點比較發現相同,因而search完畢。html
12.3-3 由12.3-2可得,insert過程和search過程同樣,是O(h)-time的,h表明樹的高度。ios
設一共有n個節點,構建一棵樹須要調用n次insert,這個過程耗時O(nh),後續的中序遍歷耗時是O(n)。因此排序過程的時間複雜度是由構建過程決定的。算法
最壞的狀況集合按照正序或者倒序排列,則h = n,總時間是O(n^2)。性能
最好狀況是集合按照層次遍歷順序排列,最後構建成一棵徹底二叉樹,h = lg(n) ,總時間爲O(n*lg(n))。ui
12.3-4 從同一棵二叉搜索樹上先刪除x再刪除y,和先刪除y再刪除x,最終獲得的樹必定是同樣的嗎?spa
不必定,舉個反例:翻譯
//如今有一棵樹,上面有1,2,3,4四個節點: 2 / \ 1 4 / 3 //若是 先刪除1 再刪除2,結果是: 2 2 4 / \ \ / 1 4 --> 4 --> 3 / / 3 3 //若是 先刪除2 再刪除1,結果是: 2 3 3 / \ / \ \ 1 4 --> 1 4 --> 4 / 3 //獲得的結果是不同的。
下面說說,我是怎樣想到這個反例的。code
要刪除x,根據刪除的規則,若是x沒有孩子,直接刪除;若是x只有一個孩子,就用惟一的孩子頂替x的位置;若是x有兩個孩子,就將x的後繼節點s頂替x的位置。htm
從這個規則中能夠發現,x有幾個孩子會影響到x的接班人人選。若是刪除的順序能夠影響到刪除x時候x的孩子個數,就會影響到最終樹的形狀。blog
那麼,y在x的什麼位置上,刪除y會對x孩子個數形成影響呢?y自己就是x的一個孩子,並且y沒有孩子。下面分左孩子和右孩子討論。由於x只有一個孩子y的狀況太簡單,也不能成爲反例,不作討論,下面對x有兩個孩子的狀況進行分析。稱以x爲根的樹爲X樹。
若是y是x的左孩子,先刪y,刪除以後,x的孩子個數變成1,此時刪除x,X樹被其右子樹取代,X樹的根變爲x的右孩子。反過來,先刪除x,此時x有兩個孩子,X樹的根變爲x的後繼,只要其後繼不是它的右孩子自己,那麼結果就是不同的。這也就是上面給出的反例。
若是y是x的右子樹,先刪y,再刪x,X樹被x左子樹取代,先刪x,y頂替x的位置,再刪y,X樹仍然被x的左子樹取代,結果是同樣的,不能做爲反例。
還有一種可能,刪除x,x的接班人是本身的後繼s,原以s爲根的樹S會發生改變,從S樹上節點可否找出一個y做爲反例,我暫時沒有想清楚。
12.3-5 二叉搜索樹的每一個節點保存「後繼」,「左孩子」,「右孩子」三個屬性,在O(h)時間內實現insert delete search。(這道題在《算法導論》第三版的中文版翻譯有誤)
爲何要把「父親」屬性替換成「後繼」屬性呢?相比保存「父親」,保存「後繼」屬性的優點在於查找後繼節點時間O(1),排序雖然都是O(n)可是常數項較小。執行這兩種操做時,性能至關於單向鏈表。而執行插入、刪除、查找操做時,性能至關於二叉樹。
咱們先來總結一下這些操做須要讀寫哪些屬性。
insert操做須要修改的有:父親節點的孩子屬性,前驅節點和新插入節點的後繼屬性
delete操做須要修改的有:父親節點的孩子屬性,前驅節點的後繼屬性
search操做須要讀取的有:節點的孩子屬性
根據上面的總結能夠發現,這道題的關鍵點是在O(h)時間內找到父親節點和前驅節點。
查找父親節點的作法是從Root向下逐級查找;若是當前節點沒有左孩子,那麼查找前驅節點的作法也是從Root向下查找,能夠和父親節點的查找工做合併起來。若是當前節點有左孩子,那麼前驅節點是左孩子的最大節點。
#include <iostream> #include <cassert> using namespace std; struct Node { int key; Node* succ; Node* left; Node* right; Node(int k):key(k),succ(nullptr),left(nullptr),right(nullptr){} }; Node* minimum(Node* pRoot); Node* parent_pred(Node* pRoot, int key, Node*& pPred);//爲新插入節點,找父親的同時從父親中找前驅 void insert(Node* pRoot, int key) { Node* pNew = new Node(key); Node* pPred; Node* pParent = parent_pred(pRoot, key, pPred); Node* pHead = minimum(pRoot); //upate parent's child if(key < pParent->key) pParent->left = pNew; else pParent->right = pNew; //update pPred's succ and pNew's succ if(pPred) { pNew->succ = pPred->succ; pPred->succ = pNew; } else { pNew->succ = pHead;//注意:這個頭結點必定要在插入以前獲取 } } Node* pred(Node* pNode); //從左子樹上找前驅 Node* parent(Node* pRoot, Node* pNode); //找父親 Node* parent_pred(Node* pRoot, Node* pNode, Node*& pPred); //爲已有節點,找父親的同時從父親中找前驅 void Delete(Node*& pRoot, Node* pDelete) { Node* pDeleteReplace = nullptr; if(!pDelete->left) { pDeleteReplace = pDelete->right;//no child //only right } else { pDeleteReplace = pDelete->left; //only left if(pDelete->right) //both { Node* pSucc = pDelete->succ; if(pSucc != pDelete->right) { Node* pSuccParent = parent(pRoot, pSucc); if(pSucc->key < pSuccParent->key) pSuccParent->left = pSucc->right; else pSuccParent->right = pSucc->right; pSucc->right = pDelete->right; } pSucc->left = pDelete->left; pDeleteReplace = pSucc; } } //update parent's child, pred's succ Node* pPred; Node* pDeleteParent = parent_pred(pRoot, pDelete, pPred); bool bLeft; if(pDeleteParent) bLeft = pDeleteParent->left == pDelete; if(pDeleteParent) { if(bLeft) pDeleteParent->left = pDeleteReplace; else pDeleteParent->right = pDeleteReplace; } else { pRoot = pDeleteReplace; } if(pPred) { pPred->succ = pDelete->succ; } } Node* search(Node* pRoot, int key) { Node* pCurrent = pRoot; int keyCurrent; while(pCurrent) { keyCurrent = pCurrent->key; if(key == keyCurrent) break; if(key < keyCurrent) pCurrent = pCurrent->left; else pCurrent = pCurrent->right; } return pCurrent; } Node* minimum(Node* pRoot) { Node* pMin = pRoot; while(pMin->left) { pMin = pMin->left; } return pMin; } Node* parent_pred(Node* pRoot, int key, Node*& pPred) { Node* pCurrent = pRoot; Node* pParent = nullptr; pPred = nullptr; while(pCurrent) { pParent = pCurrent; if(key < pCurrent->key) { pCurrent = pCurrent->left; } else { pCurrent = pCurrent->right; pPred = pParent; } } return pParent; } Node* parent(Node* pRoot, Node* pNode) { Node* pCurrent = pRoot; Node* pParent = nullptr; while(pNode != pCurrent) { assert(pCurrent); pParent = pCurrent; if(pNode->key < pCurrent->key) pCurrent = pCurrent->left; else pCurrent = pCurrent->right; } return pParent; } Node* parent_pred(Node* pRoot, Node* pNode, Node*& pPred) { Node* pCurrent = pRoot; Node* pParent = nullptr; pPred = nullptr; while(pNode != pCurrent) { assert(pCurrent); pParent = pCurrent; if(pNode->key < pCurrent->key) pCurrent = pCurrent->left; else { pCurrent = pCurrent->right; pPred = pParent; } } return pParent; } Node* pred(Node* pNode) { Node* pPred = pNode->left; while(pPred->right) pPred = pPred->right; return pPred; } void walk(Node* pRoot) { Node* pCurrent = minimum(pRoot); while(pCurrent) { cout << pCurrent->key << "\t"; pCurrent = pCurrent->succ; } cout << endl; } void test() { //build Node* pRoot = new Node(4); insert(pRoot, 2); insert(pRoot, 5); insert(pRoot, 1); insert(pRoot, 3); insert(pRoot, 7); insert(pRoot, 6); insert(pRoot, 8); walk(pRoot); //search cout << search(pRoot, 3)->key << endl; cout << search(pRoot, 6)->key << endl; cout << search(pRoot, 4)->key << endl; //delete Delete(pRoot, pRoot->right);//delete 5 Delete(pRoot, pRoot->left); //delete 2 Delete(pRoot, pRoot->left); //delete 3 Delete(pRoot, pRoot);// delete 4 Delete(pRoot, pRoot->left);//delete 1 walk(pRoot); //destroy Node* pCurrent = minimum(pRoot); while(pCurrent) { delete pCurrent; pCurrent = pCurrent->succ; } pCurrent = nullptr; } /*output 1 2 3 4 5 6 7 8 3 6 4 6 7 8 */