Create a list of combinations recursively - java

I am trying to implement a combination method using recursion. I have already done it using a for loop but want to approach it via recursion.
The method gets two inputs and creates all possible combinations. It is supposed to store the combination in an instance variable that I have called "combination". I tried different codes but they don't work properly. I think recursive back-tracking is the best way to approach this.
For example, object.pe1.combination(4,3) would create something like this:
image of combination list
// Instance variable needed for this problem
ArrayList<Integer[]> combination;
private int size;
// To calculate all the possible combinations
private int factorial(int x){
if (x == 0) {
return 1;
}
else {
return x * factorial(x - 1);
}
}
void combination(int n, int r) {
// formula for calculating the combination of r items selected among n: n! / (r! * (n - r)!)
int noc = factorial (n) / (factorial (r) * factorial (n - r)); // number of combinations
this.combination = new ArrayList<Integer[]>(noc); // 2D array. Each slot stores a combination
if (noc == 0) {
}
else {
this.combination = new ArrayList<Integer[]>(noc);
int[] arr = new int[n];
int[] temparr = new int[r];
arr = createCombination(temparr, 0, r);
}
}
private int[] createCombination(int[] temparr, int index, int r) {
// this is where I am stuck
temparr[0] = index;
if (temparr[r] == 0) {
temparr = new int[r - 1];
temparr = createCombination(temparr, index + 1, r - 1);
}
else {
return temparr;
}
}

A recursive implementation of any algorithm is comprised of two parts:
base case - a condition that terminates a branch of recursive calls and represents an edge case for which result is known in advance;
recursive case - a part where the logic resides and the recursive calls are made.
For this task a base case will be a situation when size of the combination equals to a target size (denoted as r in your code, in the code below I gave it a name targetSize).
Explanation of the recursive logic:
Every method call tracks its own combination;
Every combination unless it reaches the targetSize is used as a blue-print for other combinations;
Each item from the source of data can be used only once, hence when it's being added to a combination it must be removed from the source.
The type ArrayList<Integer[]> which you are using to store the combination isn't a good choice. Arrays and generics do not play well together. List<List<Integer>> will be more appropriate for this purpose.
Also in my code List is used as a source of data instead of an array, which isn't a complicated conversion and can be achieved easily.
Pay attention to the comments in the code.
private List<List<Integer>> createCombination(List<Integer> source, List<Integer> comb, int targetSize) {
if (comb.size() == targetSize) { // base condition of the recursion
List<List<Integer>> result = new ArrayList<>();
result.add(comb);
return result;
}
List<List<Integer>> result = new ArrayList<>();
Iterator<Integer> iterator = source.iterator();
while (iterator.hasNext()) {
// taking an element from a source
Integer item = iterator.next();
iterator.remove(); // in order not to get repeated the element has to be removed
// creating a new combination using existing as a base
List<Integer> newComb = new ArrayList<>(comb);
newComb.add(item); // adding the element that was removed from the source
result.addAll(createCombination(new ArrayList<>(source), newComb, targetSize)); // adding all the combinations generated
}
return result;
}
For the input
createCombination(new ArrayList<>(List.of(1, 2, 3)), new ArrayList<>(), 2));
It'll produce the output
[[1, 2], [1, 3], [2, 3]]

Related

Array of greater elements

