题目:输入一颗二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整指针的指向。
例如,将图1中的二叉搜索树转换成一个排序的双向链表:4=6=8=10=12=14=16。
图1 二叉搜索树
我们先来看一下这个题目该如何求解,然后再给出相应的源码。首先题目要求不能创造新的结点,那么,我们就只能使用树本身的结点,但是双向链表还有前驱和后继两个指针怎么办?答案是使用树的左孩子来做前驱指针,右孩子来做后继指针。又因为链表需要有序,所以使用中序遍历。
解体思路:
1)构造二叉查找树
2)定义一个全局指针变量,指向双向链表的首元素。
3)中序遍历二叉查找树,对于每次根节点的访问,修改其左孩子为链表的最后一个元素,修改其右孩子为NULL,那么这个元素的后继怎么办?由下一个元素来设置。
具体代码如下:
BSTree.h:
#ifndef _BSTREE_H_ #define _BSTREE_H_ typedef struct _tagBSTreeNode { int m_nVal; _tagBSTreeNode * m_pLeft; _tagBSTreeNode * m_pRight; }BSTreeNode; extern BSTreeNode * pHead; //追加树结点 int AppendNode(BSTreeNode **, int nNum); //打印树结点 void PrintTree(BSTreeNode ** ppTree); //释放树 void Clear(BSTreeNode ** ppTree); //转换为有序双链表 void InOrderTree(BSTreeNode ** ppTree); //销毁链表 void ClearList(BSTreeNode * pHead); //打印链表 void PrintList(BSTreeNode * pHead); //打印路径 void PrintPath(BSTreeNode * pHead, int nSum); #endifBSTree.cpp:
#include <stdlib.h> #include <stdio.h> #include "BSTree.h" //追加树结点 int AppendNode(BSTreeNode ** ppTree, int nNum) { BSTreeNode * pNewNode = NULL; if (!ppTree) return 0; if (*ppTree == NULL) { pNewNode = (BSTreeNode *)malloc(sizeof(BSTreeNode)); if (!pNewNode) return 0; pNewNode->m_nVal = nNum; pNewNode->m_pLeft = NULL; pNewNode->m_pRight = NULL; *ppTree = pNewNode; return 1; } if ((*ppTree)->m_nVal > nNum) { AppendNode(&((*ppTree)->m_pLeft), nNum); } else if ((*ppTree)->m_nVal < nNum) { AppendNode(&((*ppTree)->m_pRight), nNum); } else { printf("树结点已经存在. "); return 0; } return 1; } //打印树结点 void PrintTree(BSTreeNode ** ppTree) { if (!ppTree) return; if (*ppTree == NULL) { return; } if ((*ppTree)->m_pLeft) PrintTree(&((*ppTree)->m_pLeft)); if (*ppTree) printf("%d", (*ppTree)->m_nVal); if ((*ppTree)->m_pRight) PrintTree(&((*ppTree)->m_pRight)); } //释放树 void Clear(BSTreeNode ** ppTree) { BSTreeNode * pLeftTree = NULL; BSTreeNode * pRightTree = NULL; if (!ppTree) return; if (*ppTree == NULL) return; if ((*ppTree)->m_pLeft) { pLeftTree = (*ppTree)->m_pLeft; Clear(&pLeftTree); } if ((*ppTree)->m_pRight) { pRightTree = (*ppTree)->m_pRight; Clear(&pRightTree); } if (*ppTree) { free(*ppTree); *ppTree = NULL; } return; } //以左孩子为前驱 //以右孩子为后继 BSTreeNode * pHead = NULL; void AppendNode2List(BSTreeNode * pInput) { BSTreeNode * pCurNode = NULL; if (pInput == NULL) return; if (!pHead) { pHead = pInput; pInput->m_pLeft = NULL; pInput->m_pRight = NULL; return; } pCurNode = pHead; while (pCurNode->m_pRight) { pCurNode = pCurNode->m_pRight; } pCurNode->m_pRight = pInput; pInput->m_pLeft = pCurNode; pInput->m_pRight = NULL; return ; } //打印树结点 void InOrderTree(BSTreeNode ** ppTree) { if (!ppTree) return; if (*ppTree == NULL) { return; } if ((*ppTree)->m_pLeft) InOrderTree(&((*ppTree)->m_pLeft)); if (*ppTree) AppendNode2List(*ppTree); if ((*ppTree)->m_pRight) InOrderTree(&((*ppTree)->m_pRight)); } void PrintList(BSTreeNode * pHead) { BSTreeNode * pCurNode = NULL; if (!pHead) return; pCurNode = pHead; printf(" 链表数据: "); while (pCurNode) { printf("%d ", pCurNode->m_nVal); pCurNode = pCurNode->m_pRight; } return; } void ClearList(BSTreeNode * pHead) { BSTreeNode * pCurNode = NULL; if (!pHead) return; while (pHead) { pCurNode = pHead->m_pRight; free(pHead); pHead = pCurNode; } return; }main.cpp:
//把二叉查找树转换为有序的双链表 //要求不可以创建新结点,只可以改变指针 //提示: //可以将左孩子用于指向前驱结点 //可以将右孩子用于指向后继结点 #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <stdio.h> #include <crtdbg.h> #include "BSTree.h" void main() { BSTreeNode * pTree = NULL; if (AppendNode(&pTree, 5) == NULL) { printf("追加失败. "); return; } if (AppendNode(&pTree, 4) == NULL) { printf("追加失败. "); return; } if (AppendNode(&pTree, 3) == NULL) { printf("追加失败. "); return; } if (AppendNode(&pTree, 2) == NULL) { printf("追加失败. "); return; } if (AppendNode(&pTree, 1) == NULL) { printf("追加失败. "); return; } PrintTree(&pTree); InOrderTree(&pTree); PrintList(pHead); ClearList(pHead); _CrtDumpMemoryLeaks(); system("pause"); return; }运行效果如图1所示:
图1 运行效果
图1是代码的运行效果,希望大家能够回去实践一下,加深体会。