【问题标题】:How to convert a binary search tree into a doubly linked list?如何将二叉搜索树转换为双向链表?
【发布时间】:2011-04-14 03:42:25
【问题描述】:

给定一个二叉搜索树,我需要将其转换为双向链表(通过以之字形顺序遍历),仅使用指向 C++ 中结构的指针,如下所示,

给定树:

                1
                |
        +-------+---------+
        |                 |
        2                 3
        |                 |
   +----+---+        +----+---+
   |        |        |        |
   4        5        6        7
   |        |        |        |
+--+--+  +--+--+  +--+--+  +--+--+
|     |  |     |  |     |  |     |
8     9  10    11 12    13 14    15

节点结构:

struct node
{
    char * data;
    node * left;
    node * right;
};

创建列表(之字形顺序):

1 <-> 3 <-> 2 <-> 4 <-> 5 <-> 6 <-> 7 <-> 15 <-> ... <-> 8

谁能帮帮我。

【问题讨论】:

  • 列表中节点的顺序重要吗?
  • “创建列表”部分是否应该是答案?我不知道他们将如何获得或想要该列表。如果结果是一个列表(1 2 3 4 ... 15),则更有意义。还是只是一个关卡遍历,每个关卡都从另一端开始?
  • @Nick:是的,我知道如何创建排序列表。但是这里我需要按照这个特定的顺序创建。
  • 这似乎是一个曲折的顺序。您应该在问题中提及这一点。
  • 你可以在实现 BFS 时使用堆栈和队列。我认为这应该可行。

标签: c++ data-structures linked-list


【解决方案1】:
/* * File: main.cpp * Author: viswesn * * Created on December 1, 2010, 4:01 PM */

struct node {

int item; 
struct node *left; 
struct node *right; 
    struct node *next; 
    struct node *prev; 
};

struct node *dlist = NULL;

struct node *R = NULL;

struct node* EQueue[10] = {'\0'};

int Etop = 0;

struct node* OQueue[10] = {'\0'};

int Otop = 0;

int level=-1;

struct node *insert(struct node *R, int item)

{

struct node *temp = NULL; 

if (R != NULL) { 
    if (item < R->item) { 
        R->left = insert(R->left, item); 
    } else { 
        R->right = insert(R->right, item); 
    } 
} else { 
    temp = (struct node *)malloc(sizeof(struct node)); 
    if (temp == NULL) { 
        return NULL; 
    } 
    temp->item = item; 
    temp->left = NULL; 
    temp->right = NULL; 
            temp->next = NULL; 
            temp->prev = NULL; 
    R = temp; 
} 
return R; 
}

void print(struct node *node)

{

if (node != NULL) { 

    print(node->left); 
    printf("%d<->", node->item); 
    print(node->right); 
} 
}

void EvenEnqueue(struct node *item) {

if (Etop > 10) { 
    printf("Issue in EvenEnqueue\n"); 
    return; 
} 
EQueue[Etop] = item; 
Etop++; 
}

void OddEnqueue(struct node *item)

{

if (Otop > 10){ 
    printf("Issue in OddEnqueue\n"); 
    return; 
} 
OQueue[Otop] = item; 
Otop++; 
}

int isEvenQueueEmpty() {

if (EQueue[0] == '\0') { 
    return 1; 
} 
return 0; 
}

int isOddQueueEmpty()

{

if (OQueue[0] == '\0') { 
    return 1; 
} 
return 0; 
}

void EvenDQueue()

{

int i = 0; 
for(i=0; i< Etop; i++) 
    EQueue[i]='\0'; 
Etop = 0; 
}

void OddDQueue()

{

int i = 0; 
for(i=0; i< Otop; i++) 
    OQueue[i]='\0'; 
Otop = 0; 
}

void addList() {

int counter = 0; 
struct node *item = NULL; 
if (level%2 == 0) { 
        /* Its Even level*/ 
        while(counter < Etop) 
        { 
            struct node *t1 = EQueue[counter]; 
            struct node *t2 = EQueue[counter+1]; 
            if ((t1!=NULL) && (t2!=NULL)) { 
                t1->next = t2; 
                t2->prev = t1; 
            } 
            counter++; 
        } 
        item = EQueue[0]; 
} else { 
        /* Its odd level */ 
        while(counter < Otop) 
        { 
            struct node *t1 = OQueue[counter]; 
            struct node *t2 = OQueue[counter+1]; 
            if ((t1!=NULL) && (t2!=NULL)) { 
                t2->next = t1; 
                t1->prev = t2; 
            } 
            counter++; 
        } 
         item = OQueue[Otop-1]; 
} 

if(dlist !=NULL) { 
    dlist->next = item; 
} 
item->prev = dlist; 
if (level%2 == 0) { 
    dlist = EQueue[Etop-1]; 
} else { 
    dlist = OQueue[0]; 
} 
}