I'd like to create a method that recieves an array and a value to compare. It should return the numbers which are greater than the value compared in the form of an array.
For instance, greaterThan([3, 4, 5, 6], 4]) should return [5, 6] and greaterThan([3, 4, 5, 6], 5]) should return [6].
Since the length of the result array is dynamic, I've incremented the size each time it finds a larger element.
static String greaterThan(int[] vectorCompare, int valueCompare)
{
Arrays.sort(vectorCompare); // make it faster
int size = 0;
int[] result = new int[size];
for (int value : vectorCompare)
{
if (valueCompare < value)
{
size++;
}
}
int[] resultCopy = Arrays.copyOf(result, size);
// what to do next?
return Arrays.toString(resultCopy);
}
Any tips for me? I thought of using ArrayList but I'm kinda stuck.
You don't need to sort it, just collect the elements in a list and call toString on the contents. You can sort the list of collected values afterwards if the order is important.
static String greaterThan(int[] vector, int compare) {
List<Integer> greater = new ArrayList<>();
for (int i : vector) {
if (i > compare) {
greater.add(i);
}
}
return greater.toString();
}
Sorting your input in the above method will actually make it slow. With normal array iteration and comparison your complexity will be O(n) while with sorting it would be O(nlogn). Instead you can try this.
static String greaterThan(int[] vectorCompare, int valueCompare)
{
List<Integer> list = new ArrayList<>();
for (int value : vectorCompare)
{
if (valueCompare < value)
{
list.add(value);
}
}
return list.toString();
}
By using Arraylist you would not be required to worry about the number of elements that are to be returned.
It can be done as below:
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(4);
list.add(6);
list.add(7);
int valToCompare = 4;
List<Integer> result = find(list, valToCompare);
private static List<Integer> find(List<Integer> list, int val) {
return list.stream().filter(v -> v > val).collect(Collectors.toList());
}
This is about as simple as it can get. This works with arrays.
public static int[] greaterThan(int[] numbs, int max) {
return Arrays.stream(numbs).parallel().filter(a -> a > max).toArray();
}
You can also do the same for a List.
public static List<Integer> greaterThan(List<Integer> numbs, int max) {
return numbs.parallelstream().filter(a -> a > max).collect(Collectors.toList());
}
In both cases, the List or array is turned into parallel streams of values. Those are filtered to allow only those that are greater than max thru the pipeline. Then they are collected into a List or an array and returned. This particular task is suited to parallel operations in multiple threads and will speed up execution, especially if your PC has multiple cores.
Even if you're new at Java, lambdas and streams are important features of the language and should be learned.

How to calculate the shortest path between two vertices in Graph with given parameters?

We need to compute the minCost(), which has follwing parameters:
gNodes - no of Nodes in graph g.
an array of int's, gFrom, where each gfrom[i] denotes a node connected by ith edge in graph g.
an array of int's, gTo, where each gTo[i] denotes a node connected by ith edge in graph g.
an array of int's, gWeight, denoting the respective weights of each edge in graph g.
an int, start, denoting the start node index.
an int, end, denoting the end node index.
an integer, wExtra, denoting the weight of optional extra edge.
We need to find the path from start to end having minimum possible weight. We can add at most one extra edge(ie. zero or one) having wExtra weight between any two distinct nodes that are not already connected by an edge. The function must return an int denoting the minimum path weight from start to end.
I was able to come up with following code (Dijkstra algorithm) but it doesn't give the expected output.
public static int minCost(int gNodes, int[] gFrom, int[] gTo, int[] gWeights, int start, int end) {
//making a array to store shortest length and filling it with infinity except the first one
int[] shortest = new int[gNodes];
for (int i = 0; i < gNodes; i++) {
shortest[i] = Integer.MAX_VALUE;
}
shortest[start]=0;
//filling the Queue with all vertices
Queue<Integer> theQ = new PriorityQueue<>();
for (int i = 0; i < gNodes; i++) {
theQ.add(i + 1);
}
//following the algorithm
while (!theQ.isEmpty()) {
int u = theQ.poll();
//making a list of adjacent vertices
List<Integer> adjacent = new ArrayList<>();
for (int i = 0; i < gFrom.length; i++) {
if (gFrom[i] == u) {
adjacent.add(gTo[i]);
} else if (gTo[i] == u) {
adjacent.add(gFrom[i]);
}
}
for (int v: adjacent) {
int weight=0;
for (int i = 0; i < gFrom.length; i++) {
if ((gFrom[i] == u && gTo[i] == v) || (gFrom[i] == v && gTo[i] == u)) {
weight = gWeights[i];
}
}
//relaxing the verices
if (shortest[v] > shortest[u] + weight) {
shortest[v] = shortest[u] + weight;
}
if (v == end) {
return shortest[v];
}
theQ.add(v);
}
}
return -1;
}
public static void main(String[] args) {
int gNodes = 4;
int[] gFrom = {1, 2, 2, 3};
int[] gTo = {2, 3, 4, 4};
int[] gWeights = {2, 1, 2, 3};
int start =1;
int end = 4;
System.out.println(shortestDistance(gNodes, gFrom, gTo, gWeights, start, end));
}
}
It's not giving the expected output which I think is because I can't think of how to use that wExtra. Also, the code is quite messy. Please let me know what's wrong or feel free to provide any robust code that does it well. Thanks.
A possible idea to integrate wExtra is the following:
Duplicate the graph, such that you have two nodes for every input node. The original graph represents the state before introducing the new edge. The copy represents the state after the introduction. For every node n in the original graph, you should then introduce directed edges with weight wExtra to all nodes m in the copy, where the original of m is not adjacent to n. This basically represents the fact that you can introduce a new edge between any two non-adjacent edges. But once you have done this, you cannot go back. Then, run usual Dijkstra on the modified graph between start and either the original end or the copy of end and you should get the correct result.
The best way to visualize this is probably to interpret the two sub graphs as layers. You start at the original layer and want to get to one of the two end nodes (whichever is closer). But you may switch layers only once.

