二叉查找树转变成排序的双向链表

大三党忙于应付考试,很久没写也没看算法文章了,鉴于本人对算法兴趣浓厚,所以忙里偷闲,每天做几道精选的算法题。

本题源于百度面试,难度不大,描述如下:

题目: 
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。 
要求不能创建任何新的结点,只调整指针的指向。 

        10 
        /    \ 
      6    14 
    /    \   /   \ 
  4    8 12 16 

转换成双向链表 
4=6=8=10=12=14=16。


思考逻辑:我们知道树是递归定义的,而二叉查找树又是一棵有序的二叉树,很容易想到利用递归的思想去解决。这里给出一种用分治的想法解决思路:分治,即分而治之,divided and conquer,divided很简单,将问题分解成若干个子问题,显然这道题的子问题就是将左子树,右子树转化为DBList,而分治的难点在conquer,考虑到递归解决子问题,例如以上述题目中所给二叉树为例,10的左子树返回的结果将是4=6=8,head指向4,tail指向8,而10的右子树则返回12=14=16,head指向12,tail指向16,那么,conquer的时候,只需要将10,也就是root的左孩子指向左子树返回的tail,有孩子指向右子树返回的head,再调整左子树返回的tail右孩子指向root,右子树返回的head左海子指向root,问题便解决了。输出双向链表,只需按照返回的head头指针,遍历head->rchild即可。

以下给出C/C++代码:(感谢阿财提供的代码,精简如斯)

struct BSTreeNode
{
	int m_nValue;	
	BSTreeNode *m_pLeft;
	BSTreeNode *m_pRight;
};

BSTreeNode * treeToLinkedList(BSTreeNode*  root)
{
	BSTreeNode *head, *tail;
	helper(head,tail,root);
	return head;
}

void helper(BSTreeNode *&head, BSTreeNode *&tail, BSTreeNode *root)
{
	BSTreeNode *lt, *rh;
	if(root == NULL)
	{
		head = NULL, tail = NULL;
		return ;
	}
	helper(head,lt,root->m_pLeft);
	helper(rh,tail,root->m_pRight);
	if(lt != NULL)
	{
		lt->m_pRight = root;
		root->m_pLeft = lt;	
	}
	else
	{
		head = root;
	}

	if(rh != NULL)
	{
		root->m_pRight = rh;
		rh->m_pLeft = root;
	}
	else
	{
		tail = root;
	}
}

很不规范,纯粹是为了方便的main函数测试:

int main()
{
	BSTreeNode node4,node6,node8,node10,node12,node14,node16;
	node4.m_nValue = 4;
	node6.m_nValue = 6;
	node8.m_nValue = 8;
	node10.m_nValue = 10;
	node12.m_nValue = 12;
	node14.m_nValue =14;
	node16.m_nValue = 16;

	node10.m_pLeft = &node6;
	node10.m_pRight = &node14;
	
	node6.m_pLeft = &node4;
	node6.m_pRight = &node8;

	node14.m_pLeft = &node12;
	node14.m_pRight = &node16;

	node4.m_pLeft = node4.m_pRight = NULL;
	node8.m_pLeft = node8.m_pRight = NULL;
	node12.m_pLeft = node12.m_pRight = NULL;
	node16.m_pLeft = node16.m_pRight = NULL;
	
	 BSTreeNode *head = treeToLinkedList(&node10);
	 while(head != NULL)
	 {
		 printf("value:%d\n",head->m_nValue);
		 head = head->m_pRight;
	 }

	 return 0;
}
结果如图:


本题暂时归纳到这里,如有不足或者错误之处,望各位道友斧正。


updated 2014/10/14

上面的程序采用的是内部调整结构,然后head和tail做引用传递,处理上更为方便一些,这里再给出一种思路:

typedef struct _BSTreeNode
{
    int m_nValue;
    struct _BSTreeNode *m_pLeft;
    struct _BSTreeNode *m_pRight;
}BSTreeNode;

BSTreeNode * treeToLinkedList(BSTreeNode *root, int opcode)
{
    BSTreeNode *head,*tail;
    if(root == NULL)
    {
        head = tail = NULL;
        return NULL;
    }

    tail = treeToLinkedList(root->m_pLeft,1);
    head = treeToLinkedList(root->m_pRight,2);
    if(tail != NULL)
    {
        root->m_pLeft = tail;
        tail->m_pRight = root;
    }
    else
    {
        tail = root;
    }

    if(head != NULL)
    {
        root->m_pRight = head;
        head->m_pLeft = root;
    }
    else
    {
        head = root;
    }

    BSTreeNode *pTemp1,*pTemp2;
    if(opcode == 1)
    {
        return head;
    }
    else if(opcode == 2)
    {
        return tail;
    }
    else
    {
        pTemp1 = root->m_pLeft;
        pTemp2 = root;
        while(pTemp1 != NULL)
        {
            pTemp2 = pTemp1;
            pTemp1 = pTemp1->m_pLeft;
        }
        return pTemp2;
    }
}

分支结构中1代表左子树,2代表右子树,处理时,针对左子树则返回head,针对右子树则返回tail(想想为什么是倒回来的?),针对根节点,则需要遍历到二叉排序树最小值,easy(调用时第二个参数写^[1,2]即可)。

想必有很多读者也想到了中序遍历,对于二叉排序树,中序遍历输出即为assending序列,那么完全可以修改一下中序遍历,达到调整节点的效果,这里就不给出代码实现了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值