prefix evaluate using a queue? - algorithm

I need to evaluate prefix using a queue (not stack). for example:
+ 3 * 2 1
is equivalent to 3+(2*1) = 5.
I am thinking about to loop through the queue over and over using dequeue and enqueue. If the pattern "operator" + "number" + "number" if found, dequeue 3 times and enqueue the result until there is only a number left in the queue.
while size(q)>1
if elements are in this pattern: an operator is followed by 2 numbers.
operator <--dequeue(q);
number1 <--dequeue(q);
number2 <--dequeue(q);
int a = apply(operator, number1, number2 );
enqueue (q, a);
else if the element is a number or operator:
element <-- dequeue(q);
enqueue (q, element);
return dequeue(q);
My algorithm has 2 problems:
operators and numbers are 2 different types and need to be saved in one queue. how can I save a "+" in an int queue?
2 3 + is an invalid input, but it will eventually return 5. 2 and 3 will be enqueued to the right, it becomes + 2 3. If the input is invalid, how do I prevent it?
Many thanks

Answers-
1- No this is not the best algorithm to solve prefix input(Stack approach is better).
2- You can give a special number for each operator.(lets say -999 for '-').
Better approach(without stack)
try something like this recursive approach
Simple recursion:
int c=0;
Evaluate(input,current_position):
c++;
Read a token from input at current pos.
If the token is a value:
Return the value of the token
If the token is a binary operator:
if(current_position+2 exists)
Let first_argument = Evaluate(input,current_position+1)
Let second_argument = Evaluate(input,current_position+2)
Return apply(operator, first_argument, second_argument)
else
invalid expression.
if(c==len(expression)
valid exp
else
invalid exp

This is my recursive solution using a queue structure (Last in First out).
Method 2:
Each element will be dequeued from an old queue and enqueued to a new list. If the pattern is found, dequeue 3 times and enqueue the result to the new queue. If the queue length doesn't change, report invalid input.
Define:
1. Given an input string.
2. Recursive function: int prefix_eval( q )
Base case: if size(q)==1, return dequeue(q);
Create a new queue: new_q;
int old_qlen = q->qlen;
While(size(q)>0)
if q->data[0] is an operator, q->data[1] and q->data[2] are numbers.
operator <--dequeue(q);
number1 <--dequeue(q);
number2 <--dequeue(q);
element = apply(operator, number1, number2 );
enqueue (new_q, element);
Else:
element = dequeue(q);
enqueue(new_q, element);
If (old_qlen > new_q->qlen)
Prefix_eval(new_q);
Else
Report invalid input and return
Start:
Create a queue q;
Enqueue q with each token from the input
Prefix_eval(q);

Related

Reduce binary string to an empty string by removing subsequences with alternative characters

This was a question asked in the coding round for NASDAQ internship.
Program description:
The program takes a binary string as input. We have to successively remove sub-sequences having all characters alternating, till the string is empty. The task was to find the minimum number of steps required to do so.
Example1:
let the string be : 0111001
Removed-0101, Remaining-110
Removed-10 , Remaining-1
Removed-1
No of steps = 3
Example2:
let the string be : 111000111
Removed-101, Remaining-110011
Removed-101, Remaining-101
Removed-101
No of steps = 3
Example3:
let the string be : 11011
Removed-101, Remaining-11
Removed-1 , Remaining-1
Removed-1
No of steps = 3
Example4:
let the string be : 10101
Removed-10101
No of steps = 1
The solution I tried, considered the first character of the binary string as first character for my sub-sequence. Then created a new string, where the next character would be appended if it wasn't part of the alternating sequence. The new string becomes our binary string. In this way, a loop continues till the new string is empty. (somewhat an O(n^2) algorithm). As expected, it gave me a timeout error. Adding a somewhat similar code in C++ to the one I had tried, which was in Java.
#include<bits/stdc++.h>
using namespace std;
int main() {
string str, newStr;
int len;
char c;
int count = 0;
getline(cin, str);
len = str.length();
//continue till string is empty
while(len > 0) {
len = 0;
c = str[0];
for(int i=1; str[i] != '\0';i++) {
//if alternative characters are found, set as c and avoid that character
if(c != str[i])
c = str[i];
//if next character is not alternate, add the character to newStr
else {
newStr.push_back(str[i]);
len++;
}
}
str = newStr;
newStr = "";
count++;
}
cout<<count<<endl;
return 0;
}
I also tried methods like finding the length of the largest sub sequence of same consecutive characters which obviously didn't satisfy every case, like that of example3.
Hope somebody could help me with the most optimized solution for this question. Preferably a code in C, C++ or python. Even the algorithm would do.
I found a more optimal O(NlogN) solution by maintaining a Min-Heap and Look-up hashMap.
We start with the initial array as alternating counts of 0, 1.
That is, for string= 0111001; lets assume our input-array S=[1,3,2,1]
Basic idea:
Heapify the count-array
Extract minimum count node => add to num_steps
Now extract both its neighbours (maintained in the Node-class) from the Heap using the lookup-map
Merge both these neighbours and insert into the Heap
Repeat steps 2-4 until no entries remain in the Heap
Code implementation in Python
class Node:
def __init__(self, node_type: int, count: int):
self.prev = None
self.next = None
self.node_type = node_type
self.node_count = count
#staticmethod
def compare(node1, node2) -> bool:
return node1.node_count < node2.node_count
def get_num_steps(S: list): ## Example: S = [2, 1, 2, 3]
heap = []
node_heap_position_map = {} ## Map[Node] -> Heap-index
prev = None
type = 0
for s in S:
node: Node = Node(type, s)
node.prev = prev
if prev is not None:
prev.next = node
prev = node
type = 1 - type
# Add element to the map and also maintain the updated positions of the elements for easy lookup
addElementToHeap(heap, node_heap_position_map, node)
num_steps = 0
last_val = 0
while len(heap) > 0:
# Extract top-element and also update the positions in the lookup-map
top_heap_val: Node = extractMinFromHeap(heap, node_heap_position_map)
num_steps += top_heap_val.node_count - last_val
last_val = top_heap_val.node_count
# If its the corner element, no merging is required
if top_heap_val.prev is None or top_heap_val.next is None:
continue
# Merge the nodes adjacent to the extracted-min-node:
prev_node = top_heap_val.prev
next_node = top_heap_val.next
removeNodeFromHeap(prev_node, node_heap_position_map)
removeNodeFromHeap(next_node, node_heap_position_map)
del node_heap_position_map[prev_node]
del node_heap_position_map[next_node]
# Created the merged-node for neighbours and add to the Heap; and update the lookup-map
merged_node = Node(prev_node.node_type, prev_node.node_count + next_node.node_count)
merged_node.prev = prev_node.prev
merged_node.next = next_node.next
addElementToHeap(heap, node_heap_position_map, merged_node)
return num_steps
PS: I havent implemented the Min-heap operations above, but the function-method-names are quite eponymous.
We can solve this in O(n) time and O(1) space.
This isn't about order at all. The actual task, when you think about it, is how to divide the string into the least number of subsequences that consist of alternating characters (where a single is allowed). Just maintain two queues or stacks; one for 1s, the other for 0s, where characters pop their immediate alternate predecessors. Keep a record of how long the queue is at any one time during the iteration (not including the replacement moves).
Examples:
(1)
0111001
queues
1 1 -
0 - 0
0 - 00
1 1 0
1 11 -
1 111 - <- max 3
0 11 0
For O(1) space, The queues can just be two numbers representimg the current counts.
(2)
111000111
queues (count of 1s and count of 0s)
1 1 0
1 2 0
1 3 0 <- max 3
0 2 1
0 1 2
0 0 3 <- max 3
1 1 2
1 2 1
1 3 0 <- max 3
(3)
11011
queues
1 1 0
1 2 0
0 1 1
1 2 0
1 3 0 <- max 3
(4)
10101
queues
1 1 0 <- max 1
0 0 1 <- max 1
1 1 0 <- max 1
0 0 1 <- max 1
1 1 0 <- max 1
I won't write the full code. But I have an idea of an approach that will probably be fast enough (certainly faster than building all of the intermediate strings).
Read the input and change it to a representation that consists of the lengths of sequences of the same character. So 11011 is represented with a structure that specifies it something like [{length: 2, value: 1}, {length: 1, value: 0}, {length: 2, value: 1}]. With some cleverness you can drop the values entirely and represent it as [2, 1, 2] - I'll leave that as an exercise for the reader.
With that representation you know that you can remove one value from each of the identified sequences of the same character in each "step". You can do this a number of times equal to the smallest length of any of those sequences.
So you identify the minimum sequence length, add that to a total number of operations that you're tracking, then subtract that from every sequence's length.
After doing that, you need to deal with sequences of 0 length. - Remove them, then if there are any adjacent sequences of the same value, merge those (add together the lengths, remove one). This merging step is the one that requires some care if you're going for the representation that forgets the values.
Keep repeating this until there's nothing left. It should run somewhat faster than dealing with string manipulations.
There's probably an even better approach that doesn't iterate through the steps at all after making this representation, just examining the lengths of sequences starting at the start in one pass through to the end. I haven't worked out what that approach is exactly, but I'm reasonably confident that it would exist. After trying what I've outlined above, working that out is a good idea. I have a feeling it's something like - start total at 0, keep track of minimum and maximum total reaches. Scan each value from the start of string, adding 1 to the total for each 1 encountered, subtracting 1 for each 0 encountered. The answer is the greater of the absolute values of the minimum and maximum reached by total. - I haven't verified that, it's just a hunch. Comments have lead to further speculation that doing this but adding together the maximum and absolute of minimum may be more realistic.
Time complexity - O(n)
void solve(string s) {
int n = s.size();
int zero = 0, One = 0, res = 0;
for (int i = 0; i < n; i++)
{
if (s[i] == '1')
{
if (zero > 0)
zero--;
else
res++;
One++;
}
else
{
if (One > 0)
One--;
else
res++;
zero++;
}
}
cout << res << endl;
}

What is wrong with the recursive algorithm developed for the below problem?

I have tried to solve an algorithmic problem. I have come up with a recursive algorithm to solve the same. This is the link to the problem:
https://codeforces.com/problemset/problem/1178/B
This problem is not from any contest that is currently going on.
I have coded my algorithm and had run it on a few test cases, it turns out that it is counting more than the correct amount. I went through my thought process again and again but could not find any mistake. I have written my algorithm (not the code, but just the recursive function I have thought of) below. Can I please know where had I gone wrong -- what was the mistake in my thought process?
Let my recursive function be called as count, it takes any of the below three forms as the algorithm proceeds.
count(i,'o',0) = count(i+1,'o',0) [+ count(i+1,'w',1) --> iff (i)th
element of the string is 'o']
count(i,'w',0) = count(i+1,'w',0) [+ count(i+2,'o',0) --> iff (i)th and (i+1)th elements are both equal to 'v']
count(i,'w',1) = count(i+1,'w',1) [+ 1 + count(i+2,'w',0) --> iff (i)th and (i+1)th elements are both equal to 'v']
Note: The recursive function calls present inside the [.] (square brackets) will be called iff the conditions mentioned after the arrows are satisfied.)
Explanation: The main idea behind the recursive function developed is to count the number of occurrences of the given sequence. The count function takes 3 arguments:
argument 1: The index of the string on which we are currently located.
argument 2: The pattern we are looking for (if this argument is 'o' it means that we are looking for the letter 'o' -- i.e. at which index it is there. If it is 'w' it means that we are looking for the pattern 'vv' -- i.e. we are looking for 2 consecutive indices where this pattern occurs.)
argument 3: This can be either 1 or 0. If it is 1 it means that we are looking for the 'vv' pattern, having already found the 'o' i.e. we are looking for the 'vv' pattern shown in bold: vvovv. If it is 0, it means that we are searching for the 'vv' pattern which will be the
beginning of the pattern vvovv (shown in bold.)
I will initiate the algorithm with count(0,'w',0) -- it means, we are at the 0th index of the string, we are looking for the pattern 'vv', and this 'vv' will be the prefix of the 'vvovv' pattern we wish to find.
So, the output of count(0,'w',0) should be my answer. Now comes the trouble, for the following input: "vvovooovovvovoovoovvvvovo" (say input1), my program (which is based on the above algorithm) gives the expected answer(= 50). But, when I just append "vv" to the above input to get a new input: "vvovooovovvovoovoovvvvovovv" (say input2) and run my algorithm again, I get 135 as the answer, while the correct answer is 75 (this is the answer the solution code returns). Why is this happening? Where had I made an error?
Also, one more doubt is if the output for the input1 is 50, then the output for the input2 should be at least twice right -- because all of the subsequences which were present in the input1, will be present in the input2 too and all of those subsequences can also form a new subsequence with the appended 'vv' -- this means we have at least 100 favourable subsequences right?
P.S. This is the link to the solution code https://codeforces.com/blog/entry/68534
This question doesn't need recursion or dynamic programming.
The basic idea is to count how many ws we have before and after each o.
If you have X vs, it means you have X - 1 ws.
Let's use vvvovvv as an example. We know that before and after the o we have 3 vs, which means 2 ws. To evaluate the answer, just multiply 2x2 = 4.
For each o we find, we just need to multiply the ws before and after it, sum it all and this is our answer.
We can find how many ws there are before and after each o in linear time.
#include <iostream>
using namespace std;
int convert_v_to_w(int v_count){
return max(0, v_count - 1);
}
int main(){
string s = "vvovooovovvovoovoovvvvovovvvov";
int n = s.size();
int wBefore[n];
int wAfter[n];
int v_count = 0, wb = 0, wa = 0;
//counting ws before each o
int i = 0;
while(i < n){
v_count = 0;
while(i < n && s[i] == 'v'){
v_count++;
i++;
}
wb += convert_v_to_w(v_count);
if(i < n && s[i] == 'o'){
wBefore[i] = wb;
}
i++;
}
//counting ws after each o
i = n - 1;
while(i >= 0){
v_count = 0;
while(i >= 0 && s[i] == 'v'){
v_count++;
i--;
}
wa += convert_v_to_w(v_count);
if(i >= 0 && s[i] == 'o'){
wAfter[i] = wa;
}
i--;
}
//evaluating answer by multiplying ws before and after each o
int ans = 0;
for(int i = 0; i < n; i++){
if(s[i] == 'o') ans += wBefore[i] * wAfter[i];
}
cout<<ans<<endl;
}
output: 100
complexity: O(n) time and space

