-1

I am solving a array question.

Given a fixed-length integer array arr, duplicate each occurrence of zero, shifting the remaining elements to the right.

Note that elements beyond the length of the original array are not written. Do the above modifications to the input array in place and do not return anything.

Example 1:

Input: arr = [1,0,2,3,0,4,5,0]
Output: [1,0,0,2,3,0,0,4]

Example 2:

Input: arr = [1,2,3]
Output: [1,2,3]

I tried this solution but I am getting wrong output

class Solution {
    fun duplicateZeros(arr: IntArray): Unit {
        arr.forEachIndexed { index, value ->
            if(value == 0 && ((arr.size - 1) != index)) {
                arr[index + 1] = 0
            }
        }
    }
}

Actual Output

[1,0,0,0,0,0,0,0]

Expected Output

[1,0,0,2,3,0,0,4]

Note please don't make copy of array. I want to solve in same array. Can someone guide me where is my logic is wrong and how can I fix the problem?

Thanks

UPDATE

I tried the code and some test case is working fine and long one is not working correctly..

fun main() {
//    val nums = intArrayOf(1, 0, 2, 3, 0, 4, 5, 0)
//    val nums = intArrayOf(0, 0, 0, 0, 0, 0, 0)
//    val nums = intArrayOf(8, 4, 5, 0, 0, 0, 0, 7)
    val nums = longTestCase()
    var extraSpace = 0
    var temp = nums.lastIndex
    for (i in nums.indices) {
        if (nums[i] == 0) {
            if (i < nums.lastIndex - extraSpace) {
                extraSpace++
            }
        }
    }
    while (temp >= 0) {
        if (nums[temp - extraSpace] == 0 && extraSpace > 0) {
            nums[temp] = 0
            extraSpace--
            temp--
            if (temp >= 0) {
                nums[temp] = 0
            }
        } else {
            nums[temp] = nums[temp - extraSpace]
        }
        temp--
    }
    nums.forEach { print("$it ") }
}


private fun longTestCase(): IntArray {
    return intArrayOf(
        9,
        9,
        9,
        4,
        8,
        0,
        0,
        3,
        7,
        2,
        0,
        0,
        0,
        0,
        9,
        1,
        0,
        0,
        1,
        1,
        0,
        5,
        6,
        3,
        1,
        6,
        0,
        0,
        2,
        3,
        4,
        7,
        0,
        3,
        9,
        3,
        6,
        5,
        8,
        9,
        1,
        1,
        3,
        2,
        0,
        0,
        7,
        3,
        3,
        0,
        5,
        7,
        0,
        8,
        1,
        9,
        6,
        3,
        0,
        8,
        8,
        8,
        8,
        0,
        0,
        5,
        0,
        0,
        0,
        3,
        7,
        7,
        7,
        7,
        5,
        1,
        0,
        0,
        8,
        0,
        0
    )
}

Actual Output

[9,9,9,4,8,0,0,0,3,7,2,0,0,0,0,0,0,0,0,9,1,0,0,0,0,1,1,0,0,5,6,3,1,6,0,0,0,0,2,3,4,7,0,0,3,9,3,6,5,8,9,1,1,3,2,0,0,0,0,7,3,3,0,0,5,7,0,0,8,1,9,6,3,0,0,8,8,8,8,0,0]

Expected output

[9,9,9,4,8,0,0,0,0,3,7,2,0,0,0,0,0,0,0,0,9,1,0,0,0,0,1,1,0,0,5,6,3,1,6,0,0,0,0,2,3,4,7,0,0,3,9,3,6,5,8,9,1,1,3,2,0,0,0,0,7,3,3,0,0,5,7,0,0,8,1,9,6,3,0,0,8,8,8,8,0]
Kotlin Learner
  • 3,995
  • 6
  • 47
  • 127
  • 3
    This seems like a good time to learn how to [*debug*](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) your programs. For example by stepping through it statement by statement in a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) while monitoring variables and their values. – Some programmer dude Jul 28 '22 at 20:32
  • At `arr[0]`, the first value, your loop sees a `1` - it does nothing. At `arr[1]`, the second value, it sees a `0`, and sets `arr[2]` to `0` - overwriting the existing `2`. Next it goes to `arr[2]` (the one it just overwrote), sees a `0`, and then overwrites `arr[3]` (a `3`) with `0`. Your loop doesn't insert a new element, it overwrites everything with `0` after the first `0` it sees. – aSemy Jul 28 '22 at 20:44
  • *"please don't make copy of array"* Why not? Using [a collection builder](https://kotlinlang.org/docs/constructing-collections.html#create-with-collection-builder-functions) would make the logic much more clear. There's a reason why Kotlin encourages immutable data structures - they're usually less buggy. – aSemy Jul 28 '22 at 20:47
  • so how can we swift the next element when inserting 0 at that place? – Kotlin Learner Jul 28 '22 at 20:51
  • 1
    On the first occurrence of 0, you’re writing 0 to the next index you’re going to read while iterating, so it will just push zeroes all the way down. You will need to read the next value before overwriting it. – Tenfour04 Jul 28 '22 at 20:53
  • Think of it this way - you don't want to *overwrite* the next value, you want to *push* all the remaining values forward one space, to make room for your extra zero. You also don't want to duplicate that newly added zero (otherwise each duplication will cause a duplication and you'll just fill the array with them) – cactustictacs Jul 28 '22 at 22:40
  • This task isn't that easy to solve using just a single array. It is doable, but it is probably not the best choice for someone who is still learning. I suggest to start by doing this using two arrays. Also, pick a pen a paper and try to analyze how this transformation behaves, where exactly each item ends. – broot Jul 28 '22 at 23:15
  • Sure i'll try not to duplicate the value and try to shift the values. Thank you guys all – Kotlin Learner Jul 29 '22 at 07:39
  • Oh wow, you got the idea for the linear solution, congratulations :-) I won't verify it for correctness, but yes, this is a very good approach. – broot Jul 30 '22 at 15:31
  • @broot I really appreciate it your kind words :).. – Kotlin Learner Aug 05 '22 at 20:41
  • @Tenfour04 Can you help me on this long test case failure issue? – Kotlin Learner Aug 05 '22 at 20:42
  • @RoarS. I am getting problem in my long test case. – Kotlin Learner Aug 05 '22 at 20:42
  • @vivekmodi: I ran a few tests with your code, and there seems to be issues. It also fails with `1 0 1 0 1`. I made my own version of your code that passes all tests so far, hope this will help finding the bug in your code. Code is in below answer. Good luck! BR – Roar S. Aug 06 '22 at 11:31
  • @vivekmodi; I guess I'm not the only one looking forward to see your final solution. Please share when you get there. BR – Roar S. Aug 11 '22 at 21:33
  • 1
    @RoarS. Your answer is best I found. – Kotlin Learner Aug 12 '22 at 19:14

