如何将二叉树就地转换为二叉搜索树,即我们不能使用任何额外的空间

Posted

技术标签:

【中文标题】如何将二叉树就地转换为二叉搜索树,即我们不能使用任何额外的空间【英文标题】:How to convert a binary tree to binary search tree in-place, i.e., we cannot use any extra space 【发布时间】:2010-04-05 05:46:59 【问题描述】:

如何将二叉树原地转换为二叉搜索树,即我们不能使用任何额外的空间。

【问题讨论】:

【参考方案1】:

将二叉树转换为双向链表 - 可以在 O(n) 中就地完成 然后使用归并排序对其进行排序,nlogn 将列表转换回树 - O(n)

简单的 nlogn 解决方案。

【讨论】:

但这会占用额外的空间来创建链表。正确的 ?问题是没有占用额外的空间。 @MSach 有办法将树转换为“就地”链表 我认为这不会保留 BST 的原始结构。 这太棒了!简单直观!【参考方案2】:

你没有做太多,但如果要求是我认为的那样,你已经创建了一个二叉树并位于内存中,但没有排序(无论如何你希望它排序的方式)。

我假设树节点看起来像

struct tree_node 
    struct tree_node * left;
    struct tree_node * right;
    data_t data;
;

我还假设您可以阅读 C

虽然我们可以坐下来想知道为什么创建这棵树时没有按排序顺序创建这对我们没有任何好处,所以我将忽略它并只处理它的排序。

不使用额外空间的要求很奇怪。暂时会有额外的空间,如果只是在堆栈上。我将假设这意味着调用 malloc 或类似的东西,并且生成的树必须使用不比原始未排序树更多的内存。

第一个也是最简单的解决方案是对未排序的树进行前序遍历,从该树中删除每个节点,然后将排序插入到新树中。这是 O(n+nlog(n)),也就是 O(nlog(n))。

如果这不是他们想要的,而你将不得不使用旋转和其他东西......那太可怕了!

我认为你可以通过做一个奇怪的堆排序来做到这一点,但我遇到了问题。 想到的另一件事是在树上做一个奇怪的冒泡排序,这会非常慢。

为此,每个节点都会被反复比较并可能与其每个直接子节点(因此也与其父节点)交换,直到您遍历树并且没有找到任何节点 需要掉期。做一个振动排序(从左到右和从右到左的冒泡排序)版本效果最好,并且在初始通过之后,您不需要遍历与其父级没有顺序的子树.

我敢肯定,这个算法要么是我之前的其他人想出来的,并且有一个我不知道的酷名字,要么它在某些方面存在根本性的缺陷,我没有看到。

提出第二个建议的运行时计算非常复杂。起初我以为它只是 O(n^2),就像气泡和振动器排序一样,但我不能满足自己,子树遍历避免可能不足以使它比 O(n^) 好一点2)。本质上,冒泡排序和振动排序也得到了这种优化,但仅在总排序发生较早的末端并且您可以降低限制。使用此树版本,您也有机会避免在集合中间出现块。好吧,就像我说的,它可能存在致命缺陷。

【讨论】:

【参考方案3】:

执行 PostOrder Traversal 并从中创建一个二叉搜索树。

struct Node * newroot = '\0';

struct Node* PostOrder(Struct Node* root)

      if(root != '\0')
      
          PostOrder(root->left);
          PostOrder(root->right);
          insertBST(root, &newroot);
      


insertBST(struct Node* node, struct Node** root)

   struct Node * temp, *temp1;
   if( root == '\0')
   
      *root == node;
       node->left ==  '\0';
       node->right == '\0';
   
   else
   
       temp = *root;
       while( temp != '\0')
       
           temp1= temp;
           if( temp->data > node->data)
               temp = temp->left;
           else
               temp = temp->right;
       
       if(temp1->data > node->data)
       
           temp1->left = node;
       
       else
       
           temp1->right = node;
       
       node->left = node->right = '\0';
    

【讨论】:

【参考方案4】:

执行以下算法以达到解决方案。

1) 在不使用任何空格的情况下找到顺序后继。

Node InOrderSuccessor(Node node)
 
    if (node.right() != null) 
     
        node = node.right() 
        while (node.left() != null)  
            node = node.left() 
        return node 
    
    else
     
        parent = node.getParent(); 
        while (parent != null && parent.right() == node)
        
            node = parent 
            parent = node.getParent() 
         
        return parent 
     
 

2) 不使用空格按顺序遍历。

a) 找到中序遍历的第一个节点。如果它有,它应该离开树的大多数孩子,或者如果有的话,它应该离开第一个右孩子的左边,或者是右孩子本身。 b) 使用上述算法找出第一个节点的 inode 继任者。 c) 对所有返回的后继重复步骤 2。

