excuse me for the confusing title, I need to implement an algorithm which can be simplified as the following:
given an array of integers, and the number of merges needed (denoted as k), return the maximum min value of the merged array, a merge can only happen with adjacent elements.
E.g. array = [3,2,8,2,9], k = 2
The maximum min value of the array after two merges is 5, and the merged array is [5, 10, 9]. In this example, we need to merge elements 3 & 2 and 8 & 2.
Any other merge strategies will yield min val that is smaller or equal to 5, e.g.:
[5,8,11], [3,10,11], [13,2,9](merged elements can be merged again)
What is the best data structure to represent the data and what is an efficient algorithm to address the problem? As far as I can think of, a greedy algorithm needs to be applied where a merge needs to happen with the current min value of the array and one of its smaller neighboring element.
Edit: I just realized that greedy algorithm might not apply, sorry for the misleading comment, if it doesn't distinguish between merging with left or right elements, this will generate the wrong answer. Take this as an example, given an array = [4,5,3,5], and we need to remove 2 elements.
With greedy, [4,5,3,5] -> [4,8,5] -> [12,5], so the answer is 5; however the correct answer should be 8 with the following merge sequence:
[4,5,3,5] -> [4,5,8] -> [9,8]
ValPosFrom is a simple class that stores those things, from being the place to merge from . you can get non deterministic results from things like List = 3,2,6,3,2 and k=1 it will merge one of the 2 mins to 5 but it doesn't matter which one. it converges when all of any positions neighbors values are unique.
private static List<Integer> merge(List<Integer> things, int merges) {
List<Integer> result = new ArrayList<>(things);
for (int i = 0; i < merges; i++) {
int min = Integer.MAX_VALUE;
List<Integer> positions = new ArrayList<>();
for (int j = 0; j < result.size(); j++) {
if (result.get(j) < min) {
positions.clear();
positions.add(j);
min = result.get(j);
} else if (result.get(j) == min) {
positions.add(j);
}
}
List<ValPosFrom> neighbors = new ArrayList<>();
positions.forEach(p -> {
if (p - 1 >= 0) {
neighbors.add(new ValPosFrom(result.get(p - 1), p - 1, p));
}
if (p + 1 < result.size()) {
neighbors.add(new ValPosFrom(result.get(p + 1), p + 1, p));
}
});
ValPosFrom vpf = Collections.min(neighbors, Comparator.comparingInt(v -> v.val));
result.set(vpf.pos, result.get(vpf.pos) + result.get(vpf.from));
result.remove(vpf.from);
}
return result;
}
There are two integer arrays, i.e. A = [2,9,8,13] and B = [5,3,1,11,4],
we knew the length of the arrays,
i have to choose a integer from array A here let say i choose 13,
now i need to find out all the combinations of
(1 integer from array A and 1 or more than 1 integer from array B such as sum of the all the integer choosen from array A and array B is 13 or multiple of 13)
i.e.
'2' from array A and '11' from array B makes 2+11=13
'8' from array A and ('1' & '4') from array B makes 8+1+4=13
'9' from array A and ('1' & '3') from array B makes 9+1+3=13
'13' from array A and ('1','3','4','5') from array B makes 13+1+3+4+5=26
note:- have to choose only 1 integer from array A and can choose 1 or more than 1 integer from array B
only way i find out is write all the combinations in if else statment, and i definitly believe there is other way please help
I don't know if this is going to be any clear but here's my solution:
public static void main(String[] args) {
int[] A = new int[]{2,9,8,13};
int[] B = new int[]{5,3,1,11,4};
System.out.println(findMe(A, B));
}
private static List<List<Integer>> findMe(int[] A, int[] B) {
List<List<Integer>> solutions = new ArrayList<List<Integer>>();
Arrays.sort(A);
if (A[A.length - 1] > 9) { //if the greatest is greater than 9
for (int a1 : A) { //for all the values in A
Collection<List<Integer>> solution = findMeNow(B, a1);
solutions.addAll(solution);
}
}
return solutions;
}
private static Collection<List<Integer>> findMeNow(int[] B, int a1) {
List<List<Integer>> lists = new ArrayList<List<Integer>>(); //list of all possible combinations
Set<List<Integer>> solutions = new HashSet<List<Integer>>(); //list of all possible combinations
for (int b1 : B) {
int currentSize = lists.size();
for (int index = 0; index < currentSize; index++) {
//for each sub list, create a copy, add the new element and add it to the mother list
List<Integer> list = lists.get(index);
List<Integer> copyList = new ArrayList<>(list);
copyList.add(b1);
lists.add(copyList);
}
lists.add(new ArrayList<>(Arrays.asList(a1, b1)));
//Then check the sum for each of the resulting lists
for (List<Integer> list : lists) {
int sum = 0;
for (Integer value : list) {
sum += value;
}
if (sum % 13 == 0) {
solutions.add(list);
}
}
}
return solutions;
}
The basic idea is a "breadth first search": you're actually building a tree of all possible combinations of elements of the array B, such:
5
5, 3, [5,3]
5, 3, [5,3], [5,1], [3,1], [5,3,1], 1
etc. etc.
Worst case scenario this is equivalent to "all possible combinations" in terms of number of operations required, but on average it's going to be better.
P.S. : as you then required all the possible solutions, this is not a breadth first search anymore.
Here's some pseudo code:
sort the arrays
work along each from opposite ends (highest-lowest in one, lowest-highest the other)
advance the pointer such that all combos are found
This gives an O(n log n) algorithm, which is far better than a O(n * n) brute force "all combos" method.
It's O(n log n) because sorting is O(n log n), but the last part is just O(n).
I have been asked this question in a job interview and I have been wondering about the right answer.
You have an array of numbers from 0 to n-1, one of the numbers is removed, and replaced with a number already in the array which makes a duplicate of that number. How can we detect this duplicate in time O(n)?
For example, an array of 4,1,2,3 would become 4,1,2,2.
The easy solution of time O(n2) is to use a nested loop to look for the duplicate of each element.
This can be done in O(n) time and O(1) space.
(The algorithm only works because the numbers are consecutive integers in a known range):
In a single pass through the vector, compute the sum of all the numbers, and the sum of the squares of all the numbers.
Subtract the sum of all the numbers from N(N-1)/2. Call this A.
Subtract the sum of the squares from N(N-1)(2N-1)/6. Divide this by A. Call the result B.
The number which was removed is (B + A)/2 and the number it was replaced with is (B - A)/2.
Example:
The vector is [0, 1, 1, 2, 3, 5]:
N = 6
Sum of the vector is 0 + 1 + 1 + 2 + 3 + 5 = 12. N(N-1)/2 is 15. A = 3.
Sum of the squares is 0 + 1 + 1 + 4 + 9 + 25 = 40. N(N-1)(2N-1)/6 is 55. B = (55 - 40)/A = 5.
The number which was removed is (5 + 3) / 2 = 4.
The number it was replaced by is (5 - 3) / 2 = 1.
Why it works:
The sum of the original vector [0, ..., N-1] is N(N-1)/2. Suppose the value a was removed and replaced by b. Now the sum of the modified vector will be N(N-1)/2 + b - a. If we subtract the sum of the modified vector from N(N-1)/2 we get a - b. So A = a - b.
Similarly, the sum of the squares of the original vector is N(N-1)(2N-1)/6. The sum of the squares of the modified vector is N(N-1)(2N-1)/6 + b2 - a2. Subtracting the sum of the squares of the modified vector from the original sum gives a2 - b2, which is the same as (a+b)(a-b). So if we divide it by a - b (i.e., A), we get B = a + b.
Now B + A = a + b + a - b = 2a and B - A = a + b - (a - b) = 2b.
We have the original array int A[N]; Create a second array bool B[N] too, of type bool=false. Iterate the first array and set B[A[i]]=true if was false, else bing!
You can do it in O(N) time without any extra space. Here is how the algorithm works :
Iterate through array in the following manner :
For each element encountered, set its corresponding index value to negative.
Eg : if you find a[0] = 2. Got to a[2] and negate the value.
By doing this you flag it to be encountered. Since you know you cannot have negative numbers, you also know that you are the one who negated it.
Check if index corresponding to the value is already flagged negative, if yes you get the duplicated element. Eg : if a[0]=2 , go to a[2] and check if it is negative.
Lets say you have following array :
int a[] = {2,1,2,3,4};
After first element your array will be :
int a[] = {2,1,-2,3,4};
After second element your array will be :
int a[] = {2,-1,-2,3,4};
When you reach third element you go to a[2] and see its already negative. You get the duplicate.
Scan the array 3 times:
XOR together all the array elements -> A. XOR together all the numbers from 0 to N-1 -> B. Now A XOR B = X XOR D, where X is the removed element, and D is the duplicate element.
Choose any non-zero bit in A XOR B. XOR together all the array elements where this bit is set -> A1. XOR together all the numbers from 0 to N-1 where this bit is set -> B1. Now either A1 XOR B1 = X or A1 XOR B1 = D.
Scan the array once more and try to find A1 XOR B1. If it is found, this is the duplicate element. If not, the duplicate element is A XOR B XOR A1 XOR B1.
Use a HashSet to hold all numbers already seen. It operates in (amortized) O(1) time, so the total is O(N).
I suggest using a BitSet. We know N is small enough for array indexing, so the BitSet will be of reasonable size.
For each element of the array, check the bit corresponding to its value. If it is already set, that is the duplicate. If not, set the bit.
#rici is right about the time and space usage: "This can be done in O(n) time and O(1) space."
However, the question can be expanded to broader requirement: it's not necessary that there is only one duplicate number, and numbers might not be consecutive.
OJ puts it this way here:
(note 3 apparently can be narrowed)
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.
The question is very well explained and answered here by Keith Schwarz, using Floyd's cycle-finding algorithm:
The main trick we need to use to solve this problem is to notice that because we have an array of n elements ranging from 0 to n - 2, we can think of the array as defining a function f from the set {0, 1, ..., n - 1} onto itself. This function is defined by f(i) = A[i]. Given this setup, a duplicated value corresponds to a pair of indices i != j such that f(i) = f(j). Our challenge, therefore, is to find this pair (i, j). Once we have it, we can easily find the duplicated value by just picking f(i) = A[i].
But how are we to find this repeated value? It turns out that this is a well-studied problem in computer science called cycle detection. The general form of the problem is as follows. We are given a function f. Define the sequence x_i as
x_0 = k (for some k)
x_1 = f(x_0)
x_2 = f(f(x_0))
...
x_{n+1} = f(x_n)
Assuming that f maps from a domain into itself, this function will have one of three forms. First, if the domain is infinite, then the sequence could be infinitely long and nonrepeating. For example, the function f(n) = n + 1 on the integers has this property - no number is ever duplicated. Second, the sequence could be a closed loop, which means that there is some i so that x_0 = x_i. In this case, the sequence cycles through some fixed set of values indefinitely. Finally, the sequence could be "rho-shaped." In this case, the sequence looks something like this:
x_0 -> x_1 -> ... x_k -> x_{k+1} ... -> x_{k+j}
^ |
| |
+-----------------------+
That is, the sequence begins with a chain of elements that enters a cycle, then cycles around indefinitely. We'll denote the first element of the cycle that is reached in the sequence the "entry" of the cycle.
An python implementation can also be found here:
def findDuplicate(self, nums):
# The "tortoise and hare" step. We start at the end of the array and try
# to find an intersection point in the cycle.
slow = 0
fast = 0
# Keep advancing 'slow' by one step and 'fast' by two steps until they
# meet inside the loop.
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
# Start up another pointer from the end of the array and march it forward
# until it hits the pointer inside the array.
finder = 0
while True:
slow = nums[slow]
finder = nums[finder]
# If the two hit, the intersection index is the duplicate element.
if slow == finder:
return slow
Use hashtable. Including an element in a hashtable is O(1).
One working solution:
asume number are integers
create an array of [0 .. N]
int[] counter = new int[N];
Then iterate read and increment the counter:
if (counter[val] >0) {
// duplicate
} else {
counter[val]++;
}
This can be done in O(n) time and O(1) space.
Without modifying the input array
The idea is similar to finding the starting node of a loop in a linked list.
Maintain two pointers: fast and slow
slow = a[0]
fast = a[a[0]]
loop till slow != fast
Once we find the loop (slow == fast)
Reset slow back to zero
slow = 0
find the starting node
while(slow != fast){
slow = a[slow];
fast = a[fast];
}
slow is your duplicate number.
Here's a Java implementation:
class Solution {
public int findDuplicate(int[] nums) {
if(nums.length <= 1) return -1;
int slow = nums[0], fast = nums[nums[0]]; //slow = head.next, fast = head.next.next
while(slow != fast){ //check for loop
slow = nums[slow];
fast = nums[nums[fast]];
}
if(slow != fast) return -1;
slow = 0; //reset one pointer
while(slow != fast){ //find starting point of loop
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
This is an alternative solution in O(n) time and O(1) space. It is similar to rici's. I find it a bit easier to understand but, in practice, it will overflow faster.
Let X be the missing number and R be the repeated number.
We can assume the numbers are from [1..n], i.e. zero does not appear. In fact, while looping through the array, we can test if zero was found and return immediately if not.
Now consider:
sum(A) = n (n + 1) / 2 - X + R
product(A) = n! R / X
where product(A) is the product of all element in A skipping the zero. We have two equations in two unknowns from which X and R can be derived algebraically.
Edit: by popular demand, here is a worked-out example:
Let's set:
S = sum(A) - n (n + 1) / 2
P = n! / product(A)
Then our equations become:
R - X = S
X = R P
which can be solved to:
R = S / (1 - P)
X = P R = P S / (1 - P)
Example:
A = [0 1 2 2 4]
n = A.length - 1 = 4
S = (1 + 2 + 2 + 4) - 4 * 5 / 2 = -1
P = 4! / (1 * 2 * 2 * 4) = 3 / 2
R = -1 / (1 - 3/2) = -1 / -1/2 = 2
X = 3/2 * 2 = 3
You could proceed as follows:
sort your array by using a Linear-time sorting algorithm (e.g. Counting sort) - O(N)
scan the sorted array and stop as soon as two consecutive elements are equal - O(N)
public class FindDuplicate {
public static void main(String[] args) {
// assume the array is sorted, otherwise first we have to sort it.
// time efficiency is o(n)
int elementData[] = new int[] { 1, 2, 3, 3, 4, 5, 6, 8, 8 };
int count = 1;
int element1;
int element2;
for (int i = 0; i < elementData.length - 1; i++) {
element1 = elementData[i];
element2 = elementData[count];
count++;
if (element1 == element2) {
System.out.println(element2);
}
}
}
}
public void duplicateNumberInArray {
int a[] = new int[10];
Scanner inp = new Scanner(System.in);
for(int i=1;i<=5;i++){
System.out.println("enter no. ");
a[i] = inp.nextInt();
}
Set<Integer> st = new HashSet<Integer>();
Set<Integer> s = new HashSet<Integer>();
for(int i=1;i<=5;i++){
if(!st.add(a[i])){
s.add(a[i]);
}
}
Iterator<Integer> itr = s.iterator();
System.out.println("Duplicate numbers are");
while(itr.hasNext()){
System.out.println(itr.next());
}
}
First of all creating an array of integer using Scanner class. Then iterating a loop through the numbers and checking if the number can be added to set (Numbers can be added to set only when that particular number should not be in set already, means set does not allow duplicate no. to add and return a boolean vale FALSE on adding duplicate value).If no. cannot be added means it is duplicate so add that duplicate number into another set, so that we can print later. Please note onething that we are adding the duplicate number into a set because it might be possible that duplicate number might be repeated several times, hence add it only once.At last we are printing set using Iterator.
//This is similar to the HashSet approach but uses only one data structure:
int[] a = { 1, 4, 6, 7, 4, 6, 5, 22, 33, 44, 11, 5 };
LinkedHashMap<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();
for (int i : a) {
map.put(i, map.containsKey(i) ? (map.get(i)) + 1 : 1);
}
Set<Entry<Integer, Integer>> es = map.entrySet();
Iterator<Entry<Integer, Integer>> it = es.iterator();
while (it.hasNext()) {
Entry<Integer, Integer> e = it.next();
if (e.getValue() > 1) {
System.out.println("Dupe " + e.getKey());
}
}
We can do using hashMap efficiently:
Integer[] a = {1,2,3,4,0,1,5,2,1,1,1,};
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int x : a)
{
if (map.containsKey(x)) map.put(x,map.get(x)+1);
else map.put(x,1);
}
Integer [] keys = map.keySet().toArray(new Integer[map.size()]);
for(int x : keys)
{
if(map.get(x)!=1)
{
System.out.println(x+" repeats : "+map.get(x));
}
}
This program is based on c# and if you want to do this program using another programming language you have to firstly change an array in accending order and compare the first element to the second element.If it is equal then repeated number found.Program is
int[] array=new int[]{1,2,3,4,5,6,7,8,9,4};
Array.Sort(array);
for(int a=0;a<array.Length-1;a++)
{
if(array[a]==array[a+1]
{
Console.WriteLine("This {0} element is repeated",array[a]);
}
}
Console.WriteLine("Not repeated number in array");
sort the array O(n ln n)
using the sliding window trick to traverse the array O(n)
Space is O(1)
Arrays.sort(input);
for(int i = 0, j = 1; j < input.length ; j++, i++){
if( input[i] == input[j]){
System.out.println(input[i]);
while(j < input.length && input[i] == input[j]) j++;
i = j - 1;
}
}
Test case int[] { 1, 2, 3, 7, 7, 8, 3, 5, 7, 1, 2, 7 }
output 1, 2, 3, 7
Traverse through the array and check the sign of array[abs(array[i])], if positive make it as negative and if it is negative then print it, as follows:
import static java.lang.Math.abs;
public class FindRepeatedNumber {
private static void findRepeatedNumber(int arr[]) {
int i;
for (i = 0; i < arr.length; i++) {
if (arr[abs(arr[i])] > 0)
arr[abs(arr[i])] = -arr[abs(arr[i])];
else {
System.out.print(abs(arr[i]) + ",");
}
}
}
public static void main(String[] args) {
int arr[] = { 4, 2, 4, 5, 2, 3, 1 };
findRepeatedNumber(arr);
}
}
Reference: http://www.geeksforgeeks.org/find-duplicates-in-on-time-and-constant-extra-space/
As described,
You have an array of numbers from 0 to n-1, one of the numbers is
removed, and replaced with a number already in the array which makes a
duplicate of that number.
I'm assuming elements in the array are sorted except the duplicate entry. If this is the scenario , we can achieve the goal easily as below :
public static void main(String[] args) {
//int arr[] = { 0, 1, 2, 2, 3 };
int arr[] = { 1, 2, 3, 4, 3, 6 };
int len = arr.length;
int iMax = arr[0];
for (int i = 1; i < len; i++) {
iMax = Math.max(iMax, arr[i]);
if (arr[i] < iMax) {
System.out.println(arr[i]);
break;
}else if(arr[i+1] <= iMax) {
System.out.println(arr[i+1]);
break;
}
}
}
O(n) time and O(1) space ;please share your thoughts.
Here is the simple solution with hashmap in O(n) time.
#include<iostream>
#include<map>
using namespace std;
int main()
{
int a[]={1,3,2,7,5,1,8,3,6,10};
map<int,int> mp;
for(int i=0;i<10;i++){
if(mp.find(a[i]) == mp.end())
mp.insert({a[i],1});
else
mp[a[i]]++;
}
for(auto i=mp.begin();i!=mp.end();++i){
if(i->second > 1)
cout<<i->first<<" ";
}
}
int[] a = {5, 6, 8, 9, 3, 4, 2, 9 };
int[] b = {5, 6, 8, 9, 3, 6, 1, 9 };
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i])
{
Console.Write("Original Array manipulated at position {0} + "\t\n"
+ "and the element is {1} replaced by {2} ", i,
a[i],b[i] + "\t\n" );
break;
}
}
Console.Read();
///use break if want to check only one manipulation in original array.
///If want to check more then one manipulation in original array, remove break
This video If Programming Was An Anime is too fun not to share. It is the same problem and the video has the answers:
Sorting
Creating a hashmap/dictionary.
Creating an array. (Though this is partially skipped over.)
Using the Tortoise and Hare Algorithm.
Note: This problem is more of a trivia problem than it is real world. Any solution beyond a hashmap is premature optimization, except in rare limited ram situations, like embedded programming.
Furthermore, when is the last time you've seen in the real world an array where all of the variables within the array fit within the size of the array? Eg, if the data in the array is bytes (0-255) when do you have an array 256 elements or larger without nulls or inf within it, and you need to find a duplicate number? This scenario is so rare you will probably never get to use this trick in your entire career.
Because it is a trivia problem and is not real world the question, I'd be cautious accepting an offer from a company that asks trivia questions like this, because people will pass the interview by sheer luck instead of skill. This implies the devs there are not guaranteed to be skilled, which unless you're okay teaching your seniors skills, you might have a bad time.
int a[] = {2,1,2,3,4};
int b[] = {0};
for(int i = 0; i < a.size; i++)
{
if(a[i] == a[i+1])
{
//duplicate found
//copy it to second array
b[i] = a[i];
}
}
given a unsorted set of n integers, return all subsets of size k (i.e. each set has k unique elements) that sum to 0.
So I gave the interviewer the following solution ( which I studied on GeekViewpoint). No extra space used, everything is done in place, etc. But of course the cost is a high time complexity of O(n^k) where k=tuple in the solution.
public void zeroSumTripplets(int[] A, int tuple, int sum) {
int[] index = new int[tuple];
for (int i = 0; i < tuple; i++)
index[i] = i;
int total = combinationSize(A.length, tuple);
for (int i = 0; i < total; i++) {
if (0 != i)
nextCombination(index, A.length, tuple);
printMatch(A, Arrays.copyOf(index, tuple), sum);
}// for
}// zeroSumTripplets(int[], int, int)
private void printMatch(int[] A, int[] ndx, int sum) {
int calc = 0;
for (int i = 0; i < ndx.length; i++)
calc += A[ndx[i]];
if (calc == sum) {
Integer[] t = new Integer[ndx.length];
for (int i = 0; i < ndx.length; i++)
t[i] = A[ndx[i]];
System.out.println(Arrays.toString(t));
}// if
}// printMatch(int[], int[], int)
But then she imposed the following requirements:
must use hashmap in answer so to reduce time complexity
Must absolutely -- ABSOLUTELY -- provide time complexity for general case
hint when k=6, O(n^3)
She was more interested in time-complexity more than anything else.
Does anyone know a solution that would satisfy the new constraints?
EDIT:
Supposedly, in the correct solution, the map is to store the elements of the input and the map is then to be used as a look up table just as in the case for k=2.
When the size of the subset is 2 (i.e. k=2), the answer is trivial: loop through and load all the elements into a map. Then loop through the inputs again this time searching the map for sum - input[i] where i is the index from 0 to n-1, which would then be the answers. Supposedly this trivial case can be extended to where k is anything.
Since no-one else has made an attempt, I might as well throw in at least a partial solution. As I pointed out in an earlier comment, this problem is a variant of the subset sum problem and I have relied heavily on documented approaches to that problem in developing this solution.
We're trying to write a function subsetsWithSum(A, k, s) that computes all the k-length subsets of A that sum to s. This problem lends itself to a recursive solution in two ways:
The solution of subsetsWithSum(x1 ... xn, k, s) can be found by computing subsetsWithSum(x2 ... xn, k, s) and adding all the valid subsets (if any) that include x1; and
All the valid subsets that include element xi can be found by computing subsetsWithSum(A - xi, k-1, s-xi) and adding xi to each subset (if any) that results.
The base case for the recursion occurs when k is 1, in which case the solution to subsetsWithSum(A, 1, s) is the set of all single element subsets where that element is equal to s.
So a first stab at a solution would be
/**
* Return all k-length subsets of A starting at offset o that sum to s.
* #param A - an unordered list of integers.
* #param k - the length of the subsets to find.
* #param s - the sum of the subsets to find.
* #param o - the offset in A at which to search.
* #return A list of k-length subsets of A that sum to s.
*/
public static List<List<Integer>> subsetsWithSum(
List<Integer> A,
int k,
int s,
int o)
{
List<List<Integer>> results = new LinkedList<List<Integer>>();
if (k == 1)
{
if (A.get(o) == s)
results.add(Arrays.asList(o));
}
else
{
for (List<Integer> sub : subsetsWithSum(A, k-1, s-A.get(o), o+1))
{
List<Integer> newSub = new LinkedList<Integer>(sub);
newSub.add(0, o);
results.add(0, newSub);
}
}
if (o < A.size() - k)
results.addAll(subsetsWithSum(A, k, s, o+1));
return results;
}
Now, notice that this solution will often call subsetsWithSum(...) with the same set of arguments that it has been called with before. Hence, subsetsWithSum is just begging to be memoized.
To memoize the function, I've put the arguments k, s and o into a three element list which will be the key to a map from these arguments to a result computed earlier (if there is one):
public static List<List<Integer>> subsetsWithSum(
List<Integer> A,
List<Integer> args,
Map<List<Integer>, List<List<Integer>>> cache)
{
if (cache.containsKey(args))
return cache.get(args);
int k = args.get(0), s = args.get(1), o = args.get(2);
List<List<Integer>> results = new LinkedList<List<Integer>>();
if (k == 1)
{
if (A.get(o) == s)
results.add(Arrays.asList(o));
}
else
{
List<Integer> newArgs = Arrays.asList(k-1, s-A.get(o), o+1);
for (List<Integer> sub : subsetsWithSum(A, newArgs, cache))
{
List<Integer> newSub = new LinkedList<Integer>(sub);
newSub.add(0, o);
results.add(0, newSub);
}
}
if (o < A.size() - k)
results.addAll(subsetsWithSum(A, Arrays.asList(k, s, o+1), cache));
cache.put(args, results);
return results;
}
To use the subsetsWithSum function to compute all the k-length subsets that sum to zero, one can use the following function:
public static List<List<Integer>> subsetsWithZeroSum(List<Integer> A, int k)
{
Map<List<Integer>, List<List<Integer>>> cache =
new HashMap<List<Integer>, List<List<Integer>>> ();
return subsetsWithSum(A, Arrays.asList(k, 0, 0), cache);
}
Regrettably my complexity calculating skills are a bit (read: very) rusty, so hopefully someone else can help us compute the time complexity of this solution, but it should be an improvement on the brute-force approach.
Edit: Just for clarity, note that the first solution above should be equivalent in time complexity to a brute-force approach. Memoizing the function should help in many cases, but in the worst case the cache will never contain a useful result and the time complexity will then be the same as the first solution. Note also that the subset-sum problem is NP-complete meaning that any solution has an exponential time complexity. End Edit.
Just for completeness, I tested this with:
public static void main(String[] args) {
List<Integer> data = Arrays.asList(9, 1, -3, -7, 5, -11);
for (List<Integer> sub : subsetsWithZeroSum(data, 4))
{
for (int i : sub)
{
System.out.print(data.get(i));
System.out.print(" ");
}
System.out.println();
}
}
and it printed:
9 -3 5 -11
9 1 -3 -7
I think your answer was very close to what they were looking for, but you can improve the complexity by noticing that any subset of size k can be thought of as two subsets of size k/2. So instead of finding all subsets of size k (which takes O(n^k) assuming k is small), use your code to find all subsets of size k/2, and put each subset in a hashtable, with its sum as the key.
Then iterate through each subset of size k/2 with a positive sum (call the sum S) and check the hashtable for a subset whose sum is -S. If there is one then the combination of the two subsets of size k/2 is a subset of size k whose sum is zero.
So in the case of k=6 that they gave, you would find all subsets of size 3 and compute their sums (this will take O(n^3) time). Then checking the hashtable will take O(1) time for each subset, so the total time is O(n^3). In general this approach will take O(n^(k/2)) assuming k is small, and you can generalize it for odd values of k by taking subsets of size floor(k/2) and floor(k/2)+1.
#kasavbere -
Recently a friend had one of those harrowing all-day interviews for a C++ programming job with Google. His experience was similar to yours.
It inspired him to write this article - I think you might enjoy it:
The Pragmatic Defense
I'm trying to figure out how to modify the n greatest elements of an array without modifying their position. For example, suppose I have an array of ints {5, 2, 3, 4, 8, 9, 1, 3};
I want to add 1 to the two greatest elements, making the array {5, 2, 3, 4, 9, 10, 1, 3}.
All of the methods I can think of to go about doing this end up feeling clunky and unintuitive when I try to implement them, signaling to me that I'm not thinking about it correctly. For example, I could use a TreeMap with the values of the array as keys and their indices as values to find the greatest values, modify them, and then throw them back into the array, but then I would have have to implement my own Comparator to sort the TreeMap in reverse order(unless there's an easier way I'm not aware of?). I was also considering copying the contents of the array into a list, iterating through n times, each time finding the greatest element and its index, putting the modified greatest element back into the array at that index, removing the element from the list, and repeat, but that feels sloppy and inefficient to me.
Any suggestions as to how to approach this type of problem?
The simplest thing would be to scan your array, and store the indices of the n highest values. Increment the values of those elements.
This is going to be O(n) performance, and I don't think any fancier methods can beat that.
edit to add: you can sort the array in place in O(n) at best, in which case you can get the n highest values very quickly, but the requirement is to not change position of the elements, so you'd have to start with a copy of the array if you wanted to do that (or preserve ordering information so you could put everything back afterward).
You might be over engineering the solution to this problem: scan the array, from beginning to end, and mark the two largest elements. Return to the two greatest elements and add 1 to it. The solution shouldn't be longer than 10 lines.
Loop over the array and keep track of the indices and values of the two largest items
a. Initialize the tracker with -1 for an index and MIN_INT for a value or the first two values of the array
b. At each step of the loop compare the current value against the two tracker values and update if necessary
Increment the two items
Any algorithm you choose should be O(n) for this. Sorting and n passes are way overkill.
Find the nth largest element (call it K) using techniques here and here (can be done in linear time), then go through the array modifying all elements >= K.
i would do something like this
int[] indices = new int[2];
int[] maximas = new int[] { 0, 0 };
int[] data = new int[] { 3, 4, 5, 1, 9 };
for (int i = 0; i < 5; ++i)
{
if (data[i] > maximas[1])
{
maximas[0] = maximas[1];
maximas[1] = data[i];
indices[0] = indices[1];
indices[1] = i;
}
else if (data[i] > maximas[0])
{
maximas[0] = data[i];
indices[0] = i;
}
}
didn't test it, but I think it should work :)
I have tought a bit about this but I cannot achieve more than worstcase:
O( n + (m-n) * n ) : (m > n)
best case:
O(m) : (m <= n)
where m = number of values, n = number of greatest value to search
This is the implementation in C#, but you can easily adapt to java:
int n = 3;
List<int> values = new List<int> {1,1,1,8,7,6,5};
List<int> greatestIndexes = new List<int>();
for (int i = 0; i < values.Count; i++) {
if (greatestIndexes.Count < n)
{
greatestIndexes.Add(i);
}
else {
int minIndex = -1, minValue = int.MaxValue;
for (int j = 0; j < n; j++)
{
if (values[greatestIndexes[j]] < values[i]) {
if (minValue > values[greatestIndexes[j]])
{
minValue = values[greatestIndexes[j]];
minIndex = j;
}
}
}
if (minIndex != -1)
{
greatestIndexes.RemoveAt(minIndex);
greatestIndexes.Add(i);
}
}
}
foreach (var i in greatestIndexes) {
Console.WriteLine(values[i]);
}
Output:
8
7
6