如何将二叉树就地转换为二叉搜索树,即我们不能使用任何额外的空间。
11 回答
将二叉树转换为双向链表 - 可以在 O(n) 中就地完成
然后使用归并排序,nlogn
将列表转换回树 - O(n)
简单的 nlogn 解决方案。
你没有做太多的事情,但如果要求是我认为的那样,你已经创建了一个二叉树并位于内存中,但没有排序(无论如何你希望它被排序)。
我假设树节点看起来像
struct tree_node {
struct tree_node * left;
struct tree_node * right;
data_t data;
};
我还假设您可以阅读 C
虽然我们可以坐下来想知道为什么这棵树是在没有按排序顺序创建的情况下创建的,这对我们没有任何好处,所以我将忽略它并只处理它的排序。
不使用额外空间的要求很奇怪。暂时会有额外的空间,如果只是在堆栈上。我将假设这意味着调用 malloc 或类似的东西,并且结果树必须使用不比原始未排序树更多的内存。
第一个也是最简单的解决方案是对未排序的树进行前序遍历,从该树中删除每个节点,然后将排序插入到新树中。这是 O(n+n log(n)),也就是 O(n log(n))。
如果这不是他们想要的,你将不得不使用旋转和其他东西......那太可怕了!
我认为你可以通过做一个奇怪的堆排序来做到这一点,但我遇到了问题。想到的另一件事会非常慢,就是在树上做一个奇怪的冒泡排序。
为此,将每个节点进行比较,并可能与它的每个直接子节点(因此也与它的父节点)反复进行交换,直到您遍历树并且找不到任何需要的交换为止。做一个振动排序(从左到右和从右到左的冒泡排序)版本效果最好,并且在初始通过后,您不需要遍历与其父级没有顺序的子树.
我敢肯定,这个算法要么是我之前的其他人想出来的,而且有一个我不知道的酷名字,要么它在某种程度上存在根本性的缺陷,我没有看到。
提出第二个建议的运行时计算非常复杂。起初我以为它只是 O(n^2),就像气泡和振动器排序一样,但我不能满足自己,子树遍历避免可能不足以使它比 O(n^) 好一点2)。本质上,冒泡排序和振动排序也得到了这种优化,但仅在总排序发生较早的末端并且您可以降低限制。使用此树版本,您也有可能避免在集合中间出现块。好吧,就像我说的,它可能有致命的缺陷。
执行 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';
}
}
执行以下算法以达到解决方案。
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)
最坏的情况。
二叉树通常是二叉搜索树,在这种情况下不需要转换。
也许您需要澄清要转换的内容的结构。您的源代码树是否不平衡?它不是按您要搜索的键排序的吗?您是如何到达源代码树的?
好吧,如果这是一个面试问题,我会脱口而出的第一件事(实际想法为零)是:递归地迭代整个二进制文件并找到最小的元素。把它从二叉树中取出。现在,重复迭代整个树并找到最小元素的过程,并将其添加为找到的最后一个元素的父元素(前一个元素成为新节点的左子元素)。根据需要重复多次,直到原始树为空。最后,您会得到最差的排序二叉树——链表。您的指针指向根节点,这是最大的元素。
这是一个全面的可怕算法 - O(n^2) 运行时间,具有最差的二叉树输出,但在提出更好的东西之前这是一个不错的起点,并且具有您能够编写代码的优势它在白板上大约 20 行。
#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;
}
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);
}
***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;
}
}
对二叉树进行中序遍历并存储结果。通过将排序列表的中间元素作为根,按二叉搜索树的升序对结果进行排序(这可以使用二分搜索来完成)。所以我们得到平衡的二叉搜索树。
对树进行堆排序.. nlogn 复杂度..