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;
}