What is the logic behind the algorithm

I am trying to solve a problem from codility
"Even sums"
but am unable to do so. Here is the question below.
Even sums is a game for two players. Players are given a sequence of N positive integers and take turns alternately. In each turn, a player chooses a non-empty slice (a subsequence of consecutive elements) such that the sum of values in this slice is even, then removes the slice and concatenates the remaining parts of the sequence. The first player who is unable to make a legal move loses the game.
You play this game against your opponent and you want to know if you can win, assuming both you and your opponent play optimally. You move first.
Write a function:
string solution(vector< int>& A);
that, given a zero-indexed array A consisting of N integers, returns a string of format "X,Y" where X and Y are, respectively, the first and last positions (inclusive) of the slice that you should remove on your first move in order to win, assuming you have a winning strategy. If there is more than one such winning slice, the function should return the one with the smallest value of X. If there is more than one slice with the smallest value of X, the function should return the shortest. If you do not have a winning strategy, the function should return "NO SOLUTION".
For example, given the following array:
A[0] = 4 A[1] = 5 A[2] = 3 A[3] = 7 A[4] = 2
the function should return "1,2". After removing a slice from positions 1 to 2 (with an even sum of 5 + 3 = 8), the remaining array is [4, 7, 2]. Then the opponent will be able to remove the first element (of even sum 4) or the last element (of even sum 2). Afterwards you can make a move that leaves the array containing just [7], so your opponent will not have a legal move and will lose. One of possible games is shown on the following picture
Note that removing slice "2,3" (with an even sum of 3 + 7 = 10) is also a winning move, but slice "1,2" has a smaller value of X.
For the following array:
A[0] = 2 A[ 1 ] = 5 A[2] = 4
the function should return "NO SOLUTION", since there is no strategy that guarantees you a win.
Assume that:
N is an integer within the range [1..100,000]; each element of array A is an integer within the range [1..1,000,000,000]. Complexity:
expected worst-case time complexity is O(N); expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments). Elements of input arrays can be modified.
I have found a solution online in python.
def check(start, end):
if start>end:
res = 'NO SOLUTION'
else:
res = str(start) + ',' + str(end)
return res
def trans( strr ):
if strr =='NO SOLUTION':
return (-1, -1)
else:
a, b = strr.split(',')
return ( int(a), int(b) )
def solution(A):
# write your code in Python 2.7
odd_list = [ ind for ind in range(len(A)) if A[ind]%2==1 ]
if len(odd_list)%2==0:
return check(0, len(A)-1)
odd_list = [-1] + odd_list + [len(A)]
res_cand = []
# the numbers at the either end of A are even
count = odd_list[1]
second_count = len(A)-1-odd_list[-2]
first_count = odd_list[2]-odd_list[1]-1
if second_count >= count:
res_cand.append( trans(check( odd_list[1]+1, len(A)-1-count )))
if first_count >= count:
res_cand.append( trans(check( odd_list[1]+count+1, len(A)-1 )))
twosum = first_count + second_count
if second_count < count <= twosum:
res_cand.append( trans(check( odd_list[1]+(first_count-(count-second_count))+1, odd_list[-2] )))
###########################################
count = len(A)-1-odd_list[-2]
first_count = odd_list[1]
second_count = odd_list[-2]-odd_list[-3]-1
if first_count >= count:
res_cand.append( trans(check( count, odd_list[-2]-1 )))
if second_count >= count:
res_cand.append( trans(check( 0, odd_list[-2]-count-1)) )
twosum = first_count + second_count
if second_count < count <= twosum:
res_cand.append( trans(check( count-second_count, odd_list[-3])) )
res_cand = sorted( res_cand, key=lambda x: (-x[0],-x[1]) )
cur = (-1, -2)
for item in res_cand:
if item[0]!=-1:
cur = item
return check( cur[0], cur[1] )
This code works and I am unable to understand the code and flow of one function to the the other. However I don't understand the logic of the algorithm. How it has approached the problem and solved it. This might be a long task but can anybody please care enough to explain me the algorithm. Thanks in advance.
So far I have figured out that the number of odd numbers are crucial to find out the result. Especially the index of the first odd number and the last odd number is needed to calculate the important values.
Now I need to understand the logic behind the comparison such as "if first_count >= count" and if "second_count < count <= twosum".
Update:
Hey guys I found out the solution to my question and finally understood the logic of the algorithm.
The idea lies behind the symmetry of the array. We can never win the game if the array is symmetrical. Here symmetrical is defined as the array where there is only one odd in the middle and equal number of evens on the either side of that one odd.
If there are even number of odds we can directly win the game.
If there are odd number of odds we should always try to make the array symmetrical. That is what the algorithm is trying to do.
Now there are two cases to it. Either the last odd will remain or the first odd will remain. I will be happy to explain more if you guys didn't understand it. Thanks.