使用上述 2 算法并在不使用额外空间的情况下对二叉树进行按顺序遍历。 遍历时形成二叉搜索树。但复杂性是O(N2) 最坏的情况。

【讨论】:

【参考方案5】:

好吧,如果这是一个面试问题,我会脱口而出的第一件事(实际想法为零)是:递归地迭代整个二进制文件并找到最小的元素。把它从二叉树中取出。现在,重复迭代整个树并找到最小元素的过程,并将其添加为找到的最后一个元素的父元素(前一个元素成为新节点的左子元素)。根据需要重复多次,直到原始树为空。最后,您将得到最差的排序二叉树——链表。您的指针指向根节点,这是最大的元素。

这是一个全面的可怕算法 - O(n^2) 运行时间和最差的二叉树输出,但它是一个不错的起点,然后再想出更好的东西,并且具有您能够编写的优势它的代码在白板上大约 20 行。

【讨论】:

但这需要额外的空间。这个问题的限制是需要就地完成。 嗯,没有。除了局部变量,这不是。【参考方案6】:

二叉树通常二叉搜索树,在这种情况下不需要转换。

也许您需要澄清您要转换的内容的结构。您的源代码树是否不平衡?它不是按您要搜索的键排序的吗?您是如何到达源代码树的?

【讨论】:

我认为,任意二叉树不是 BST,因为它不一定具有节点排序的 BST 属性。二叉树不仅用于搜索 - 它们还可以用作表达式树,例如 @Eli,我了解(也许您只看到了我的答案的 v1)。只是我从来没有遇到过我有一个未排序的二叉树并突然想要它排序的情况。表达式树就是一个很好的例子。谁想要排序一个表达式树?我怀疑有些人对 OP 的大局有误,因此我提出了各种问题。 二叉树是 BST?你喝什么? @ShyamLovesToCode:我没有说二叉树是 BST。不要对我从未说过的事情投反对票。 不需要转换?它太误导了【参考方案7】:
#include <stdio.h>
#include <stdlib.h>

typedef int data_t;

struct tree_node 
    struct tree_node * left;
    struct tree_node * right;
    data_t data;
;

        /* a bonsai-tree for testing */
struct tree_node nodes[10] =
 nodes+1, nodes+2, 1
, nodes+3, nodes+4, 2
, nodes+5, nodes+6, 3
, nodes+7, nodes+8, 4
, nodes+9, NULL, 5
, NULL, NULL, 6
, NULL, NULL, 7
, NULL, NULL, 8
, NULL, NULL, 9
        ;

struct tree_node * harvest(struct tree_node **hnd)

struct tree_node *ret;

while (ret = *hnd) 
        if (!ret->left && !ret->right) 
                *hnd = NULL;
                return ret;
                
        if (!ret->left ) 
                *hnd = ret->right;
                ret->right = NULL;;
                return ret;
                
        if (!ret->right) 
                *hnd = ret->left;
                ret->left = NULL;;
                return ret;
                
        hnd = (rand() &1) ? &ret->left : &ret->right;
        

return NULL;


void insert(struct tree_node **hnd, struct tree_node *this)

struct tree_node *ret;

while ((ret= *hnd)) 
        hnd = (this->data  < ret->data ) ? &ret->left : &ret->right;
        
*hnd = this;


void show(struct tree_node *ptr, int indent)

if (!ptr)  printf("Null\n"); return; 

printf("Node(%d):\n", ptr->data);
printf("%*c=", indent, 'L');  show (ptr->left, indent+2);
printf("%*c=", indent, 'R');  show (ptr->right, indent+2);


int main(void)

struct tree_node *root, *this, *new=NULL;

for (root = &nodes[0]; this = harvest (&root);  ) 
        insert (&new, this);
        

show (new, 0);
return 0;

【讨论】:

【参考方案8】:
struct Node

    int value;
    Node* left;
    Node* right;
;

void swap(int& l, int& r)

    int t = l;
    l = r;
    r = t;


void ConvertToBST(Node* n, Node** max)

    if (!n) return;

    // leaf node
    if (!n->left && !n->right)
    
        *max = n;
        return;
    

    Node *lmax = NULL, *rmax = NULL;
    ConvertToBST(n->left, &lmax);
    ConvertToBST(n->right, &rmax);

    bool swapped = false;
    if (lmax && n->value < lmax->value)
    
        swap(n->value, lmax->value);
        swapped = true;
    

    if (rmax && n->value > rmax->value)
    
        swap(n->value, n->right->value);
        swapped = true;
    

    *max = n;
    if (rmax && rmax->value > n->value) *max = rmax;

    // If either the left subtree or the right subtree has changed, convert the tree to BST again
    if (swapped) ConvertToBST(n, max);