Randomizing set of duplicate arrays in Java without repeating elements

In my problem I have few arrays with numbers 1 - 3,
[1,2,3], [1,2,3]
I combined the arrays into one full array,
[1,2,3, 1,2,3]
I need to randomize the array each run, so that no element repeats.
For example, this would work
[1, 2, 1, 3, 2, 3]
but this would not.
[1,2,2,3,1,3]
I chose 1,2,3 to simplify it, but my arrays would consist of the numbers 1 - 6. The idea remains the same though. Is there an algorithm or easy method to accomplish this?
This is a heuristic solution for random shuffling not allowing consecutive duplicates. It applies to lists, but it's easy to transfer it to arrays as it does only swapping and no shift operations are required. It seems to work in the majority of cases for lists consisting of millions of elements and various density factors, but always keep in mind that heuristic algorithms may never find a solution. It uses logic from genetic algorithms, with the exception that this version utilizes one individual and selective mutation only (it's easy to convert it to a real genetic algorithm though), but it's simple and works as follows:
If a duplicate is found, try swapping it with a random element after it; if not possible, try swapping it with an element prior to it (or vice versa). The key point here is the random position for exchanging elements, so as to keep a better uniform distribution on random output.
This question has been asked in alternative forms, but I couldn't find an acceptable solution yet. Unfortunately, as most of the proposed answers (except for the "greedy" extensive re-shuffling till we get a match or computing every combination), this solution does not provide a perfect uniform distribution, but seems to minimize some patterns, :( still not possible to remove every pattern, as you see below. Try it and post any comments for potential improvements.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
//Heuristic Non-Consecutive Duplicate (NCD) Shuffler
public class NCDShuffler {
private static Random random = new Random();
//private static int swaps = 0;
public static <T> void shuffle (List<T> list) {
if (list == null || list.size() <= 1) return;
int MAX_RETRIES = 10; //it's heuristic
boolean found;
int retries = 1;
do {
Collections.shuffle(list);
found = true;
for (int i = 0; i < list.size() - 1; i++) {
T cur = list.get(i);
T next = list.get(i + 1);
if (cur.equals(next)) {
//choose between front and back with some probability based on the size of sublists
int r = random.nextInt(list.size());
if ( i < r) {
if (!swapFront(i + 1, next, list, true)) {
found = false;
break;
}
} else {
if (!swapBack(i + 1, next, list, true)) {
found = false;
break;
}
}
}
}
retries++;
} while (retries <= MAX_RETRIES && !found);
}
//try to swap it with an element in a random position after it
private static <T> boolean swapFront(int index, T t, List<T> list, boolean first) {
if (index == list.size() - 1) return first ? swapBack(index, t, list, false) : false;
int n = list.size() - index - 1;
int r = random.nextInt(n) + index + 1;
int counter = 0;
while (counter < n) {
T t2 = list.get(r);
if (!t.equals(t2)) {
Collections.swap(list, index, r);
//swaps++;
return true;
}
r++;
if (r == list.size()) r = index + 1;
counter++;
}
//can't move it front, try back
return first ? swapBack(index, t, list, false) : false;
}
//try to swap it with an element in a random "previous" position
private static <T> boolean swapBack(int index, T t, List<T> list, boolean first) {
if (index <= 1) return first ? swapFront(index, t, list, false) : false;
int n = index - 1;
int r = random.nextInt(n);
int counter = 0;
while (counter < n) {
T t2 = list.get(r);
if (!t.equals(t2) && !hasEqualNeighbours(r, t, list)) {
Collections.swap(list, index, r);
//swaps++;
return true;
}
r++;
if (r == index) r = 0;
counter++;
}
return first ? swapFront(index, t, list, false) : false;
}
//check if an element t can fit in position i
public static <T> boolean hasEqualNeighbours(int i, T t, List<T> list) {
if (list.size() == 1)
return false;
else if (i == 0) {
if (t.equals(list.get(i + 1)))
return true;
return false;
} else {
if (t.equals(list.get(i - 1)) || (t.equals(list.get(i + 1))))
return true;
return false;
}
}
//check if shuffled with no consecutive duplicates
public static <T> boolean isShuffledOK(List<T> list) {
for (int i = 1; i < list.size(); i++) {
if (list.get(i).equals(list.get(i - 1)))
return false;
}
return true;
}
//count consecutive duplicates, the smaller the better; We need ZERO
public static <T> int getFitness(List<T> list) {
int sum = 0;
for (int i = 1; i < list.size(); i++) {
if (list.get(i).equals(list.get(i - 1)))
sum++;
}
return sum;
}
//let's test it
public static void main (String args[]) {
HashMap<Integer, Integer> freq = new HashMap<Integer, Integer>();
//initialise a list
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(1);
list.add(2);
list.add(3);
/*for (int i = 0; i<100000; i++) {
list.add(random.nextInt(10));
}*/
//Try to put each output in the frequency Map
//then check if it's a uniform distribution
Integer hash;
for (int i = 0; i < 10000; i++) {
//shuffle it
shuffle(list);
hash = hash(list);
if (freq.containsKey(hash)) {
freq.put(hash, freq.get(hash) + 1);
} else {
freq.put(hash, 1);
}
}
System.out.println("Unique Outputs: " + freq.size());
System.out.println("EntrySet: " + freq.entrySet());
//System.out.println("Swaps: " + swaps);
//for the last shuffle
System.out.println("Shuffled OK: " + isShuffledOK(list));
System.out.println("Consecutive Duplicates: " + getFitness(list));
}
//test hash
public static int hash (List<Integer> list) {
int h = 0;
for (int i = 0; (i < list.size() && i < 9); i++) {
h += list.get(i) * (int)Math.pow(10, i); //it's reversed, but OK
}
return h;
}
}
This is a sample output; it's easy to understand the issue with the non-uniform distribution.
Unique Outputs: 6
EntrySet: [1312=1867, 3121=1753, 2131=1877, 1321=1365, 1213=1793, 1231=1345]
Shuffled OK: true
Consecutive Duplicates: 0
You could use Collections.shuffle to randomize the list. Do it in a while loop, until the list passes your constraint.
If the arrays are relatively small, it would not be too hard for you just to combine the two arrays, randomize it then check the numbers, and if there are too same numbers just shift one over or just randomize it again.
There's no pre-written algorithm that I know of (which doesn't mean one doesn't exist), but the problem is easy to understand and the implementation is straightforward.
I will offer two suggestions dependent on if you want to build a valid array or if you want to build an array and then check its validity.
1 - Create some collection (Array, ArrayList, etc) that contains all of the possible values that will be included in your final array. Grab one of those values and add it to the array. Store a copy of that value in a variable. Grab another value from the possible values, check that it's not equal to your previous value, and add it to the array if it's valid.
2 - Create an array that contains the number of values you want. Check that item n != item n+1 for all items except the last one. If you fail one of those checks, either generate a new random value for that location or add or subtract some constant from the value at that location. Once you have checked all of the values in this array, you know you have a valid array. Assuming the first and last values can be the same.
The most optimal solution, I can think of, is to count the number of occurrences of each value, logically creating a "pool" for each distinct value.
You then randomly choose a value from any of the pools that are not the value of the previous selection. The random selection is weighted by pool sizes.
If a pool is more than half the size of all remaining values, then you must choose from that pool, in order to prevent repetition at the end.
This way you can produce result fast without any form of retry or backtracking.
Example (using letters as values to clarify difference from counts):
Input: A, B, C, A, B, C
Action Selected Pools(Count)
A(2) B(2) C(2)
Random from all 3 pools A A(1) B(2) C(2)
Random from B+C pools C A(1) B(2) C(1)
Random from A+B pools (1:2 ratio) A A(0) B(2) C(1)
Must choose B (>half) B A(0) B(1) C(1)
Random from A+C, so C C A(0) B(1) C(0)
Must choose B (>half) B A(0) B(0) C(0)
Result: A, C, A, B, C, B

