Java: Convert int[] to smallest representation as ranges - java

Given an array of int values, how could one parse the series into counting sequence notation?
Examples:
{1, 2, 3, 4, 5, 9, 13, 14, 15} -> "1-5,9,13-15"
{4, 6, 8, 10, 11, 12, 15, 17} -> "4,6,8,10-12,15,17"
I'm looking for a method that would produce these results. This is what I have so far, but I'm very much stumped at this point:
Test Code:
import java.util.Arrays;
public class TestSequencing {
public static void main(String[] args) {
int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
String numbers1s = "1-5,9,13-15";
System.out.println(Arrays.toString(numbers1));
System.out.println("Expected:\t" + numbers1s);
System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");
int[] numbers2 = {3, 5, 6, 9, 12};
String numbers2s = "3,5-6,9,12";
System.out.println(Arrays.toString(numbers2));
System.out.println("Expected:\t" + numbers2s);
System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");
int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
String numbers3s = "1-7";
System.out.println(Arrays.toString(numbers3));
System.out.println("Expected:\t" + numbers3s);
System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
}
public static String sequenceNums(int[] nums) {
StringBuilder sb = new StringBuilder();
int rangeStart = nums[0];
int previous = nums[0];
int current;
int expected = previous + 1;
for (int i = 1 ; i < nums.length ; i++) {
current = nums[i];
expected = previous + 1;
if (current != expected || i == (nums.length - 1)) {
if (current == rangeStart) {
sb.append(previous + ",");
} else {
sb.append(rangeStart + "-" + previous + ",");
}
rangeStart = current;
}
previous = current;
}
if (sb.charAt(sb.length() - 1) == ',') {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
Output:
[1, 2, 3, 4, 5, 9, 13, 14, 15]
Expected: 1-5,9,13-15
Produced: 1-5,9-9,13-14
[3, 5, 6, 9, 12]
Expected: 3,5-6,9,12
Produced: 3-3,5-6,9-9
[1, 2, 3, 4, 5, 6, 7]
Expected: 1-7
Produced: 1-6

Try this:
private static void appendRange(StringBuilder sb, int begin, int end) {
sb.append(",").append(begin);
if (end != begin)
sb.append("-").append(end);
}
public static String sequenceNums(int[] nums) {
StringBuilder sb = new StringBuilder();
if (nums.length == 0) return sb.toString();
int begin = nums[0], end = nums[0];
for (int cur : nums)
if (cur - end <= 1)
end = cur;
else {
appendRange(sb, begin, end);
begin = end = cur;
}
appendRange(sb, begin, end);
return sb.substring(1);
}
#Test
public void testSequenceNums() {
assertEquals("1-5,9,13-15", sequenceNums(new int[] {1, 2, 3, 4, 5, 9, 13, 14, 15}));
assertEquals("4,6,8,10-12,15,17", sequenceNums(new int[] {4, 6, 8, 10, 11, 12, 15, 17}));
assertEquals("1-7", sequenceNums(new int[] {1, 2, 3, 4, 5, 6, 7}));
assertEquals("", sequenceNums(new int[] {}));
}

In the for loop you have two issues:
1) The second if should be if (previous == rangeStart) {
2) You're not dealing with the last number in the loop (where i == (nums.length - 1)).
I would do this with the following code:
public static String sequenceNums(int[] nums) {
StringBuilder sb = new StringBuilder();
int rangeStart = nums[0];
int previous = nums[0];
int current;
int expected = previous + 1;
int size = nums.length;
for (int i = 1 ; i < size ; i++) {
current = nums[i];
expected = previous + 1;
if (current != expected) {
addRange(sb, rangeStart, previous);
rangeStart = current;
}
previous = current;
}
addRange(sb, rangeStart, nums[size - 1]);
return sb.toString();
}
private void addRange(StringBuilder sb, int from, int to) {
if (sb.length() > 0) {
sb.append(",");
}
if (from == to) {
sb.append(from);
} else {
sb.append(from + "-" + to);
}
}

Here is your fixed code.
public class TestSequencing {
public static void main(String[] args) {
int[] numbers1 = {1, 2, 3, 4, 5, 9, 13, 14, 15};
String numbers1s = "1-5,9,13-15";
System.out.println(Arrays.toString(numbers1));
System.out.println("Expected:\t" + numbers1s);
System.out.println("Produced:\t" + sequenceNums(numbers1) + "\n");
int[] numbers2 = {3, 5, 6, 9, 12};
String numbers2s = "3,5-6,9,12";
System.out.println(Arrays.toString(numbers2));
System.out.println("Expected:\t" + numbers2s);
System.out.println("Produced:\t" + sequenceNums(numbers2) + "\n");
int[] numbers3 = {1, 2, 3, 4, 5, 6, 7};
String numbers3s = "1-7";
System.out.println(Arrays.toString(numbers3));
System.out.println("Expected:\t" + numbers3s);
System.out.println("Produced:\t" + sequenceNums(numbers3) + "\n");
}
public static String sequenceNums(int[] nums) {
StringBuilder sb = new StringBuilder();
int rangeStart = nums[0];
int previous = nums[0];
int current;
int expected = previous + 1;
for (int i = 1 ; i < nums.length ; i++) {
current = nums[i];
expected = previous + 1;
if (current != expected || i == (nums.length - 1)) {
if (current == rangeStart) {
sb.append(previous + ",");
} else {
if(rangeStart != previous) {
if(i == nums.length - 1)
sb.append(rangeStart + "-" + current);
else
sb.append(rangeStart + "-" + previous + ",");
} else {
if(i == nums.length - 1)
sb.append(rangeStart + "," + current);
else
sb.append(rangeStart + ",");
}
}
rangeStart = current;
}
previous = current;
}
if (sb.charAt(sb.length() - 1) == ',') {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
Problem was, if current is not same as range starting value, you need to check for two cases
i) if range was starting with the same previous value. If so, no need to have same number separated by range (ex: 9-9 doesn't make sense. only 9 does). Another case to be handled is end of array reached. In case end of array is reached, it should just be added at the end even thought it does not fall in any range
ii) other wise, range starts and ends with previous value if end of array is not reached. If end of array is reached that would be end value of range

I took the below approach to represent integer array in ranges.
Note: The numbers should be pre-sorted in ascending order.
We will have two variables start & current, which we will use in the iterations to identify the range. index will be the current index of the array.
Once a range is found, we will keep pushing it into the StringBuilder.
This is the Code:
// We will take this set of integers
int[] temp = new int[] { 0, 1, 4, 5, 8, 9, 11, 12, 13 };
// Helper variables
Integer start = null, current = null;
// The found range(s) will be stored in this variable.
StringBuilder rangeBuilder = new StringBuilder();
// The current index of the array in iteration
int index = 0;
do {
// During the first iteration both start & current will be null. So setting the current index value to them.
if (start == null) {
start = current = temp[index];
} else {
// Checking if the index value is the next number of current.
if (temp[index] == (current + 1)) {
current = temp[index];
} else {
if (start == current) {
rangeBuilder.append(start + ",");
} else {
rangeBuilder.append(start + "-" + current + ",");
}
start = current = temp[index];
}
}
// Checking if we have reached the end of the array.
if (index + 1 == temp.length) {
if (start == current) {
rangeBuilder.append(start);
} else {
rangeBuilder.append(start + "-" + current);
}
}
} while (index++ < temp.length - 1);
// Printing the range.
System.out.println("Range: " + rangeBuilder.toString());
Explanation:
We are taking this example:
{ 0, 1, 4, 5, 8, 9, 11, 12, 13 }
Note: We will represent start -> s & current -> c
Iteration-1:
Entry: start = null, current = null, rangeBuilder = ""
sc
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? No
Has the index reached the last element ? No
Exit: start = 0, current = 0, rangeBuilder = ""
Iteration-2:
Entry: start = 0, current = 0, rangeBuilder = ""
s c
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? Yes. So we move c from 0 -> 1.
Has the index reached the last element ? No
Exit: start = 0, current = 1, rangeBuilder = ""
Iteration-3:
Entry: start = 0, current = 1, rangeBuilder = ""
sc
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.
Has the index reached the last element ? No
Exit: start = 4, current = 4, rangeBuilder = "0-1,"
Iteration-4:
Entry: start = 4, current = 4, rangeBuilder = "0-1,"
s c
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? Yes. So we move c from 4 -> 5.
Has the index reached the last element ? No
Exit: start = 4, current = 5, rangeBuilder = "0-1,"
Iteration-5:
Entry: start = 4, current = 5, rangeBuilder = "0-1,"
sc
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.
Has the index reached the last element ? No
Exit: start = 8, current = 8, rangeBuilder = "0-1,4-5,"
Iteration-6:
Entry: start = 8, current = 8, rangeBuilder = "0-1,4-5,"
s c
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? Yes. So we move c from 8 -> 9.
Has the index reached the last element ? No
Exit: start = 8, current = 9, rangeBuilder = "0-1,4-5,"
Iteration-7:
Entry: start = 8, current = 9, rangeBuilder = "0-1,4-5,"
sc
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? No. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates. Then we move s & c to the index value.
Has the index reached the last element ? No
Exit: start = 11, current = 11, rangeBuilder = "0-1,4-5,8-9,"
Iteration-8:
Entry: start = 11, current = 11, rangeBuilder = "0-1,4-5,8-9,"
s c
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? Yes. So we move c from 11 -> 12.
Has the index reached the last element ? No
Exit: start = 11, current = 12, rangeBuilder = "0-1,4-5,8-9,"
Iteration-9:
Entry: start = 11, current = 12, rangeBuilder = "0-1,4-5,8-9,"
s c
0, 1, 4, 5, 8, 9, 11, 12, 13
^
Is the index value the next number of current ? Yes. So we move c from 12 -> 13.
Has the index reached the last element ? Yes. So we push s and c into the String Builder (rangeBuilder). While pushing, we check if s and c are same to avoid duplicates.
End of iteration. The String Builder (rangerBuilder) will have this value: 0-1,4-5,8-9,11-13
Feel free to improve this code :)

I was able to solve your problem by introducing a boolean flag and reworking the method to test for consecutive numbers. If the current and next numbers are consecutive, inRangeFlag is triggered for the next iteration. See the code comments below for further breakdown:
public static String sequenceNums(int[] nums) {
StringBuilder sb = new StringBuilder();
int current;
int next;
boolean inRangeFlag = false;
for (int i = 0; i < nums.length; i++) {
current = nums[i];
// TRUE: if element is not last element, because last number is
// always appended.
if (i < nums.length - 1) {
next = nums[i + 1];
// TRUE: if current element and next are consecutive
if (next - current == 1) {
// If rangeflag is false, the current number is the start
// of a range. Append the number with hyphen.
if (!inRangeFlag) {
sb.append(current + "-");
}
// Trigger inRange Flag for next iteration.
inRangeFlag = true;
} else {
sb.append(current + ",");
inRangeFlag = false; // Turn flag false because not inRange.
}
} else {
sb.append(current);
}
}
return sb.toString();
}

Related

Small exercise for Java given two arrays which represents a path

Given two arrays which represents a path and each element in the array represent the time it takes the driver to travel, write a method that chooses the fastest path he can take. The driver can switch paths only once between arrays.
For example the following arrays:
int[] road1 = new int[] { 5, 4, 5, 8, 12, 9, 9, 3 };
int[] road2 = new int[] { 7, 3, 3, 12, 10, 2, 10, 7 };
the output should be 49 since the driver will start at road2 and switch at index 6 to the second Array.
Edit:
My question is how do I make the recursion stop after switching to the other array? I tried to put a counter marker but it didn't work and I reverted back to my original output. Am I missing something about how recursion works?
My code prints 53 where it should print 49.
My code:
public class MyClass {
public static int shortestRoad(int[] road1, int[] road2) {
return shortestRoadNumbers(road1, road2, 0);
}
private static int shortestRoadNumbers(int[] road1, int[] road2, int index) {
if (index == road1.length || index == road2.length) {
return 0;
}
if (road1[index] >= road2[index] && road1[index + 2] >= road2[index + 2]) {
return (road2[index] + shortestRoadNumbers(road1, road2, index + 1));
} else {
return (road1[index] + shortestRoadNumbers(road1, road2, index + 1));
}
}
public static void main(String args[]) {
int[] road1 = new int[] { 5, 4, 5, 8, 12, 9, 9, 3 };
int[] road2 = new int[] { 7, 3, 3, 12, 10, 2, 10, 7 };
MyClass.shortestRoad(road1, road2);
int result = MyClass.shortestRoad(road1, road2);
System.out.println(result);
}
}
Let the following schema to illustrate your problem
We have two paths, and each path contain many nodes (values) , we can switch from one path to another just one time. Find the best combination of nodes (values) that minimise the score.
We can distinguish 4 cases:
1- the sum of the values of the first path without switching.
2-the sum of the values of the second path without switching.
3-the sum of the values from the first path until a node i, then switch to path second path from node i+1 (sum from node+1 til the end)
4-the inverse of the point 3.
static int shortestRoad(int road1[], int road2[])
{
// case 1
int bestValue = sumValues(road1,0);
// case 2
int sumValuesRoad2 = sumValues(road2,0);
if ( sumValuesRoad2 < bestValue)
bestValue = sumValuesRoad2;
// case 3: best values of all combination from road 1 to road 2
int bestValuesSwitchFromRoad1ToRoad2 = shortestRoad_Switch_RoadFrom_RoadTo(road1, road2);
if ( bestValuesSwitchFromRoad1ToRoad2 < bestValue)
bestValue = bestValuesSwitchFromRoad1ToRoad2;
// case 4: best values of all combination from road 2 to road 1
int bestValuesSwitchFromRoad2ToRoad1 = shortestRoad_Switch_RoadFrom_RoadTo(road2, road1);
if ( bestValuesSwitchFromRoad2ToRoad1 < bestValue)
bestValue = bestValuesSwitchFromRoad2ToRoad1;
return bestValue;
}
sum the values of a given array from idx til the end:
static int sumValues(int array[], int idx_from)
{
int sum = 0;
for (int i = idx_from; i<array.length; ++i)
sum += array[i];
return sum;
}
case 3 and 4:
static int shortestRoad_Switch_RoadFrom_RoadTo(int[] road_from, int[] road_to)
{
int sumValues_RoadFrom_til_idx = 0;
int sumValues_RoadFrom_idx_switch_RoadTo = 0;
int bestValue = Integer.MAX_VALUE;
for (int i = 0; i<road_from.length-1; ++i)
{
sumValues_RoadFrom_til_idx += road_from[i];
sumValues_RoadFrom_idx_switch_RoadTo = sumValues_RoadFrom_til_idx+sumValues(road_to,i+1);
if(sumValues_RoadFrom_idx_switch_RoadTo < bestValue )
bestValue = sumValues_RoadFrom_idx_switch_RoadTo;
}
return bestValue;
}
Driver code:
public static void main(String[] args)
{
int road1[] = { 5, 4, 5, 8, 12, 9, 9, 3 };
int road2[] = { 7, 3, 3, 12, 10, 2, 10, 7 };
int road_a[] = { 1, 1, 1, 1, 1, 9, 9, 9,9,9 };
int road_b[] = { 9, 9, 9, 9, 9, 1, 1, 1,1,1 };
int road_c[] = { 1, 1, 1, 1, 1, 2 };
int road_d[] = { 9, 9, 9, 9, 9, 1, 1, 1,1,1 };
System.out.println("best road1, road2 = "+shortestRoad(road1,road2));
System.out.println("best road_a, road_b = "+shortestRoad(road_a,road_b));
System.out.println("best road_c, road_d = "+shortestRoad(road_c,road_d));
return 0;
}
Results:
best road1, road2 = 49
best road_a, road_b = 10
best road_c, road_d = 7
ps:
the best path in your example is begin from road2 and then switch to road 1 at i=5 (i begin from 0)
{ 5, 4, 5, 8, 12, 9, -->9, 3 }
{ -->7, 3, 3, 12, 10, 2 /, 10, 7 }
public static int shortestRoad(int[]road1, int[]road2)
{
int sumRoad1Only = 0;
int sumRoad2Only = 0;
for(int i=0; i<road1.length; i++)
{
sumRoad1Only += road1[i];
sumRoad2Only += road2[i];
}
Those sums are for the option that the driver chooses one lane, and doesn't change it until the end. Now, we can find the switch index, for options like starting at one road, and switching to the other. In this specific question I realized that the best point of switch between the arrays - is where the difference between the two collected sums until a certain index is the largest. In your example, it is index 6. That doesn't say that switching a lane is always giving a smaller sum.
int roadIndex1 = road1.length-1;
int roadIndex2 = road2.length-1;
int totalSumRoad1 = sumRoad1Only;
int totalSumRoad2 = sumRoad2Only;
int max = 0;
int indexOfSwitch = 0;
int diff = 0;
while(roadIndex1 >=0 && roadIndex2 >=0)
{
diff = Math.abs(totalSumRoad2 - totalSumRoad1);
if(diff > max)
{
max = diff;
indexOfSwitch = roadIndex1;
}
totalSumRoad1 -= road1[roadIndex1];
totalSumRoad2 -= road2[roadIndex2];
roadIndex1--;
roadIndex2--;
}
If the index of switch is at last index, we shall move it one left, so there be a transition between the arrays.
if(indexOfSwitch == road1.length-1)
{
indexOfSwitch--;
}
now we found the indexOfSwitch, we can calculate the options of starting at road1, and switching exactly once to road2, and vice versa:
int begin1 = 0;
int begin2 = 0;
for(int k = 0; k<=indexOfSwitch; k++)
{
begin1 += road1[k];
begin2 += road2[k];
}
int end1 = sumRoad1Only - begin1;
int end2 = sumRoad2Only - begin2;
int begin1End2 = begin1 + end2;
int begin2End1 = begin2 + end1;
and when we find all the options, we can return the minimum.
return Math.min(Math.min(sumRoad1Only, sumRoad2Only), Math.min(begin1End2, begin2End1));

Find the index of array where corruption begins

What will be the best solution to this question? (Less than O(n))
Given an array of positive integers where successive elements increase by 1
(except for a single element that does NOT increase by one--the start of the
"corruption"), return the index of where the corruption starts.
Example 1:
array: [5, 6, 7, 8, 12, 13] indices: 0 1 2 3 4 5
The corruption starts at index 4.
Example 2:
array: [5, 2, 3, 4, 5, 6] indices: 0 1 2 3 4 5
The corruption starts at index 1.
P.S. My solution was of O(n), also I tried to branch it in two parts still it will reduce half.
Hint: I was told I can use binary search.
Edit:
My solution was simply to iterate the array and see if difference is greater or less than one.
Try something like this
public class Main {
public static void main(String[] args) {
int[] nums = {5, 6, 7, 8, 12, 13};
int res = checkArray(nums, 0, nums.length - 1);
System.out.println("res = " + res);
}
public static int checkArray(int[] nums, int start, int end) {
if (end - start < 2) {
return end;
} else {
int middle = (start + end) / 2;
int a = nums[start];
int b = nums[middle];
if (b - a != middle - start) {
return checkArray(nums, start, middle);
} else {
return checkArray(nums, middle, end);
}
}
}
}
It use the fact that difference between first and last element of subarray is equal to its length if array do not have corruption.
public static void main(String[] args) {
// corruption starts at 13
int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17};
int corruptionIndex = -1;
int start = 0;
int end = arr.length;
while (end - start > 1) {
int middle = (start + end) / 2;
// we add the first element onto our value as an offset
int expectedValue = middle + arr[0];
if (arr[middle] != expectedValue) {
// something has already gone wrong, let's check the first half
end = middle;
}
else {
// so far so good, let's check the second half
start = middle;
}
corruptionIndex = end;
}
System.out.println("Corruption Index: " + corruptionIndex);
}
var arr1 = [5, 9, 7, 8, 9, 13] ;
var arr2 = [5, 2] ;
var arr3 = [5, 6, 7, 8, 9, 13] ;
check(arr1);
check(arr2);
check(arr3);
function check(arr){
for(var i=1;i<arr.length;i++){
if(arr[i]-arr[i-1] !=1 ){
console.log('corroption begins at '+i);
break;
}
}
}
we can check for current and prev element difference , right. if diff is not 1, we need to break. its in js
O(n) is your only option. Binary search is O(log(n)) but that only works for searching for a specific number in a sorted list. You neither have a sorted list nor a specific number you are searching for
class FindCorruptionIndex
{
public static void main(String[] args)
{
int i,j;
int array[]={1,2,3,4,7,8,9};
System.out.print("The array is [ ");
for (int x :array )
{
System.out.print(x+",");
}
System.out.print("\b ] ");
System.out.println();
for(i=0;i<array.length-1;i++)
{
j=array[i+1]-array[i];
if (j>=2)
{
System.out.println("The corruption Index position is "+(i+1));
}
}
}
}

Find groups of continuous integers in a list in Java

I have a list of integers placed in order.
I want to get groups of consecutive integers as arrays with 1st and last integer of each group.
For example, for (2,3,4,5,8,10,11,12,15,16,17,18,25) I want to get a list with those arrays: [2,5] [8,8] [10,12] [15,18] [25,25]
Here is my code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MyRangesTest {
public static void main(String[] args) {
//create list of integers
List<Integer> list=Arrays.asList(2,3,4,5,8,10,11,12,15,16,17,18,25);
System.out.println("list:" + list);
//create a list with integers where a new sequense of consecutive integers starts or ends
List<Integer> sublistsStarsAndEnds= new ArrayList<>();
sublistsStarsAndEnds.add(list.get(0));//1st line (always in sublistsStarsAndEnds list)
for (int i=1; i<list.size()-1; i++){
if (list.get(i)>1+list.get(i-1)){
sublistsStarsAndEnds.add(list.get(i-1));
sublistsStarsAndEnds.add(list.get(i));
}
}
sublistsStarsAndEnds.add(list.get(list.size()-1));//last line (always in sublistsStarsAndEnds list)
System.out.println("sublistsStarsAndEnds: " + sublistsStarsAndEnds);//present the result
//create list with arrays that represents start and end of each subrange of consequent integers
List<Integer[]> ranges= new ArrayList<>();
for (int i=0; i<sublistsStarsAndEnds.size()-1; i=i+2){
Integer[] currentrange=new Integer[2];
currentrange[0]=sublistsStarsAndEnds.get(i);
currentrange[1]=sublistsStarsAndEnds.get(i+1);
ranges.add(currentrange);//present the result
}
//present the result
String rangestxt="";//create result text
for (int i=0; i<ranges.size(); i++){
rangestxt=rangestxt+ranges.get(i)[0]+ " " + ranges.get(i)[1]+ " ";
}
System.out.println("ranges: " + rangestxt);//present the result
}
}
This code works in the general case for what I want but when the last sequence has only 1 integer it fails to get the right result.
For example when using this list: (2,3,4,5,8,10,11,12,15,16,17,18,25) instead of getting the ranges [2,5] [8,8] [10,12] [15,18] [25,25] we get the ranges [2,5] [8,8] [10,12] [15,25].
The problem is with the detection of where the ranges start or end. In my code those places are stored in the sublistsStarsAndEnds list. Here instead of getting [2, 5, 8, 8, 10, 12, 15, 15, 25, 25] we get [2, 5, 8, 8, 10, 12, 15, 25].
I tried to correct the code but I without good results.
Any suggestions please?
P.S. Someone wanted to get the result I want and asked a question for Python here "Identify groups of continuous numbers in a list
But I don't know Python so I tried my own coding.
try this
public static void main(String[] args) {
List<Integer> list=Arrays.asList(2,3,4,5,8,10,11,12,15,16,17,18,19,25);
List<List<Integer>>lList=new ArrayList<List<Integer>>(); //list of list of integer
System.out.println("list:" + list);
int i=0;
int start=0;
List<Integer> sList=new ArrayList<Integer>(2);
for( i = 1; i <list.size();i++){
if( list.get(i - 1) + 1 != list.get(i)){
sList.add(list.get(start));
sList.add(list.get(i-1));
lList.add(sList);
sList=new ArrayList<Integer>(2);
start=i;
}
}
sList.add(list.get(start)); // for last range
sList.add(list.get(list.size()-1));
lList.add(sList);
System.out.println("Range :"+lList);
}
output :
list:[2, 3, 4, 5, 8, 10, 11, 12, 15, 16, 17, 18, 19, 25]
Range :[[2, 5], [8, 8], [10, 12], [15, 19], [25, 25]]
If I understand your question, you could write a POJO class Range like
static class Range {
private int start;
private int end;
Range(int start, int end) {
this.start = start;
this.end = end;
}
#Override
public String toString() {
return String.format("%d - %d", start, end);
}
}
Then your problem becomes adding a start to an end position where the end position is i-1 in list.get(i - 1) + 1 != list.get(i). Something like,
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 4, 5, 8, 10, 11, 12, 15, 16,
17, 18, 25);
System.out.println("list:" + list);
int start = 0;
List<Range> ranges = new ArrayList<>();
for (int i = 1; i < list.size(); i++) {
if (list.get(i - 1) + 1 != list.get(i)) {
ranges.add(new Range(list.get(start), list.get(i - 1)));
start = i;
}
}
ranges.add(new Range(list.get(start), list.get(list.size() - 1)));
System.out.println(ranges);
}
Output is (as requested)
[2 - 5, 8 - 8, 10 - 12, 15 - 18, 25 - 25]
I will point out that this is very nearly Run-length Encoding.
Elegant Solution:
static String pair(int[] array){
String res = "";
int i = 0, j = 1;
//loop through all items in array.
while(i < array.length){
//increase j while array[j] - array[j - 1] equals 1
while(j < array.length && array[j] - array[j - 1] == 1){
j++;
}
//we came out of that loop, no longer in a sequence.
//write to the output.
res += toTuple(i,j - 1, array);
//i now points to j.
//j is now i + 1;
i = j;
j = i + 1;
}
return res;
}
static String toTuple(int low, int high, int[] array){
return "[" + array[low] + "," + array[high] + "]";
}
Sample Input: {1, 2, 3, 6, 7,9,10,11,13,14,15,20}
Output: [1,3][6,7][9,11][13,15][20,20]
Another short answer in kotlin, Assuming no repetition in the list
list.fold(mutableListOf<MutableList<Int>>()) { acc, i ->
acc.also { outer ->
outer.lastOrNull()?.takeIf { it[1] + 1 == i }?.also {
it[1] = i
} ?: mutableListOf(i, i).also {
outer.add(it)
}
}
}
Here's a simple little algorithm that I sometimes adapt and use.
public void printRanges(int[] input) {
if (input.length == 0)
return;
// Only necessary if not already sorted
Arrays.sort(input);
int start = input[0];
int end = input[0];
for (int rev : input) {
if (rev - end > 1) {
// break in range
System.out.println("Range: [" + start + ", " + end + "]");
start = rev;
}
end = rev;
}
System.out.println("Range: [" + start + ", " + end+"]");
}
I'd like to contribute a solution written in kotlin:
#Test
fun test_extract_continuous_range() {
val inputList = listOf(0, 2, 3, 4, 5, 8, 10, 11, 12, 15, 16, 17, 18, 19, 25, 26, 27, 30)
println("Input: $inputList")
val result = mutableListOf<IntRange>()
result.add(inputList.first()..inputList.first()) // add the first item as the first range
inputList.windowed(2)
.map { w -> w.first() to w.second() } // optional map to Pair for convenient
.forEach { p ->
if (p.first + 1 == p.second) {
// same range, extend it
val updatedLastRange = result.last().start..p.second
result[result.lastIndex] = updatedLastRange
} else {
// new range
result.add(p.second..p.second)
}
}
println("Result: $result")
val sizes = result.map(IntRange::count)
println("Sizes: $sizes")
}

Print sequence of numbers without consequtive duplicates?

Given a sequence of numbers like this:
1 2 2 5 5 5 1 7 3 7 7 7
Output should be
1 2 5 1 7 3 7
The current output of my code is
1 2 5 1 7 3
I am unable to fix the problem. Can anyone tell me what should I do or change in my code?
Here's my current code:
public class Q3 {
public static void main(String args[]) {
int [] input=new int[]{1 ,2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int current= input[0];
boolean found=false;
for(int i=0; i< input.length; i++) {
if(current == input[i] && !found) {
found=true;
} else if(current!=input[i]) {
System.out.print(" " + current);
current=input[i];
found=false;
}
}
}
}
The basic problem is, you need to seed the current value with something that is not equal to the first element, this way, you can just loop through the array without issues, flags or other tricks, for example...
int[] input = new int[]{1, 2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int current = -input[0];
for (int i = 0; i < input.length; i++) {
if (current != input[i]) {
current = input[i];
System.out.print(" " + current);
}
}
System.out.println("");
Which outputs...
1 2 5 1 7 3 7
This sets the current value to the negative of the first element (in this -1), so when we do our first comparison, -1 != 1 and then we can proceeded as normal...
You can try in this way
Eg:
int[] input = new int[]{1, 2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int temp =input[0];
boolean isFirst=true;
for (int i = 0; i < input.length; i++) {
if(isFirst){
System.out.print(temp);
isFirst=false;
}
if (temp != input[i]) {
System.out.print(input[i]);
}
temp = input[i];
}
Out put:
1251737
Logic: I am taking only first occurrence of continuous numbers.
You can simplify code a lot, if you don't want to remove all duplicates, only closest one.
public class Q3 {
public static void main(String args[]) {
int [] input=new int[]{1 ,2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int base = input[0];
System.out.print(base);
for (int current : input) {
if (current != base) {
base = current;
System.out.print(" " + base);
}
}
}
}
Output:
1 2 5 1 7 3 7
This is not right to use other's answer without giving the credit
int[] input = new int[]{1, 2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int current = input[0];
boolean found = false;
for (int i = 0; i < input.length; i++) {
if (current == input[i] && !found) {
found = true;
} else if (current != input[i]) {
System.out.print(" " + current);
current = input[i];
found = false;
}
}
System.out.print(" " + current); <--- you forgot this line
output:
1 2 5 1 7 3 7
My answer can be found here
How to efficiently remove duplicates from an array without using Set
int [] input = new int[]{1 ,2, 2, 5, 5, 5, 1, 7, 3, 7, 7, 7};
int previous = 0;
boolean started = false; // loop is not started yet
for (int i = 0; i < input.length; i++)
{
// if the loop is not started, or the number is not equal to previously printed number...
if (!started || input[i] != previous) {
System.out.print(input[i] + " ");
previous = input[i];
}
started = true;
}

Grouping an array of integers that skip numbers in Java

Say I have the following int array:
[1,2,3,6,7, 8, 9,20, 22]
I'd like to be able to display to the user the following based off the above array:
Numbers 1 through 3, 6 through 9, 20, 22
How could I go about doing this? Essentially checking if the number previous equals the current number - 1? Guess I just answered my own question, but any shortcuts/pointers would be welcome.
Note: only dealing with integers, nothing negative (wouldn't be an integer would it).
A live example using Ideone can be found here. Runs in O(n) time.
Integer[] A = {1, 2, 3, 6, 7, 8, 9, 20, 22};
int start = 0, end;
System.out.print("Numbers ");
while((end = start) < A.length){
// Increment the 'end' pointer while consecutive numbers exist.
while(end + 1 < A.length && A[end + 1] == A[end] + 1) end++;
// If end == start: 'A[start]'
// Else : 'A[start] through A[end]'
System.out.print(A[start] +
(end == start ? "":(" through " + A[end])) +
(end < A.length - 1 ? ", ":""));
// Increment start pointer to next element in A
start = end + 1;
}
Not the best looking code, but works:
public static void display(int[] input) {
String out = "Numbers ";
int first = input[0];
int last = -1;
int older = first;
for (int i : input) {
if (i == older + 1) {
last = i;
older++;
} else if (i > older + 1) {
out += last > first ? first + " through " + last + ", " : first + ", ";
first = i;
older = i;
}
if (i == input[input.length - 1]) {
out += last > first ? first + " through " + last : first;
}
}
System.out.println(out);
}
Example:
int[] in1 = { 1, 2, 3, 6, 7, 8, 9, 20, 22 };
int[] in2 = { 1, 2, 3, 6, 7, 8, 9, 20, 21, 22 };
display(in1);
display(in2);
Outputs:
Numbers 1 through 3, 6 through 9, 20, 22
Numbers 1 through 3, 6 through 9, 20 through 22
Try something like:
int[] numberArray = [1, 2, 3, 6, 7, 8, 9, 20, 22];
int rangeStart = -999;
int rangeEnd = -999;
for (int n : numberArray) {
if (n > rangeEnd + 1) {
if (rangeEnd = -999) {
System.out.print("Numbers ");
else if (rangeEnd > rangeStart) {
System.out.printf("%d through %d, ", rangeStart, rangeEnd);
} else {
System.out.printf("%d, ", rangeEnd);
}
rangeStart = rangeEnd = n;
} else {
rangeEnd = n;
}
}
if (rangeEnd > rangeStart) {
System.out.printf("%d through %d, ", rangeStart, rangeEnd);
} else {
System.out.printf("%d,", rangeEnd);
}
Might need some fine-tuning regarding the use of commas and newlines.
Sorry for my terrible formatting, just some psuedocode you can think about.
for(int i = 0; i < length; i++){
start = int[i]
compare int[i] to int[i+1] {
if true, store as end, compare to next in array
else, break out
}
if start != end, print start through end
else, print start
}
**edit: and increment i to be the last + 1

Categories