大三党忙于应付考试,很久没写也没看算法文章了,鉴于本人对算法兴趣浓厚,所以忙里偷闲,每天做几道精选的算法题。
本题源于百度面试,难度不大,描述如下:
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
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序列,那么完全可以修改一下中序遍历,达到调整节点的效果,这里就不给出代码实现了。