I am trying to get the mode of an input of ten numbers in java

import java.util.*;
public class main {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] quiz = new int[10];
int mean = 0,mode = 0,median,range;
Scanner scan = new Scanner(System.in);
for(int x=0;x<=9;x++){
System.out.print("Enter quiz["+(x+1)+"]:");
quiz[x]= scan.nextInt();
}
Arrays.sort(quiz);
for(int x=0;x<=9;x++){
mean = mean+quiz[x];
}
mean = mean/10;
median = (quiz[4]+quiz[5])/2;
range = quiz[9]-quiz[0];
int[] cntr = new int[10];
for(int x=0;x<=9;x++){
for(int y=0;y<=9;y++){
if (quiz[x]==quiz[y]&&x!=y){
cntr[x]++;
}
}
}
int[] sortcntr = cntr;
int ndx = 0;
Arrays.sort(sortcntr);
for(int z=0;z<=9;z++){
if(cntr[z]==sortcntr[9]){
ndx = z;
}
else
mode=0;
}
mode = quiz[ndx];
System.out.println("Mean: "+mean);
System.out.println("Median: "+median);
System.out.println("Range: "+range);
if(mode==0){
System.out.println("Mode: none");
}
else
System.out.println("Mode: "+mode);
System.out.print(sortcntr[9]);
System.out.print(cntr[9]);
System.out.println(ndx);
}
}
this is the codes that i used everything is right except for the mode. the mode variable there always returns the highest number from the number. the latter part was just for debugging and not for use. please help
The main problem of your code is that you obviously think that the line
int[] sortcntr = cntr;
creates a copy of the array cntr. However, arrays have reference semantics in Java. Thus, you simply create a second reference to the same array. If you then sort sortcntr, it applies to cntr as well since it's the same array.
To create a copy of the array:
int[] sortcntr = new int[ cntr.length ];
System.arraycopy(cntr, 0, sortcntr, 0, cntr.length);
BTW: Wouldn't it make more sense to work with floating-point numbers (double) instead of integer numbers?
for(int x=0;x<=9;x++){
for(int y=0;y<=9;y++){
The inner loop should start at x+1, otherwise you count everything twice.
Just to help you out, if you decide to more generify (As Raffaele said) the process of getting the mode of a given set of data, here is a method I developed a while ago which will even return multiple modes if there are more than one with the same occurrence. (Uses the Java 8 Stream API)
/**
* Computes the mode of the passed integers.
*
* #param args Numbers to find the mode of.
* #return Mode of the passed numbers.
*/
public static int[] mode(int... args) {
/* Create a map of integers to their frequencies */
Map<Integer, Integer> frequencies = IntStream.of(args).collect(
HashMap::new,//Indicated that this collector will result in a HashMap
(integerIntegerMap, value) -> integerIntegerMap.merge(value, 1, Maths::sum), //For each value in the arguments added, merge it with the current map and add the frequencies
(integerIntegerMap, integerIntegerMap2) -> integerIntegerMap.putAll(integerIntegerMap2) //While this is not used, it simply combines 2 HashMaps. (I think this is only used when in parallel)
);
//Here we get the maximum number of occurrences for any number, we could return the mode here; but there could be multiple modes
int maxOccurrences = frequencies.entrySet().stream().mapToInt(Map.Entry::getValue).max().getAsInt();
//Here we simply go through the entry set again, filtering out only the numbers with a frequency equal to the max, then returning them as an array
return frequencies.entrySet().stream().filter(entry -> entry.getValue() == maxOccurrences).mapToInt(Map.Entry::getKey).toArray();
}
-Thomas
Since the input is already sorted to compute range and median, you can use the following code to get the mode after a single loop and without any extra memory (live on ideone):
// this must be sorted
int[] values = {1, 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 8};
int mode = values[0];
int modeOccurrences = 1;
int occurrences = 1;
int current = values[0];
for (int i = 1; i < values.length; i++) {
int value = values[i];
if (value == current) {
occurrences++;
} else {
if (occurrences > modeOccurrences) {
mode = current;
modeOccurrences = occurrences;
}
occurrences = 1;
current = value;
}
}
if (occurrences > modeOccurrences) {
mode = current;
modeOccurrences = occurrences;
}
You can even generify this piece of code to work with plain objects instead of numerical types, provided modes can be sorted and compared (I used enums in my demo)

Determine if array contains two elements which equal a certain sum?

// Checks whether the array contains two elements whose sum is s.
// Input: A list of numbers and an integer s
// Output: return True if the answer is yes, else return False
public static boolean calvalue (int[] numbers, int s){
for (int i=0; i< numbers.length; i++){
for (int j=i+1; j<numbers.length;j++){
if (numbers[i] < s){
if (numbers[i]+numbers[j] == s){
return true;
}
}
}
}
return false;
}
This can be achieved in O(n).
Create a hash-backed set out of your list, such that it contains all elements of the list. This takes O(n).
Walk through each element n of your list, calculate s-n = d, and check for the presence of d in the set. If d is present, then n+d = s, so return true. If you pass through the list without finding an appropriate d, return false. This is achieved in a single pass through your list, with each lookup taking O(1), so this step also takes O(n).
Both the solutions mentioned in other answers to this post, and a few other answers as well (eg using a bitmap instead of a hash-table), appear in the following duplicates and slight variations of the question:
• Find two elements in an array that sum to k,
• Find a pair of elements from an array whose sum equals a given number,
• Determine whether or not there exist two elements in set s whose sum is exactly,
• Checking if 2 numbers of array add up to i,
• Find pair of numbers in array that add to given sum,
• Design an algorithm to find all pairs of integers within an array which sum to a,
• Given an unsorted array find any two elements in the array whose sum is equal t,
• A recursive algorithm to find two integers in an array that sums to a given inte,
• Find 2 numbers in an unsorted array equal to a given sum,
• Find two elements so sum is equal to given value,
• and, per google, many more.
You can solve this by sorting the array, then keep 2 pointers to the start and the end of the array and find the 2 numbers by moving both pointers. The sorting step takes O(nlog n) and the 2nd step takes O(n).
As #Adam has pointed out, it is also good to remove duplicate elements from the array, so that you may reduce the time from the second step if the array contains many duplicated numbers.
As for how to do the second step:
Move the pointer at the end backward if sum of the current 2 numbers is larger than n.
Move the pointer at the start forward if sum of the current 2 numbers is smaller than n.
Stop and reject when both pointers point to the same element. Accept if sum is equal to n.
Why is this correct (I use right end to denote larger end and left end to denote smaller end):
If sum is larger than n, there is no point in using the right end, since all numbers larger than current left end will make it worse.
If sum is smaller than n, there is no point in using the left end, since all numbers smaller than current right end will make it worse.
At each step, we will have gone through all possible combinations (logically) between the removed numbers and the numbers which remain. At the end, we will exhaust all possible combinations possible between all pairs of numbers.
Here is a solution witch takes into account duplicate entries. It is written in javascript and assumes array is sorted.
var count_pairs = function(_arr,x) {
if(!x) x = 0;
var pairs = 0;
var i = 0;
var k = _arr.length-1;
if((k+1)<2) return pairs;
var halfX = x/2;
while(i<k) {
var curK = _arr[k];
var curI = _arr[i];
var pairsThisLoop = 0;
if(curK+curI==x) {
// if midpoint and equal find combinations
if(curK==curI) {
var comb = 1;
while(--k>=i) pairs+=(comb++);
break;
}
// count pair and k duplicates
pairsThisLoop++;
while(_arr[--k]==curK) pairsThisLoop++;
// add k side pairs to running total for every i side pair found
pairs+=pairsThisLoop;
while(_arr[++i]==curI) pairs+=pairsThisLoop;
} else {
// if we are at a mid point
if(curK==curI) break;
var distK = Math.abs(halfX-curK);
var distI = Math.abs(halfX-curI);
if(distI > distK) while(_arr[++i]==curI);
else while(_arr[--k]==curK);
}
}
return pairs;
}
Enjoy!
In Java
private static boolean find(int[] nums, long k, int[] ids) {
// walk from both sides towards center.
// index[0] keep left side index, index[1] keep right side index,
// runtime O(N)
int l = ids[0];
int r = ids[1];
if (l == r) {
ids[0] = -1;
ids[1] = -1;
return false;
}
if (nums[l] + nums[r] == k) {
ids[0]++;
ids[1]++;
return true;
}
if (nums[l] + nums[r] < k) {
ids[0]++;
} else {
ids[1]--;
}
return find(nums, k, ids);
}
public static boolean twoSum(final int[] nums, int target) {
// Arrays.sort(nums); //if the nums is not sorted, then sorted it firstly, thus the running time will be O(NlogN)
int[] ids = new int[2];
ids[0] = 0;
ids[1] = nums.length - 1;
return find(nums, target, ids);
}
Test
#Test(timeout = 10L, expected = Test.None.class)
public void test() {
Assert.assertEquals( twoSum(new int[]{3, 2, 4}, 6), true);
Assert.assertEquals( twoSum(new int[]{3, 2, 4}, 8), false);
}
IF only answer is not enough, and want to know which one is i and j that the A[i]+A[j]=target
with the idea of #cheeken as following, if there are duplicated number, take the the one appears firstly.
public static int[] twoSum2(final int[] nums, int target) {
int[] r = new int[2];
r[0] = -1;
r[1] = -1;
Set<Integer> set = new HashSet<>(nums.length);
Map<Integer, List<Integer>> map = new HashMap<>(nums.length);
for (int i = 0; i < nums.length; i++) {
int v = nums[i];
if (set.contains(target - v)) {
r[0] = map.get(target - v).get(0);
r[1] = i;
return r;
}
set.add(v);
List ids = map.get(v);
if (ids == null) {
ids = new LinkedList<>();
ids.add(i);
map.put(v, ids);
} else {
ids.add(i);
map.put(v, ids);
}
}
return r;
}
Test
int[] r = twoSum2(new int[]{3, 2, 4}, 6);
Assert.assertEquals(r[0], 1);
Assert.assertEquals(r[1], 2);
r = twoSum2(new int[]{3, 2, 4}, 8);
Assert.assertEquals(r[0], r[1]);
Assert.assertEquals(r[1], -1);

Categories

Resources