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 */