ArchibaldRajs
ArchibaldRajs

Reputation: 45

Most efficient way to remove consectuive elements from an array? Java

I am trying to solve this problem in the most efficient way.

Given an array of integers, keep removing three consecutive same integers until there are no more three consecutive same elements in an array and return number of times those elements appeared.

For example int[] {4,4,7,7,6,6,6,7,4} will return 3. When we remove consecutive 6's int array becomes {4,4,7,7,7,4} in the next iteration, consecutive 7's are removed and we're left with {4,4,4} and so on.

int[] {3,4,4,5,5,3} would return 0.

I've been trying a dozen of times using different collections but i have been said that my code requires to many operations and is too slow. So i wanted to use arrays only but i got stuck. Here is my code:

    public static int getNumberOfRepetitions(int[] arr) {

    int[] arr2 = new int[arr.length];
    int counter= 0;
    int index = 0;

    for (int x= 0;x<arr.length;x++) {
        if (x < arr.length - 2) {
            if (arr[x] == arr[x + 2] && arr[x] == arr[x + 2]) {
                counter++;
                x = x + 2;
                continue;
            }
            arr2[index] = arr[x];
            index++;
        }
        if (x < arr.length - counter * 3) {
            arr = arr2;
            arr2 = new int[arr.length];
            index = 0;
            x = -1;
        }
    }
    return counter;
}

The algorithm iterates over an array and adds the items to the second array, if there are consecutive elements, it adds to a counter and skips over them. On the last iteration, the arr is set to arr2 and for loop should reset.

So far am i unable to figure out how to terminate the code so i got an infinite loop. I am new to programming and would appreciate any help.

Upvotes: 3

Views: 1932

Answers (3)

גלעד ברקן
גלעד ברקן

Reputation: 23945

Here's an attempt at a recursive approach. This is similar to array flattening or finding matching parentheticals, with the complication that we can have up to three elements to tie together across removed sections.

Very lightly tested JavaScript code (counterexamples/bugs welcome):

// Returns [count, leftmostIndex]
function f(A, i=0, same=1){
  // Base case, end of list
  if (i > A.length - 1)
    return [0, A.length];
  
  // We can remove this block
  if (A[i] == A[i+1] && same == 2){
    let [count, j] = f(A, i + 2, 1);
    return [count + 1, j];
  }
    
  // Same but the count is less than
  // three, keep accumulating
  if (A[i] == A[i+1])
    return f(A, i + 1, same + 1);
  
  // Next element is different,
  // see if the next section has
  // collapsed blocks
  let [count, j] = f(A, i + 1, 1);
  
  // There is no available element
  // to try and accumulate
  if (j > A.length - 1)
    return [count, i];
    
  // The next available element
  // is the same
  if (A[i] == A[j]){
    // We've reached a count of three,
    // remove this block
    if (same == 2){
      return [count + 1, j + 1];

    // Haven't reached a count of
    // three, try one more section
    } else {
      let [count2, j2] = f(A, j + 1, 1);
      if (A[i] == A[j2])
        return [1 + count + count2, j2 + 1];
      else
        return [count + count2, i];
    }
        
  // The next available element is
  // different
  } else {
    return [count, i];
  }
}

var As = [
  [4,4,7,7,6,6,6,7,4],
  [5,7,7,7,8,4,4,5,5,6,6,6,6,6,6,5,4]
];

for (let A of As){
  console.log(JSON.stringify(A));
  console.log(JSON.stringify(A.map((_, i) => i)));
  console.log(JSON.stringify(f(A)));
}

Upvotes: 0

Harshal Parekh
Harshal Parekh

Reputation: 6017

There are several things wrong in your logic. Instead of pointing each of them out, here is a new approach:

private static int getNumberOfRepetitions(int[] arr) {
    int[] arr2 = new int[arr.length];
    int counter= 0;
    int j = 0;
    for (int i : arr) {
        arr2[j++] = i;
        if (j > 2) {
            if (arr2[j - 1] == arr2[j - 2] && arr2[j - 1] == arr2[j - 3]) {
                counter++;
                j -= 3;
            }
        }
    }
    return counter;
}

Here, we add elements to a new array one by one and maintain an index for that array. When there are 3 elements in the new array, compare the last 3. If they are equal, decrement the index (‘j’) by 3.

Upvotes: 4

Andreas
Andreas

Reputation: 159135

I have been told that my code requires too many operations and is too slow.

That is correct, because you can actually do this without ever modifying the array.

Since this is an assignment for you to complete, I'm just going to show you how to do that, without writing any code.

  1. Initialize counter to 0, and start iterating from the beginning of the array.

  2. Locate the next (first) triplet.
    If not found, you're done, so return the counter value.

          start
            │  end
            │   │
    4,4,7,7,6,6,6,7,4
    
  3. You found a triplet to remove, so increment the counter by 1.

  4. Compare the surrounding values. First compare the values right before and after.
    If different, skip to step 7.

          start
            │  end
            │   │
    4,4,7,7,6,6,6,7,4
          └───────┘ are they equal?
    
  5. When surrounding values are equal, there are two possibilities for a cascade triplet, either an extra value before, or an extra value after.
    If the extra value before/after are both different from surrounding values, skip to step 7.

          start                                start
            │  end                               │  end
            │   │                    O R         │   │
    4,4,7,7,6,6,6,7,4                        4,7,6,6,6,7,7,4,4
        └─┴───────┘ are they equal?            └───────┴─┘ are they equal?
    
  6. Expand the sequence to remove, and go back to step 3 to repeat the cascade search.

      start                       start
        │        end                │        end
        │         │       O R       │         │
    4,4,7,7,6,6,6,7,4             4,7,6,6,6,7,7,4,4
    
      start                       start
        │        end                │        end
        │         │       O R       │         │
    4,4,7,7,6,6,6,7,4             4,7,6,6,6,7,7,4,4
    └─┴─────────────┘             └─────────────┴─┘   are they equal?
    
  7. Now we need to look for a more complicated disjoint triplet.

    12344433255666527
     ││└┴┘││││││││││   simple triplet found (step 2-3)
     │└───┴┘││││││││   surrounding triplet found (step 4-6)
     │      │││└┴┘││   another simple triplet found
     │      │└┴───┘│   surrounding triplet found
     └──────┴──────┘   disjoint triplet found
    

    To do that, we need to keep track of a previous sequence removed.

      ┌ prevStart
      │    ┌ prevEnd
      │    │ ┌ start
      │    │ │    ┌ end
    12344433255666527
     └──────┴──────┘   are they equal?
    

    If the previous end is 2 positions before the new start, and the 3 values before, between, and after are the equal, then we found a disjoint triplet. Expand the sequence-to-remove to include both previous sequence, current sequence, and new triplet, then go back to step 3 to repeat the cascade search.

  8. You have now found a section to remove, and incremented the counter the appropriate number of times, so starting after the end position, go back to step 2 to search for the next triplet.

As you can see, the arrays are never modified, we're just working with index values into the array. This requires more code than simply modifying the array and trying again, but the new code runs much faster, because we don't have to copy array elements around.

Good luck code that. 🙂

Upvotes: 3

Related Questions