return index of sequence of repeating numbers in array

given an array:
array = [16 16 16 22 23 23 23 25 52 52 52]
I want return a list of indices that point to the elements of three repeating numbers.
In this case that would be :
indices = find_sequence(nbr_repeats = 3)
print indices
[0 1 2 4 5 6 8 9 10]
what is the fastest and most elegant algorithm to use in order to implement find_sequence?
Simplest way i know of...keep a track of the first place you saw a number. Keep on going til you find a different number, then if the sequence is long enough, add all the numbers from the start of the sequence til just before the end.
(Of course, you'll have to check the sequence length after you're done checking elements, too. I did it by iterating one past the end and just skipping the element check on the last iteration.)
To find_repeats (input : list, minimum : integer):
start := 0
result := []
for each x from 0 to (input length):
' "*or*" here is a short-circuit or
' so we don't go checking an element that doesn't exist
if x == (input length) *or* array[x] != array[start]:
if (x - start) >= minimum:
append [start...(x - 1)] to result
start := x
return result
Based on OP's assumption:
the list is sorted
the largest frequency is nbr_repeats
This might work:
def find_sequence(nbr_repeats, l):
res = []
current = -1
count = 0
idx = 0
for i in l:
if i == current:
count += 1
if count == nbr_repeats:
for k in reversed(range(nbr_repeats)):
res.append(idx-k)
else:
current = i
count = 1
idx += 1
return res
This looks to me like a special case of the Boyer-Moore string search algorithm, and since any language you use will contain optimisations for string search, perhaps the most elegant answer is to treat your data as a character array (i.e. a string) and use your language's built in string search functions... Note that this only works if your numbers fit into your language's supported character set (e.g. no numbers bigger than 128 in ASCII)
Since you did not specify a language, here is a pseudocode:
find_sequence(array: array of int, nbr_repeats: int) : array of int
retVal = emty array of int // the return'd array
last = empty array of int // collection of last seen same elements
i = -1
for each element e in array
++i
if (isempty(last))
add(last, e) // just starting
else if (count(last, e) >= nbr_repeats)
add(retVal, i-nbr_repeats) // found an index
else if (e == first(last))
add(last, e) // we have encountered this element before
else
if (count(last, e) >= nbr_repeats)
for (j=nbr_repeats-1; j>0; --j)
add(retVal, i-j) // catching up to i with indices
last = [e] // new element
if (count(last, e) >= nbr_repeats)
for (j=nbr_repeats-1; j>0; --j)
add(retVal, i-j) // handle end of array properly
return retVal
Edit: removed comment about sorting as it would mangle the original indices.
Note: you could also just keep the last element and its seen-count instead of maintaining a list of last same elements

