二叉树

搜索二叉树和双向链表

 一.问题描述

    输入一颗二叉搜索树,将该二叉搜索树转化为一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

    二叉树的结点结构如下:

    

[cpp]  view plain  copy
  1. template<class T>  
  2. struct TreeNode  
  3. {  
  4.     TreeNode(const T& data)  
  5.         :_data(data)  
  6.         ,_left(NULL)  
  7.         ,_right(NULL)  
  8.     {}  
  9.     T _data;  
  10.     TreeNode<T> *_left;  
  11.     TreeNode<T> *_right;  
  12. };  

 二.问题分析

 1.什么是二叉搜索树?

     二叉搜索树也叫二叉查找树/二叉排序树:

它或者是一颗空树,或者是满足如下性质的二叉树:若该树的左子树不为空,则左子树上所有节点的值均小于根结点的值;若该树的右子树不为空,则右子树上所有节点的值均大于根结点的值。

 2.二叉搜索树和双向链表的关系?

     二叉搜索树具有二叉树的基本性质,就是它具有两个指针分别指向它的左右孩子;

在双向链表中每个结点也具有两个指针分别指向该结点的前驱结点和后继结点。

由于这两种数据结构具有相似的性质,所以实现二叉搜索树转化为双向链表是可行的。

由上述二叉搜索树的概念可知,在搜索二叉树中,左子树结点的值总是小于父结点的值,右子树结点的值总是大于父结点的值,而要求是将二叉搜索树转化为有序的双向链表,所以在实现转化的过程中我们可以将原来指向左子树的结点转化为前驱结点,将原来指向右子树的结点转化为指向后继的结点。

     此时转化的基本思路已经理解了,那仫如何实现转化呢?

     试想一下如果我们对该二叉搜索树进行中序遍历时会发现什仫?通过验证中序遍历的序列正好是我们所需要的有序列,那仫相信读者已经清楚了将二叉搜索树转化为双向链表的基本步骤了,下面就让我们用代码实现吧!!!

 三.代码实现

 方法一.递归的方式,一分为三--根结点,左子树和右子树,在把左右子树都转化为有序的双向链表之后再将左右子树和根结点连接起来

     

[cpp]  view plain  copy
  1. Node * Convert(Node *root)  
  2. {  
  3.     //last指向双向链表的尾结点  
  4.     Node *last=NULL;        
  5.     ConvertNode(_root,&last);  
  6.     //从尾结点向前遍历找到头结点返回  
  7.     Node *head=last;  
  8.     while(head && head->_left)  
  9.     {  
  10.         head=head->_left;  
  11.     }  
  12.     return head;  
  13. }  
  14. void ConvertNode(Node *root,Node **last)  
  15. {  
  16.     if(NULL == root)  
  17.         return ;  
  18.     Node *cur=root;  
  19.     if(cur->_left)  
  20.     {  
  21.         ConvertNode(cur->_left,last);  
  22.     }  
  23.     cur->_left=*last;  
  24.     if(*last)  
  25.     {  
  26.         (*last)->_right=cur;  
  27.     }  
  28.     *last=cur;  
  29.     if(cur->_right)  
  30.     {  
  31.         ConvertNode(cur->_right,last);  
  32.     }  
  33. }  

  方法二.利用中序遍历和队列先进先出的特性,将该二叉搜索树进行中序遍历将中序遍历序列放入一个队列中,

然后顺序从队列中取出再进行指针的重新连接,并用一个prev指针始终指向前一个结点保证指针连接的时候可以快速的找到前一个结点。下图是我画的一个简单的转化图形:

   

    

[cpp]  view plain  copy
  1. //按照中序遍历的方式将该序列放入队列中  
  2. void InOrderToQueue(Node *root,queue<Node *>& q)  
  3. {  
  4.     if(NULL == root)  
  5.         return ;  
  6.     InOrderToQueue(root->_left,q);  
  7.     q.push(root);  
  8.     InOrderToQueue(root->_right,q);  
  9. }  
  10. Node *ConvertNoR(Node *root)  
  11. {  
  12.     Node *head=root;  
  13.     queue<Node *>q;  
  14.     InOrderToQueue(root,q);  
  15.     if(q.empty())  
  16.         return head;  
  17.     head=q.front();  
  18.     q.pop();  
  19.     Node *prev=head;  
  20.     prev->_left=NULL;  
  21.     Node *cur=NULL;  
  22.     while (!q.empty())  
  23.     {  
  24.         cur=q.front();  
  25.         q.pop();  
  26.         prev->_right=cur;  
  27.         cur->_left=prev;  
  28.         prev=cur;  
  29.     }  
  30.     prev->_right=NULL;  
  31.     return head;  
  32. }  

   在实现了这两种思路之后我又在思考是否可以通过栈的这种数据结构来实现辅助转化,类似中序遍历的非递归的方式:先找到该树的最左节点并且在找的时候按照顺序将对应结点入栈,根据二叉搜索树的性质,我们所找到的最左结点就是所有节点中值最小的结点了;如果栈不为空则连接。

   源码我已经上传github,在这里给出链接地址:

   CTTCassie/DataStruct: 算法之旅  https://github.com/CTTCassie/DataStruct

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值