void printTree()

{

int nodeCount = 0; 
int counter = 0; 

if (!isEvenQueueEmpty()) { 
    /* If even level queue is not empty */ 
    level++; 
    nodeCount = pow(2,level); 
            printf("["); 
    while(counter < nodeCount) { 
        if (EQueue[counter] != '\0') { 
            struct node *t = EQueue[counter];                                
            printf("%d<->", t->item); 
                            if (t->left != NULL) 
                                OddEnqueue(t->left); 
                            if (t->right != NULL) 
                                OddEnqueue(t->right);                
        } else { 
                        break; 
                    } 
        counter++; 
    } 
            addList(); 
            printf("]"); 
            EvenDQueue(); 
} 
counter = 0; 
if (!isOddQueueEmpty()){ 
            /* If odd level queue is not empty */ 
    level++; 
    nodeCount = pow(2,level); 
            printf("["); 
    while(counter < nodeCount){ 
        if (OQueue[counter] != '\0') { 
            struct node *t = OQueue[counter];                                 
            printf("%d<->", t->item); 
                            if (t->left != NULL) 
                                EvenEnqueue(t->left); 
                            if (t->right != NULL) 
                                EvenEnqueue(t->right); 
        } else { 
                        break; 
                    } 
        counter++; 
    } 
            addList(); 
            printf("]"); 
            OddDQueue(); 
} 
if (isEvenQueueEmpty() && isOddQueueEmpty()){ 
    return; 
} 
else { 
    printTree(); 
} 
}

void printLevel(struct node *node)

{

if (node == NULL) 
    return; 
EvenEnqueue(node); 
printTree(); 
    printf("\n"); 
}

void printList(struct node *item)

{

while(item!=NULL) { 
    printf("%d->", item->item); 
    item = item->next; 
} 
}

int main(int argc, char** argv) {

int a[]={20,30,40,12,2,15,18}; 
int size = sizeof(a)/sizeof(int); 
int i = 0; 

for(i=0; i< size; i++) { 
    R = insert(R, a[i]); 
} 
    printf("Inoder traversal - Binary tree\n"); 
print(R); 
printf("\n\n"); 
    printf("Level traversal - Binary tree\n"); 
printLevel(R); 
    printf("\n"); 
    printf("Double link list traversal - Binary tree\n"); 
    printList(R); 
    printf("\n"); 
return 0; 
}