Programming Interview Question / how to find if any two integers in an array sum to zero?

Not a homework question, but a possible interview question...
Given an array of integers, write an algorithm that will check if the sum of any two is zero.
What is the Big O of this solution?
Looking for non brute force methods
Use a lookup table: Scan through the array, inserting all positive values into the table. If you encounter a negative value of the same magnitude (which you can easily lookup in the table); the sum of them will be zero. The lookup table can be a hashtable to conserve memory.
This solution should be O(N).
Pseudo code:
var table = new HashSet<int>();
var array = // your int array
foreach(int n in array)
{
if ( !table.Contains(n) )
table.Add(n);
if ( table.Contains(n*-1) )
// You found it.;
}
The hashtable solution others have mentioned is usually O(n), but it can also degenerate to O(n^2) in theory.
Here's a Theta(n log n) solution that never degenerates:
Sort the array (optimal quicksort, heap sort, merge sort are all Theta(n log n))
for i = 1, array.len - 1
binary search for -array[i] in i+1, array.len
If your binary search ever returns true, then you can stop the algorithm and you have a solution.
An O(n log n) solution (i.e., the sort) would be to sort all the data values then run a pointer from lowest to highest at the same time you run a pointer from highest to lowest:
def findmatch(array n):
lo = first_index_of(n)
hi = last_index_of(n)
while true:
if lo >= hi: # Catch where pointers have met.
return false
if n[lo] = -n[hi]: # Catch the match.
return true
if sign(n[lo]) = sign(n[hi]): # Catch where pointers are now same sign.
return false
if -n[lo] > n[hi]: # Move relevant pointer.
lo = lo + 1
else:
hi = hi - 1
An O(n) time complexity solution is to maintain an array of all values met:
def findmatch(array n):
maxval = maximum_value_in(n) # This is O(n).
array b = new array(0..maxval) # This is O(1).
zero_all(b) # This is O(n).
for i in index(n): # This is O(n).
if n[i] = 0:
if b[0] = 1:
return true
b[0] = 1
nextfor
if n[i] < 0:
if -n[i] <= maxval:
if b[-n[i]] = 1:
return true;
b[-n[i]] = -1
nextfor
if b[n[i]] = -1:
return true;
b[n[i]] = 1
This works by simply maintaining a sign for a given magnitude, every possible magnitude between 0 and the maximum value.
So, if at any point we find -12, we set b[12] to -1. Then later, if we find 12, we know we have a pair. Same for finding the positive first except we set the sign to 1. If we find two -12's in a row, that still sets b[12] to -1, waiting for a 12 to offset it.
The only special cases in this code are:
0 is treated specially since we need to detect it despite its somewhat strange properties in this algorithm (I treat it specially so as to not complicate the positive and negative cases).
low negative values whose magnitude is higher than the highest positive value can be safely ignored since no match is possible.
As with most tricky "minimise-time-complexity" algorithms, this one has a trade-off in that it may have a higher space complexity (such as when there's only one element in the array that happens to be positive two billion).
In that case, you would probably revert to the sorting O(n log n) solution but, if you know the limits up front (say if you're restricting the integers to the range [-100,100]), this can be a powerful optimisation.
In retrospect, perhaps a cleaner-looking solution may have been:
def findmatch(array num):
# Array empty means no match possible.
if num.size = 0:
return false
# Find biggest value, no match possible if empty.
max_positive = num[0]
for i = 1 to num.size - 1:
if num[i] > max_positive:
max_positive = num[i]
if max_positive < 0:
return false
# Create and init array of positives.
array found = new array[max_positive+1]
for i = 1 to found.size - 1:
found[i] = false
zero_found = false
# Check every value.
for i = 0 to num.size - 1:
# More than one zero means match is found.
if num[i] = 0:
if zero_found:
return true
zero_found = true
# Otherwise store fact that you found positive.
if num[i] > 0:
found[num[i]] = true
# Check every value again.
for i = 0 to num.size - 1:
# If negative and within positive range and positive was found, it's a match.
if num[i] < 0 and -num[i] <= max_positive:
if found[-num[i]]:
return true
# No matches found, return false.
return false
This makes one full pass and a partial pass (or full on no match) whereas the original made the partial pass only but I think it's easier to read and only needs one bit per number (positive found or not found) rather than two (none, positive or negative found). In any case, it's still very much O(n) time complexity.
I think IVlad's answer is probably what you're after, but here's a slightly more off the wall approach.
If the integers are likely to be small and memory is not a constraint, then you can use a BitArray collection. This is a .NET class in System.Collections, though Microsoft's C++ has a bitset equivalent.
The BitArray class allocates a lump of memory, and fills it with zeroes. You can then 'get' and 'set' bits at a designated index, so you could call myBitArray.Set(18, true), which would set the bit at index 18 in the memory block (which then reads something like 00000000, 00000000, 00100000). The operation to set a bit is an O(1) operation.
So, assuming a 32 bit integer scope, and 1Gb of spare memory, you could do the following approach:
BitArray myPositives = new BitArray(int.MaxValue);
BitArray myNegatives = new BitArray(int.MaxValue);
bool pairIsFound = false;
for each (int testValue in arrayOfIntegers)
{
if (testValue < 0)
{
// -ve number - have we seen the +ve yet?
if (myPositives.get(-testValue))
{
pairIsFound = true;
break;
}
// Not seen the +ve, so log that we've seen the -ve.
myNegatives.set(-testValue, true);
}
else
{
// +ve number (inc. zero). Have we seen the -ve yet?
if (myNegatives.get(testValue))
{
pairIsFound = true;
break;
}
// Not seen the -ve, so log that we've seen the +ve.
myPositives.set(testValue, true);
if (testValue == 0)
{
myNegatives.set(0, true);
}
}
}
// query setting of pairIsFound to see if a pair totals to zero.
Now I'm no statistician, but I think this is an O(n) algorithm. There is no sorting required, and the longest duration scenario is when no pairs exist and the whole integer array is iterated through.
Well - it's different, but I think it's the fastest solution posted so far.
Comments?
Maybe stick each number in a hash table, and if you see a negative one check for a collision? O(n). Are you sure the question isn't to find if ANY sum of elements in the array is equal to 0?
Given a sorted array you can find number pairs (-n and +n) by using two pointers:
the first pointer moves forward (over the negative numbers),
the second pointer moves backwards (over the positive numbers),
depending on the values the pointers point at you move one of the pointers (the one where the absolute value is larger)
you stop as soon as the pointers meet or one passed 0
same values (one negative, one possitive or both null) are a match.
Now, this is O(n), but sorting (if neccessary) is O(n*log(n)).
EDIT: example code (C#)
// sorted array
var numbers = new[]
{
-5, -3, -1, 0, 0, 0, 1, 2, 4, 5, 7, 10 , 12
};
var npointer = 0; // pointer to negative numbers
var ppointer = numbers.Length - 1; // pointer to positive numbers
while( npointer < ppointer )
{
var nnumber = numbers[npointer];
var pnumber = numbers[ppointer];
// each pointer scans only its number range (neg or pos)
if( nnumber > 0 || pnumber < 0 )
{
break;
}
// Do we have a match?
if( nnumber + pnumber == 0 )
{
Debug.WriteLine( nnumber + " + " + pnumber );
}
// Adjust one pointer
if( -nnumber > pnumber )
{
npointer++;
}
else
{
ppointer--;
}
}
Interesting: we have 0, 0, 0 in the array. The algorithm will output two pairs. But in fact there are three pairs ... we need more specification what exactly should be output.
Here's a nice mathematical way to do it: Keep in mind all prime numbers (i.e. construct an array prime[0 .. max(array)], where n is the length of the input array, so that prime[i] stands for the i-th prime.
counter = 1
for i in inputarray:
if (i >= 0):
counter = counter * prime[i]
for i in inputarray:
if (i <= 0):
if (counter % prime[-i] == 0):
return "found"
return "not found"
However, the problem when it comes to implementation is that storing/multiplying prime numbers is in a traditional model just O(1), but if the array (i.e. n) is large enough, this model is inapropriate.
However, it is a theoretic algorithm that does the job.
Here's a slight variation on IVlad's solution which I think is conceptually simpler, and also n log n but with fewer comparisons. The general idea is to start on both ends of the sorted array, and march the indices towards each other. At each step, only move the index whose array value is further from 0 -- in only Theta(n) comparisons, you'll know the answer.
sort the array (n log n)
loop, starting with i=0, j=n-1
if a[i] == -a[j], then stop:
if a[i] != 0 or i != j, report success, else failure
if i >= j, then stop: report failure
if abs(a[i]) > abs(a[j]) then i++ else j--
(Yeah, probably a bunch of corner cases in here I didn't think about. You can thank that pint of homebrew for that.)
e.g.,
[ -4, -3, -1, 0, 1, 2 ] notes:
^i ^j a[i]!=a[j], i<j, abs(a[i])>abs(a[j])
^i ^j a[i]!=a[j], i<j, abs(a[i])>abs(a[j])
^i ^j a[i]!=a[j], i<j, abs(a[i])<abs(a[j])
^i ^j a[i]==a[j] -> done
The sum of two integers can only be zero if one is the negative of the other, like 7 and -7, or 2 and -2.

Resources