转载并参考July的博客http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html,万分感谢!
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表4<=>6<=>8<=>10<=>12<=>14<=>16。
分析:
要明白二元查找树和双向链表的各自特点
解一:
利用中序遍历,递归求解
/*Title: 1.把二元查找树转变成排序的双向链表:解一 Author: gocode Date: 2012-09-27*/ /* 10 / \ 6 14 / \ / \ 4 8 12 16 */ #include <stdio.h> #include <iostream> using namespace std; //如果不加std,则cout需要写成std::cout // 定义双向链表的节点 typedef struct BSTreeNode { int m_nValue; // 结点值 BSTreeNode *m_pLeft; // 左节点 BSTreeNode *m_pRight; // 右节点 } DoubleList; // 定义全局变量 DoubleList *pHead; DoubleList *pListIndex; // 辅助节点指针,指示双向链表的当前节点 //void convertToDoubleList(BSTreeNode * pCurrent); // 如果该函数放在ergodicBSTree函数之后,则此处必须声明convertToDoubleList // 二叉树转换成双向链表 void convertToDoubleList(BSTreeNode * pCurrent) { // 当前pCurrent结点是后加入的节点,它永远把pListIndex作为左节点, // 因为pListIndex是作为双向链表的当前节点指示器,所以pListIndex在pCurrent的左边,相应的pListIndex的右节点就是pCurrent pCurrent->m_pLeft = pListIndex; if (NULL != pListIndex) pListIndex->m_pRight = pCurrent; else pHead = pCurrent; // 建立双向链表的头节点 pListIndex = pCurrent; // 移动pListIndex指示到最新加入的节点 cout<<pCurrent->m_nValue<<" "; } // 创建二元查找树 void addBSTreeNode(BSTreeNode *& pCurrent, int value) { // 当前节点为空,则创建新节点并赋值 if (NULL == pCurrent) { BSTreeNode * pBSTree = new BSTreeNode(); pBSTree->m_pLeft = NULL; pBSTree->m_pRight = NULL; pBSTree->m_nValue = value; pCurrent = pBSTree; } // 当前节点不为空,则与value比较大小,决定放在左还是右 else { if ((pCurrent->m_nValue) > value) addBSTreeNode(pCurrent->m_pLeft, value); else if ((pCurrent->m_nValue) < value) addBSTreeNode(pCurrent->m_pRight, value); else { // 假定不允许有重复值节点存在 // cout<<"重复加入节点"<<endl; } } } // 遍历二元查找树中序将按照从小到大排序,符合要求 void ergodicBSTree(BSTreeNode * pCurrent) { if (NULL == pCurrent) return; // 左子树不为空,则遍历左子树 if (NULL != pCurrent->m_pLeft) ergodicBSTree(pCurrent->m_pLeft); // 节点接到链表尾部 convertToDoubleList(pCurrent); // 右子树不为空,则遍历右子树 if (NULL != pCurrent->m_pRight) ergodicBSTree(pCurrent->m_pRight); } // 打印双向链表 void displayDoubleList(DoubleList * pHead) { pListIndex = pHead; while(NULL != pListIndex) { cout<<pListIndex->m_nValue<<" "; pListIndex= pListIndex->m_pRight; } cout<<endl; } int main() { BSTreeNode * pRoot = NULL; addBSTreeNode(pRoot, 10); addBSTreeNode(pRoot, 4); addBSTreeNode(pRoot, 6); addBSTreeNode(pRoot, 8); addBSTreeNode(pRoot, 12); addBSTreeNode(pRoot, 14); addBSTreeNode(pRoot, 15); addBSTreeNode(pRoot, 16); cout<<"List the converting result: "<<endl; ergodicBSTree(pRoot); cout<<endl<<"List the converting DoublList: "<<endl; displayDoubleList(pHead); cout<<"End!"<<endl; getchar(); return 0; }
结果:
解二:
递归求解
/*Title: 1.把二元查找树转变成排序的双向链表:解二 Author: gocode Date: 2012-09-27*/ /* 10 / \ 6 14 / \ / \ 4 8 12 16 */ #include <iostream> using namespace std; // Node类 class Node{ public: int data; Node *left; Node *right; Node(int d = 0, Node *lr = NULL, Node *rr = NULL):data(d), left(lr), right(rr){} }; // 创建二叉查找树 // 先创建左右节点,然后创建父节点 Node *create() { Node *root; /*Node *p7 = new Node(7); Node *p9 = new Node(9); Node *p8 = new Node(8, p7, p9);*/ Node *p4 = new Node(4); Node *p8 = new Node(8); Node *p6 = new Node(6, p4, p8); Node *p12 = new Node(12); Node *p16 = new Node(16); Node *p14 = new Node(14, p12, p16); Node *p10 = new Node(10, p6, p14); root = p10; return root; } // 改变链接 Node *change(Node *p, bool asRight) { if (!p) return NULL; // 遍历左子树 // 把左孩子与父节点的关系调整成双向链表的左右链接 Node *pLeft = change(p->left, false); if (pLeft) pLeft->right = p; p->left = pLeft; // 遍历右子树 // 把右孩子与父节点的关系调整成双向链表的左右链接 Node *pRight = change(p->right, true); if (pRight) pRight->left = p; p->right = pRight; // 最关键的一段程序,需要仔细理解 // 如果是右孩子,并且有左子孩子,则一直移动当前指针到左叶子节点 // 如果是左孩子,并且有右子孩子,则一直移动当前指针到右叶子结点 // 比如在构建12<=>14<=>16的时候,节点14是根节点10的右孩子asRight==true,它有左子孩子,此时当前指针一直向左移动到最左子孩子12上,12是根节点的后继 // 比如在构建4<=>6=>8的时候,节点6是根节点10的左孩子asRight==false,它有右子孩子,此时当前指针一直向右移动到最右子孩子8上,8是根节点的前驱 // 比如在构建6<=>10<=>14的时候,节点10是根节点asRight可为ture或false,此时asRight==false,有右子孩子,此时当前指针一直向右移动到最右子孩子16上 Node *r = p; if (asRight) {// 右子树的开头节点是根节点的后继,即左叶子结点 while (r->left) r = r->left; }else{// 左子树的最末尾节点是根节点的前驱,即右叶子结点 while (r->right) r = r->right; } return r; } void main(){ Node *root = create(); Node *tail = change(root, false); // 给出root节点,但是asRight==false,则返回二叉查找树最末尾节点 while (tail) { cout << tail->data << " "; tail = tail->left; } cout << endl; root = create(); Node *head = change(root, true); // 给出root节点,但是asRight==ture,则返回二叉查找树最开头节点 while (head) { cout << head->data << " "; head = head->right; } cout << endl; getchar(); }结果:
总结:
解一在构建二叉查找树使用了addBSTreeNode函数,此方法可以方便的自动建立二叉查找树的关系。这比解二一个节点一个节点手工建立要省事,而且可以复用。解一利用中序遍历时,拆解重构左右链接从而构造出排序的双向链表。解二代码量少,可以通过改变asRight变量控制双向链表的输出。这充分利用了链表的灵活性,更是建立在对二叉查找树的遍历和链表构建有充分的正确认识。从理解难易程度看,解一更好理解些。