2 Answers2

1

Because OP found a bug in own code in the question, I made a rewrite of that code with some explanation. Code is using same type of logic, which is copying directly to final position in array opposed to e.g shuffling data one position at a time.

Code is tested with initial test dataset and latest long dataset provided by OP in the question, and some misc other input/expected pairs. Code is written inside a Kotest.

given("do/while approach") {

    forAll(
        row(arrayOf(1, 0, 1, 0, 1), arrayOf(1, 0, 0, 1, 0)),
        row(arrayOf(1, 5, 2, 0, 6, 8, 0, 6, 0), arrayOf(1, 5, 2, 0, 0, 6, 8, 0, 0)),
        row(arrayOf(1, 0, 2, 3, 0, 4, 5, 0), arrayOf(1, 0, 0, 2, 3, 0, 0, 4)),
    ) { nums, expected ->

        `when`("duplicating") {

            val zeroIndexesToDuplicate = mutableSetOf<Int>()
            var index = 0

            /** find index positions of zeroes to be duplicated */
            /** keep collecting until no more space in rearranged array */
            do {
                if (nums[index] == 0) {
                    zeroIndexesToDuplicate.add(index)
                }
            } while (++index + zeroIndexesToDuplicate.size < nums.lastIndex)

            /** set insertIndex to end of array */
            var insertIndex = nums.lastIndex

            /** set fetchIndex to first item to fetch from */
            var fetchIndex = insertIndex - zeroIndexesToDuplicate.size

            /** when insertIndex catches up with fetchIndex, no more copying is required */
            /** if no zeros, or just a trailing zero, loop is not entered */
            while (fetchIndex in 0 until insertIndex) {
                nums[insertIndex] = nums[fetchIndex]

                /** if copied item is zero to be duplicated, insert an extra zero */
                if (fetchIndex in zeroIndexesToDuplicate) {
                    nums[--insertIndex] = 0
                }

                /** decrement both indexes */
                fetchIndex--
                insertIndex--
            }

            then("result should be as expected") {
                nums shouldBe expected
            }
        }
    }
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Roar S.
  • 8,103
  • 1
  • 15
  • 37
  • 1
    in this testcase `[1,5,2,0,6,8,0,6,0]` your code output is `[1,1,5,2,0,0,6,8,0]` and expected output is `[1,5,2,0,0,6,8,0,0]`.. :( – Kotlin Learner Aug 07 '22 at 10:30
  • @vivekmodi Nice finding! It shows that testing is a must. We'll actually need to know exactly which zeros to duplicate, not only how many. I updated my answer with this bug-fix and more test cases. BR – Roar S. Aug 07 '22 at 14:44
0

I would go from right to left, and shift everything right by one step every time I encounter a zero.

Not making copies makes a bit more complicated than needed. If it were a real-world case I would have gone with building a list (copy every item once, and all zero's twice, then take initial list's size elements from newly built list) but I see you are doing this as an excercise

class Solution {
    fun duplicateZeros(arr: IntArray): Unit {
        arr.indices
            .reversed()  // because going from left to right will get you endless zero's as soon as you find a zero
            .drop(1) // because the last item will never move to the right, saves logic in [shiftRightFrom]
            .forEach {
                if (arr[it] == 0)
                    arr.shiftRightFrom(it)
        }
    }

    private fun IntArray.shiftRightFrom(index: Int) {
        ((lastIndex) downTo index + 1).forEach {
            this[it] = this[it - 1] // replace value by it's left neighbour
        }
    }
}
Joozd
  • 501
  • 2
  • 14