【讨论】:

    【解决方案2】:

    让我们假设二叉树的根在第 0 层(偶数)。连续级别可以被认为是奇偶之间的交替(根的孩子在奇数级别,他们的孩子在偶数级别等)。 对于偶数级别的节点,我们将其子节点压入堆栈(这可以实现反向遍历)。对于奇数级别的节点,我们将其子节点推送到队列中(这可以实现前向遍历)。 子节点被推送后,我们删除下一个可用元素(堆栈顶部或队列前端)并递归调用根据移除的元素所在的位置,通过将级别更改为奇数或偶数来发挥作用。删除的元素可以插入到双向链表的末尾。伪代码如下。

    // S = stack [accessible globally]
    // Q = queue [accessible globally]
    //    
    // No error checking for some corner cases to retain clarity of original idea    
    void LinkNodes(Tree *T,bool isEven,list** l)
    {
    
         if(isEven) {
            S.push(T->right);
            S.push(T->left);
            while( !S.empty() ) {
                 t = S.pop();
                 InsertIntoLinkedList(l,t);
                 LinkNodes(t,!isEven);
            }
         } else {
            Q.enque(T->left);
            Q.enque(T->right);
            while( !Q.empty() ) {
                t = Q.deque();
                InsertIntoLinkedList(l,t);
                LinkNodes(t,isEven);
            }
         }
    }
    

    在调用函数中:

    bool isEven = true;
    list *l = NULL;           
    // Before the function is called, list is initialized with root element
    InsertIntoLinkedList(&l,T); 
    
    LinkNodes(T,isEven,&l);
    

    【讨论】:

      【解决方案3】:
      #include<iostream>
      #include<conio.h>
      using namespace std;
      
      class TreeNode
      {
      public:
          int info;
          TreeNode* right;
          TreeNode* left;
          //TreeNode* parent;
          TreeNode()
          {
              info = 0;
              right = left = NULL;
          }
      
          TreeNode(int info)
          {
              this -> info = info;
              right = left = NULL;
          }
      };
      
      class ListNode
      {
      public:
          int info;
          ListNode* next;
          ListNode()
          {
              info = 0;
              next = NULL;
          }
      
          ListNode(int info)
          {
              this -> info = info;
              next = NULL;
          }
      };
      
      TreeNode* root = NULL;
      ListNode* start;
      ListNode* end;
      
      void addTreeNode(TreeNode*);
      void convertTreeToList(TreeNode*);
      void printList(ListNode*);
      int findDepth(TreeNode*);
      
      int _tmain(int argc, _TCHAR* argv[])
      {
          start = end = new ListNode(0);
          char choice = 'y';
          int info;
          while(choice == 'y')
          {
              cout<<"Enter the info of new node:\n";
              cin>>info;
              addTreeNode(new TreeNode(info));
              cout<<"Want to add a new node to the tree?(y/n)\n";
              cin>>choice;
          }
      
          cout<<"Depth of the tree is: "<<findDepth(root);
      
          cout<<"Converting the tree into a doubly linked list....\n";
      
          convertTreeToList(root);
          printList(start->next);
          cin>>choice;
          return 0;
      }
      
      
      
      void addTreeNode(TreeNode* node)
       {
           if(!root)
           {
               root = node;
           }
           else
           {
               TreeNode* currRoot = root;
               while(1)
               {
                   if(node -> info >= currRoot -> info)
                   {
                       if(!currRoot -> right)
                       {
                           currRoot -> right = node;
                           break;
                       }
                       else
                       {
                           currRoot = currRoot -> right;
                       }
                   }
                   else
                   {
                       if(!currRoot -> left)
                       {
                           currRoot -> left = node;
                           break;
                       }
                       else
                       {
                           currRoot = currRoot -> left;
                       }
                   }
               }
           }
       }
      
       void convertTreeToList(TreeNode* node)
       {
           if(node -> left != NULL)
           {
               convertTreeToList(node -> left);
           }
      
               end ->next = new ListNode(node -> info);
               end = end -> next;
               end -> next = start;
      
      
      
               if(node -> right != NULL)
           {
               convertTreeToList(node -> right);
           }
      
       }
      
      
       void printList(ListNode* start)
       {
           while(start != ::start)
           {
               cout<<start->info<<" -> ";
               start = start -> next;
           }
           cout<<"x";
       }
      
       int findDepth(TreeNode* node)
       {
           if(!node)
           {
               return 0;
           }
           else
           {
               return (max(findDepth(node -> left), findDepth(node -> right)) + 1);
           }
       }
      

      您在此处获得的链接列表是单独链接和排序的。希望您可以轻松地更改此代码以获得双向链表。只需复制 - 粘贴此代码并编译即可。它可以正常工作。

      【讨论】:

        【解决方案4】:

        在下面的这个解决方案中,我使用了两个堆栈来交替存储关卡。 假设级别 0 的节点将存储在名称为 head1 的堆栈 1 中;现在在元素不为空时弹出元素并将元素推入堆栈 2。插入的顺序即左或右子节点将取决于级别。并更改每个级别的广告订单。

        node * convert_to_dll(node *p)
        {   
            static  int level=0;
            push_in_stack(p,&head1);
            printf("%d\n",p->data);
        
            while( head1!=NULL || head2!=NULL) {
                //pop_from_queue(&headq);
        
                if(head1!=NULL && head2==NULL) {
                    while(head1!=NULL) {
                        if(level%2==0) {
                            node *p;
                            p=new node;
                            p=pop_from_stack(&head1);
        
                            if(p->right!=NULL) {
                                push_in_stack(p->right,&head2);
                                printf("%d\n",(p->right)->data);
                            }
                            if(p->left!=NULL)
                            {   
                                push_in_stack(p->left,&head2);
                                printf("%d\n",(p->left)->data);
                            }
                        }
                    }
                    //traverse_stack(head2);
                    level++;
                } else {
                    while(head2!=NULL) {
                        if(level%2!=0) {
                            node *q;
                            q=new node;
                            q=pop_from_stack(&head2);
        
                            if(q->left!=NULL) {
                                push_in_stack(q->left,&head1);
                                printf("%d\n",(q->left)->data);
                            }
                            if(q->right!=NULL) {
                                push_in_stack(q->right,&head1);
                                printf("%d\n",(q->right)->data);
                            }
                        }
                    } //traverse_stack(head2);
                    level++;
                }
            }
            return p;
        }
        

        【讨论】:

        • +1 表示双栈的想法,但在实现中有细节。您不需要跟踪level,并且可以删除依赖它的变量和if。您正在泄漏内存q = new node; q = pop_from_stack(&amp;head); 将创建一个节点并在下一条语句中泄漏它。
        【解决方案5】:

        这个 (http://cslibrary.stanford.edu/109/TreeListRecursion.html) 有漂亮图片的答案:)

        【讨论】:

        • 链接是树的有序横向,而不是锯齿形横向。
        【解决方案6】:

        为什么要使用指针??您可以将 BST 存储为数组 A[1...n]。因此,A[1] 将具有根,A[2] 和 A[3] 将具有深度为 1 的节点,等等。这是可能的,因为它是一棵几乎完整的树,并且您知道在给定深度 - 即深度 d 处的 2^d(当然最后一个深度除外)。现在您要做的就是以 zag-zag 方式访问数组,并创建一个新顺序的新 BST(数组)。那么,您将如何以之字形方式遍历数组?对于任何给定的元素 A[i],左子元素为 A[2i],右子元素为 A[2i + 1]。因此,如果您当前的深度 d 是奇数,则遍历 2^d 个元素,当您到达第 2^d 个元素时,转到其左孩子。如果您当前的深度 d 是偶数,则再次遍历 2^d 个元素,但是当您到达第 2^d 个元素时,转到其右子元素。将它们存储为数组而不是节点可以使您的数据结构更精简,并且您的实现更简单。

        【讨论】:

          【解决方案7】:

          这可能会对您有所帮助:

          1. 为树的每个级别创建一个单独的列表
          2. 在遍历树时遍历树并将值复制到列表中
          3. 颠倒所有其他列表的顺序
          4. 连接列表

          【讨论】:

            【解决方案8】:

            这是一种广度优先搜索算法。 Wikipedia 很好地解释了如何实现它。

            在实现算法之后,创建您的链表应该是不费吹灰之力(因为只需将每个访问的元素附加到列表中)

            【讨论】:

            • 进来发布这个。这是正确的答案(事实上,wiki 页面中的图像与他的树的图像几乎相同!)树只是图表 :)
            • 这不是真正的曲折。 BFS 是沿同一方向横穿每个级别,而问题明确指出您需要以与上一级别相反的方向横穿每个级别。
            • 我会像推荐的那样进行广度优先搜索,然后使用拼接成员每隔一行反转一次。
            【解决方案9】:

            嗯...这是一个艰难的。以这种顺序遍历的问题是你做了很多跳过。在任何不是深度或广度优先的树遍历顺序中,这通常都是正确的。

            以下是我解决这种情况的方法。从单个空的节点列表数组开始,首先开始遍历树的深度。请务必跟踪遍历的深度。

            在遍历的每个点,记下您的遍历深度并在数组中的索引处选择列表。如果那里没有,请先添加它。如果深度是偶数(假设根的深度为 0),则将该节点添加到列表的末尾。如果很奇怪,请将其添加到开头。

            遍历所有节点后,连接列表。

            【讨论】:

              【解决方案10】:

              这是一种尴尬的树遍历。我想知道有多少人真正需要在真实代码中做这样的事情。不过,这里要解决的问题……

              我将如何处理这个问题:

              首先,当我将所需的输出与树的结构进行比较时,我注意到树的每个“级别”都是从上到下依次处理的。所以首先是顶部节点,然后是所有子节点,然后是所有孙子节点,依此类推。因此,一个好的解决方案可能会涉及一个循环,该循环处理一个级别,同时在下一个级别中建立一个节点列表,以用于循环的下一次迭代。

              接下来,这个“之字形”顺序意味着它需要交替处理子节点的方向。如果特定的迭代从左到右进行,那么下一次迭代必须从右到左进行。然后从左到右进行后续迭代,依此类推。因此,在我处理一个级别并构建下一个级别列表的循环的想法中,当它为下一个级别构建节点列表时,它可能需要具有某种交替行为。在偶数迭代中,列表以一种方式构建。在奇数迭代中,列表以另一种方式构建。

              另外,考虑整个事情的另一种方式是:设计一个可以按 1、2、3、4、5、6 等顺序构建结果列表的解决方案。然后修改该设计以使其具有锯齿形顺序。

              这是否让您对如何设计解决方案有了足够的了解?

              【讨论】:

              • 一般不做关卡遍历再修改列表;对于不完全平衡的 BST,它将失败。
              【解决方案11】:

              您可以编写一个函数来在双向链表中添加节点。然后,您可以在遍历树时调用此函数。这样你应该可以做到的。

              【讨论】:

              • @爱迪生,帕布。我认为树的 BFS 不会给出问题的确切结果。因为树的 BFS 会产生:1-2-3-4-5-6-7-8-.....-15 OR 1-3-2-7-6-5-4-15- ...8(取决于您是先入队右孩子还是左孩子)...所以,BFS 不会给出列表的中间部分
              猜你喜欢
              • 2012-07-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-03-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多