Java 在BST中查找总计为提供值的元素

Java 在BST中查找总计为提供值的元素,java,algorithm,data-structures,big-o,binary-search-tree,Java,Algorithm,Data Structures,Big O,Binary Search Tree,我正在设法找到解决这个问题的办法 Find two elements in balanced BST which sums to a given a value. 约束时间O(n)和空间O(logn) 我想知道下面的算法是否有效 int[] findSum(Node root, int sum){ int[] sumArray; for (int i=0;i<sum;i++) { if (root.contains(i) && root.conta

我正在设法找到解决这个问题的办法

Find two elements in balanced BST which sums to a given a value.
约束时间O(n)和空间O(logn)

我想知道下面的算法是否有效

int[] findSum(Node root, int sum){
   int[] sumArray;
   for (int i=0;i<sum;i++) {
      if (root.contains(i) && root.contains(sum-i)) {
         sumArray[0] = i;
         sumArray[1] = sum-i;
      }
   }
}
int[]findSum(节点根,int和){
int[]数组;

对于(int i=0;i我相信你的方法会奏效,但没有适当的时间限制

让我们从分析该算法的复杂性开始。请注意,这里需要考虑两个不同的参数。首先,BST中的元素总数。如果将BST变大或变小,则算法完成所需的时间会变长或变短。让我们将此数称为n。其次,没有你们希望这些值加起来的总数,我们称之为U

让我们看看您当前的算法是怎么做的。现在,它迭代一个循环O(U)次,每次迭代检查BST中是否存在两个值。每次BST查找都需要时间O(logn),因此您的算法所做的总工作量是O(U logn)。然而,您的算法只使用常量空间(即空间O(1))

不幸的是,这个运行时一点也不好。如果目标数量非常大(比如100000000),那么您的算法将需要很长时间才能完成,因为U将非常大

所以现在的问题是如何更有效地解决这个问题。幸运的是,我们可以使用一个非常可爱的算法来解决这个问题,它将利用BST元素按排序顺序存储的事实

让我们先解决一个与您提出的问题稍有不同的类似问题。假设我没有给您一个BST和目标编号,而是给您一个排序数组和目标编号,然后问同样的问题:在这个排序数组中有两个数字相加到目标吗?例如,我可以给您这个数组:

 0  1  3  6  8  9  10  14  19
假设您想知道此数组中的两个数字之和是否等于16。您可以如何做到这一点

您可以尝试与以前相同的方法:检查数组是否包含0和16、1和15、2和14等,直到找到一对或没有要检查的值。使用二进制搜索检查排序数组中是否存在元素需要时间O(logn),因此此算法仍然需要O(U logn)时间。(如果知道这些值分布得很好,您可以使用加快速度,这将使O(U log n)运行时符合预期,但大的U项仍然是一个问题)

让我们考虑一个不同的方法。从根本上说,你所做的事情需要你明确地列举出所有与U相联的对,然而,大多数都不在那里,事实上,大多数时候,这两个元素都不在那里。我们可以用下面的算法更快地使事情变快:

  • 对于数组中x的每个元素,检查U-x是否在数组中
  • 如果是,请报告成功
  • 否则,如果不存在这样的对,则报告失败
此算法需要您查看数组中的总共O(n)个元素,每次进行O(logn)运算以找到匹配对。在最坏的情况下,这将花费O(n logn)时间并使用O(1)空间。如果U是一个巨大的数字,这比以前要好得多,因为不再依赖于U

但是,通过对问题的结构进行巧妙的观察,我们可以进一步加快速度。假设我们正在数组中查找数字x,并进行二进制搜索以查找U-x。如果我们找到它,我们就完成了。如果没有,我们将在数组中找到第一个大于U-x的数字。让我们调用该数字z

现在假设我们想看看一个数字y是否可以是求和为U的对的一部分,并且假设y比x大,在这种情况下我们知道

y+z

>x+z

>x+(U-x)

=U

这意味着y和z的和大于U,所以它不可能是U。这意味着什么?我们通过对与x配对的元素进行二进制搜索来找到z,然后求和为U。我们刚刚展示的是,如果你试图将z添加到数组中任何大于x的数中,那么总数必须超过U换句话说,z不能与比x大的任何东西配对。同样地,比z大的任何东西都不能与比x大的任何东西配对,因为它必须加起来比U大

根据这一观察结果,我们可以尝试使用不同的算法。让我们像以前一样使用数组,看看是否可以找到一对求和为16的数组:

 0  1  3  6  8  9  10  14  19
让我们维护两个指针——“左侧”指针lhs和“右侧”指针rhs:

 0  1  3  6  8  9  10  14  19
 ^                          ^
lhs                        rhs
当我们把这两个数相加时,我们得到了19,它超过了U。现在,我们相加的任何一对数都必须有它的较小数,至少和lhs数一样大,它是0。因此,如果我们尝试将任何一对使用数字19的数相加,我们知道总和会太大。因此,我们可以从consi中排除减额包含19的任何一对。这留下

 0  1  3  6  8  9  10  14  19
 ^                      ^
lhs                    rhs
现在,看看这个和(14),它太小了。使用与前面类似的逻辑,我们可以安全地说,剩余数组中使用0的任何和都必须得到小于16的和,因为和中的第二个数字最多是14。因此,我们可以排除0:

 0  1  3  6  8  9  10  14  19
    ^                   ^
   lhs                 rhs
我们开始看到一种模式:

  • 如果金额太小,则提前lhs
  • 如果总和太大,则减小rhs
最终,我们将找到一对总和为16的,或者lhs和rhs将相互碰撞,在这一点上,我们保证没有一对总和为16

通过这个算法,我们得到

 0  1  3  6  8  9  10  14  19
    ^                   ^
   lhs                 rhs    (sum is 15, too small)

 0  1  3  6  8  9  10  14  19
       ^                ^
      lhs              rhs    (sum is 17, too big)

 0  1  3  6  8  9  10  14  19
       ^            ^
      lhs          rhs        (sum is 13, too small)

 0  1  3  6  8  9  10  14  19
          ^         ^
         lhs       rhs        (sum is 16, we're done!)
瞧!我们找到答案了

那么这有多快?好吧,每一次
//we're looking for two numbers that equal 17 when added
Node runner;
Node root;
int i;
int [] sum_total;

void findSum(int sum){
    int search_1st = 0;
    sum_total = new int[2];
    search(int search_1st);
}   

search( Node root, int num1){
    if(i == 3){
        return;
    }
    Node runner = root;
    if(ruunner == null){
    return ;
    }
    if(runner.element == num1){
        sum_total[i] = num1;
        i++;
        if(i == 3){
            return;
        }
        //now search for sum - num1 with root
        search(root, sum - num1);
    }else{
        if(runner.left < num1){
            search(runner.right, num1);
        }else{
            search(runner.left, num1);
        }
    }
}
/* In a balanced binary search tree isPairPresent two element which sums to
   a given value time O(n) space O(logn) */
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100

// A BST node
struct node
{
    int val;
    struct node *left, *right;
};

// Stack type
struct Stack
{
    int size;
    int top;
    struct node* *array;
};

// A utility function to create a stack of given size
struct Stack* createStack(int size)
{
    struct Stack* stack =
        (struct Stack*) malloc(sizeof(struct Stack));
    stack->size = size;
    stack->top = -1;
    stack->array =
        (struct node**) malloc(stack->size * sizeof(struct node*));
    return stack;
}

// BASIC OPERATIONS OF STACK
int isFull(struct Stack* stack)
{   return stack->top - 1 == stack->size;  }

int isEmpty(struct Stack* stack)
{   return stack->top == -1;   }

void push(struct Stack* stack, struct node* node)
{
    if (isFull(stack))
        return;
    stack->array[++stack->top] = node;
}

struct node* pop(struct Stack* stack)
{
    if (isEmpty(stack))
        return NULL;
    return stack->array[stack->top--];
}

// Returns true if a pair with target sum exists in BST, otherwise false
bool isPairPresent(struct node *root, int target)
{
    // Create two stacks. s1 is used for normal inorder traversal
    // and s2 is used for reverse inorder traversal
    struct Stack* s1 = createStack(MAX_SIZE);
    struct Stack* s2 = createStack(MAX_SIZE);

    // Note the sizes of stacks is MAX_SIZE, we can find the tree size and
    // fix stack size as O(Logn) for balanced trees like AVL and Red Black
    // tree. We have used MAX_SIZE to keep the code simple

    // done1, val1 and curr1 are used for normal inorder traversal using s1
    // done2, val2 and curr2 are used for reverse inorder traversal using s2
    bool done1 = false, done2 = false;
    int val1 = 0, val2 = 0;
    struct node *curr1 = root, *curr2 = root;

    // The loop will break when we either find a pair or one of the two
    // traversals is complete
    while (1)
    {
        // Find next node in normal Inorder traversal. See following post
        // http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion/
        while (done1 == false)
        {
            if (curr1 != NULL)
            {
                push(s1, curr1);
                curr1 = curr1->left;
            }
            else
            {
                if (isEmpty(s1))
                    done1 = 1;
                else
                {
                    curr1 = pop(s1);
                    val1 = curr1->val;
                    curr1 = curr1->right;
                    done1 = 1;
                }
            }
        }

        // Find next node in REVERSE Inorder traversal. The only
        // difference between above and below loop is, in below loop
        // right subtree is traversed before left subtree
        while (done2 == false)
        {
            if (curr2 != NULL)
            {
                push(s2, curr2);
                curr2 = curr2->right;
            }
            else
            {
                if (isEmpty(s2))
                    done2 = 1;
                else
                {
                    curr2 = pop(s2);
                    val2 = curr2->val;
                    curr2 = curr2->left;
                    done2 = 1;
                }
            }
        }

        // If we find a pair, then print the pair and return. The first
        // condition makes sure that two same values are not added
        if ((val1 != val2) && (val1 + val2) == target)
        {
            printf("\n Pair Found: %d + %d = %d\n", val1, val2, target);
            return true;
        }

        // If sum of current values is smaller, then move to next node in
        // normal inorder traversal
        else if ((val1 + val2) < target)
            done1 = false;

        // If sum of current values is greater, then move to next node in
        // reverse inorder traversal
        else if ((val1 + val2) > target)
            done2 = false;

        // If any of the inorder traversals is over, then there is no pair
        // so return false
        if (val1 >= val2)
            return false;
    }
}

// A utility function to create BST node
struct node * NewNode(int val)
{
    struct node *tmp = (struct node *)malloc(sizeof(struct node));
    tmp->val = val;
    tmp->right = tmp->left =NULL;
    return tmp;
}

// Driver program to test above functions
int main()
{
    /*
                   15
                /     \
              10      20
             / \     /  \
            8  12   16  25    */
    struct node *root =  NewNode(15);
    root->left = NewNode(10);
    root->right = NewNode(20);
    root->left->left = NewNode(8);
    root->left->right = NewNode(12);
    root->right->left = NewNode(16);
    root->right->right = NewNode(25);

    int target = 28;
    if (isPairPresent(root, target) == false)
        printf("\n No such values are found\n");

    getchar();
    return 0;
}
public int sum2(TreeNode A, int B) {
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2 = new Stack<>();
    TreeNode cur1 = A;
    TreeNode cur2 = A;

    while (!stack1.isEmpty() || !stack2.isEmpty() ||
            cur1 != null || cur2 != null) {
        if (cur1 != null || cur2 != null) {
            if (cur1 != null) {
                stack1.push(cur1);
                cur1 = cur1.left;
            }

            if (cur2 != null) {
                stack2.push(cur2);
                cur2 = cur2.right;
            }
        } else {
            int val1 = stack1.peek().val;
            int val2 = stack2.peek().val;

            // need to break out of here
            if (stack1.peek() == stack2.peek()) break;

            if (val1 +  val2 == B) return 1;

            if (val1 + val2 < B) {
                cur1 = stack1.pop();
                cur1 = cur1.right;
            } else {
                cur2 = stack2.pop();
                cur2 = cur2.left;
            }
        }
    }

    return 0;
}