【讨论】:

【参考方案9】:
***I am giving this solution in Java***

import javafx.util.Pair;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class MinimumSwapRequiredBTintoBST 
    //Node of binary tree
    public static class Node
        int data;
        Node left;
        Node right;
        public Node(int data)
            this.data = data;
            this.left = null;
            this.right = null;
        
    
    public static void main(String []arg)
        root = new Node(1);
        root.left = new Node(2);
        root.right = new Node(3);
        root.left.left = new Node(4);
        root.left.right = new Node(5);
        root.right.left = new Node(6);
        root.right.right = new Node(7);
        System.out.print("Tree traverasl i.e inorder traversal :");
        inorder(root);
        System.out.println(" ");
        MinimumSwapRequiredBTintoBST bst = new MinimumSwapRequiredBTintoBST();
        bst.convertBTBST(root);
    

    private static void inorder(Node root) 
        if(root == null) return;
        inorder(root.left);
        System.out.print(root.data + "  ");
        inorder(root.right);

    

   static Node root;
    int[] treeArray;
    int index = 0;

// convert binary tree to binary search tree
    public void convertBTBST(Node node)
        int treeSize = elementsOfTree(node);
        treeArray = new int[treeSize];
        convertBtToArray(node);
        // Sort Array ,Count number of swap

        int minSwap = minimumswap(treeArray);
        System.out.println("Minmum swap required to form BT to BST :" +minSwap);
    

    private static int minimumswap(int[] arr) 
        int n =arr.length;
        // Create two arrays and use as pairs where first
        // is element and secount array as position of first element
        ArrayList<Pair<Integer, Integer>> arrpos =
            new ArrayList<Pair<Integer, Integer>>();
        // Assign the value
        for(int i =0;i<n;i++)
        
            arrpos.add(new Pair<Integer, Integer>(arr[i],i));
        
// Sort the array by array element values to get right
//position of every element as the elements of secound array

        arrpos.sort(new Comparator<Pair<Integer, Integer>>() 
            @Override
            public int compare(Pair<Integer, Integer> o1, Pair<Integer, Integer> o2) 
                return o1.getKey()-o2.getKey();
            
        );
// To keep track of visited elements .Initially all elements as not visited so put them as false
        int ans = 0;
        boolean []visited = new boolean[n];
        Arrays.fill(visited, false);
        // Traverse array elements
        for(int i =0;i<n;i++)
            // Already swapped and corrected or already present at correct pos
            if(visited[i] || arrpos.get(i).getValue() == i)
                continue;
            // Find out the number of nodes in this cycle and add in ans
            int cycle_size = 0;
            int j =i;
            while(!visited[j])
                visited[j] = true;
                j = arrpos.get(j).getValue();
                cycle_size++;
            
            if(cycle_size>0)
                ans += cycle_size-1;
            
        
        return ans;
    

    private void convertBtToArray(Node node) 
        // Check whether tree is empty or not.
        if (root == null) 
            System.out.println("Tree is empty:");
            return;
        
    else
        if(node.left != null) 
         convertBtToArray(node.left);
            treeArray[index] = node.data;
            index++;
            if(node.right != null)
                convertBtToArray(node.right);
            

        
    
    private int elementsOfTree(Node node) 
        int height = 0;
        if(node == null) return 0;
        else
            height = elementsOfTree(node.left )+ elementsOfTree(node.right)+1;
        
        return height;
    



【讨论】:

【参考方案10】:

对二叉树进行中序遍历并存储结果。 按升序对结果进行排序 通过将排序列表的中间元素作为根来形成二叉搜索树(这可以使用二叉搜索来完成)。所以我们得到了平衡的二叉搜索树。

【讨论】:

【参考方案11】:

对树进行堆排序.. nlogn 复杂度..

【讨论】:

以上是关于如何将二叉树就地转换为二叉搜索树,即我们不能使用任何额外的空间的主要内容,如果未能解决你的问题,请参考以下文章

编程将二叉树(子女右兄弟链)转换为树然后将树转换为二叉树(直接修改) 非递归实现

如何创建将二叉树转换为元组的函数?

c_cpp 将二叉树平铺到就地链接列表。

平衡二叉树---将有序数组转换为二叉搜索树

Leetcode练习(Python):树类:第108题:将有序数组转换为二叉搜索树:将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的

|二叉树|669. 修剪二叉搜索树 |108.将有序数组转换为二叉搜索树|538.把二叉搜索树转换为累加树