题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
   10
  / /
  6  14
 / / / /
4  8 12 16
 转换成双向链表
4=6=8=10=12=14=16。
 
 首先我们定义的二元查找树 节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
算法思想:
在二元查找树对应的有序双向链表中,
节点的左指针指向二元查找树中对应节点的左子树中最大节点
节点的右指针指向二元查找树中对应节点的右子树中最小节点
根据上述思想,以树的根节点为分界点,生成两个单向链表:
单链表1:根节点的左子树转换成一个左指针连接成的单向链表;该链表中只能有左指针,如果得到的是右指针,则转换成对应的左指针。
单链表2:根节点的右子树转换成一个右指针链接成的单向链表;该链表中只能有右指针,如果得到的是左指针,则转换成对应的右指针。
双向链表的左右指针转换关系:
p->m_pLeft = q  等价于 q->m_pRight =
p->m_pRight = q 等价于 q->m_pLeft =
然后在分别将上述两个单向链表补充完整,最终形成一个双向链表。
程序源码:
tree.h
/********************************
tree.h : 定义树的头文件
********************************/
#ifndef __TREE_H__
#define __TREE_H__
typedef int ElemType;
struct BSTreeNode{
ElemType m_nValue;
struct BSTreeNode *m_pLeft;
struct BSTreeNode *m_pRight;
};
typedef struct BSTreeNode *pBSTreeNode;
typedef struct BSTreeNode *pSearchTree;
pSearchTree Insert(ElemType x,pSearchTree T);
void InOrderTree(pSearchTree T);
void PrintList(pBSTreeNode pHead);
void TreeToList(pSearchTree pT);
pBSTreeNode DList(pSearchTree pRoot);
#endif
tree.c
#include <stdio.h>
#include <stdlib.h>
#include "tree.h"
extern pBSTreeNode phead;
extern int root_val;
// 打印双向链表
void PrintList(pBSTreeNode pHead)
{
if (NULL == pHead)
{
return;
}
printf("\n%11s: ","List");
pBSTreeNode p = pHead;
do
{
printf("%d, ", p->m_nValue);
p = p->m_pRight;
}while(p != NULL);
printf("\n");
}
//判断节点是否为叶子节点
int IsLeafNode(pBSTreeNode pNode)
{
if (NULL == pNode)
return 0;
if ((NULL == pNode->m_pLeft) && (NULL == pNode->m_pRight))
return 1;
return 0;
}
//判断节点属于根节点的左子树还是右子树
int IsLTreeNode(pBSTreeNode pNode)
{
if (NULL == pNode)
return 0;
return ((pNode->m_nValue < root_val)? 1:0);
}
/*创建新节点*/
pBSTreeNode NewNode(ElemType x)
{
pBSTreeNode new_node;
new_node = (pBSTreeNode)malloc(sizeof(struct BSTreeNode));
if ( NULL == new_node){
printf("malloc error\n");
return NULL;
}else{
new_node->m_nValue = x;
new_node->m_pLeft = new_node->m_pRight = NULL;
}
return new_node;
}
/*在二叉树中插入节点*/
pSearchTree Insert(ElemType x, pSearchTree T)
{
if (NULL == T)
{
T = NewNode(x); //创建新节点
}
else {
if ( x < T->m_nValue){ //如果新插入的节点小于当前节点,则插入其左节点
T->m_pLeft = Insert(x, T->m_pLeft);
}
else{
if ( x > T->m_nValue){
T->m_pRight = Insert(x, T->m_pRight);//如果大于当前节点,则插入其右节点
}
else{
printf("x is already exsited\n"); //不允许重复节点
}
}
}
return T;
}
/*中序遍历二叉树*/
void InOrderTree(pSearchTree T)
{
if (NULL != T)
{
InOrderTree(T->m_pLeft);
printf("%d, ", T->m_nValue);
InOrderTree(T->m_pRight);
}
}
/**********************************************
查找节点在双向链表中对应的左指针指向的节点
以该节点为根节点的左子树中最大的节点
*********************************************/
pBSTreeNode FindLeft(pSearchTree pT)
{
pBSTreeNode p;
if (NULL == pT){
return NULL;
}
if ( NULL == pT->m_pRight){ //如果当前节点不存在右节点,则当前节点就是所找节点
return pT;
}
else{
p = FindLeft(pT->m_pRight);
}
return p;
}
/******************************************
查找节点在双向链表中对应的右指针指向的节点
以该节点为根节点的右子树中最小的节点
*******************************************/
pBSTreeNode FindRight(pSearchTree pT)
{
pBSTreeNode p;
if (NULL == pT){
return NULL;
}
if (NULL == pT->m_pLeft){ //如果当前节点不存在左节点,则当前节点即为所找节点
return pT;
}
else{
p = FindRight(pT->m_pLeft);
}
return p;
}
/**********************************************
以根节点为分界,生成两个单向链表。
比根节点小的节点,生成用左指针连接而成的单链表
比根节点大的节点,生成用右指针链接而成的单链表
***********************************************/
void TreeToList(pSearchTree pT)
{
pSearchTree L,R;
if (NULL == pT)
return ;
L = pT->m_pLeft; //先保存当前节点的左右子树
R = pT->m_pRight;
pT->m_pLeft = FindLeft(pT->m_pLeft); //查找当前节点在双向链表中左指针指向的节点
/***********************************************
如果查找到的节点非空而且属于根节点的右子树,则进行如下处理:
1.将左指针指向改为右指针指向;
2.删除左指针,避免结构中出现环
************************************************/
if (pT->m_pLeft && !IsLTreeNode(pT->m_pLeft)){
pT->m_pLeft->m_pRight = pT; //将左指针指向改为对应的右指针指向
pT->m_pLeft = NULL; //删除左指针
}
pT->m_pRight = FindRight(pT->m_pRight); //查找当前节点在双向链表中右指针指向的节点
/***********************************************
如果查找到的节点非空而且属于根节点的左子树,则进行如下处理
1.将右指针指向改为左指针指向;
2.删除右指针,避免结构中出现环
************************************************/
if ( pT->m_pRight && IsLTreeNode(pT->m_pRight)){
pT->m_pRight->m_pLeft = pT; //将右指针指向改为对应的左指针指向
pT->m_pRight = NULL; //删除右指针
}
TreeToList(L);
TreeToList(R);
}
/****************************************
将根节点连接的两个单向链表生成一个双向链表
函数返回双向链表的头结点
*****************************************/
pBSTreeNode DList(pBSTreeNode pT)
{
if (NULL == pT)
{
return NULL;
}
pBSTreeNode p = pT;
pBSTreeNode q = p->m_pLeft;
/*补齐左指向的单向链表的右指向*/
while (q != NULL){
q->m_pRight = p;
p = q;
q = p->m_pLeft;
}
phead = p;
p = pT;
q = p->m_pRight;
/*补齐右指向的单向链表的左指向*/
while ( q != NULL){
q->m_pLeft = p;
p = q;
q = p->m_pRight;
}
return phead;
}
main.c
/****************************************
author: Rita Huang
date: 2012.12.06
desc: 由二元查找树生成有序双向链表
算法思想:
在二元查找树对应的有序双向链表中,
节点的左指针指向二元查找树中对应节点的左子树中最大节点
节点的右指针指向二元查找树中对应节点的右子树中最小节点
根据上述思想,
1)先将根节点的左子树转换成一个左指针连接成的单向链表;
2)将根节点的右子树转换成一个右指针链接成的单向链表;
3)再分别将上述两个单向链表补充完整,最终形成一个双向链表。
***************************************/
#include <stdio.h>
#include <stdlib.h>
#include "tree.h"
pBSTreeNode phead; //生成的双向链表的头结点
int root_val = 0; //二元查找树根节点的值
int main()
{
FILE *fp;
int c;
pSearchTree T;
T = NULL;
fp = freopen("in.txt","r",stdin);
if (NULL == fp){
printf("open file in.txt failed\n");
exit(0);
}
while ( scanf("%d",&c) != EOF ){
T = Insert(c,T);
}
fclose(stdin);
printf("InOrderTree: ");
InOrderTree(T);
printf("\n");
if ( NULL != T )
root_val = T->m_nValue; //获取二元查找树根节点的值
TreeToList(T); /*二元查找树转换为两个单向链表*/
phead=DList(T); /*两个单向链表生成一个双向链表*/
PrintList(phead);
return 0;
}
  
测试数据 in.txt
39 93 84 21 35 193 10 6 8 18 9  86 12 3 20 11 23 92 
运行结果 
InOrderTree: 3, 6, 8, 9, 10, 11, 12, 18, 20, 21, 23, 35, 39, 84, 86, 92, 93, 193, 
       List: 3, 6, 8, 9, 10, 11, 12, 18, 20, 21, 23, 35, 39, 84, 86, 92, 93, 193,
