How do I determine if a graph has a cycle in Java? - java

So what I'm trying to do is implement Kruskal's algorithm from a 2d array (called G) like this:
0 0 0 0 0 12 13 0
0 0 6 0 0 0 0 3
0 6 0 4 0 0 0 5
0 0 4 0 10 0 0 7
0 0 0 10 0 11 8 9
12 0 0 0 11 0 1 0
13 0 0 0 8 1 0 2
0 3 5 7 9 0 2 0
The 0's mean no edge is connecting those two vertices and a value means an edge is connecting two vertices where the value represents the edge's weight and the row and column is the two vertices it's connecting. So 12 would be connecting 0 and 5. So what I decided to do is find the smallest element in the array and then add that to another 2d array (H) that's the same size as G but all the values are 0. Then I check and see if adding that edge forms a cycle and if it does then I remove the edge from H and keep going until every row is filled. Here's what I did:
int numVerts = G.length;
int [][] H = new int[numVerts][numVerts];
int minWeight;
int k = 0;
int l = 0;
int boolcount = 0;
boolean [] R = new boolean[G.length];
Arrays.fill(R, false);
while (boolcount != numVerts){
for (int i = 0; i < numVerts; i++){
for (int j = 0; j < numVerts; j++){
if ((G[i][j] != 0) && (G[i][j] < minWeight)){
minWeight = G[i][j];
k = i;
l = j;
}
}
}
H[k][l] = minWeight;
H[l][k] = minWeight;
G[k][l] = 0;
G[l][k] = 0;
if (/*it forms a cycle*/){
H[k][l] = 0;
H[l][k] = 0;
} else {
//keep the edge
}
So the only thing I need to figure out now is how do I check if an edge forms a cycle with any other edge in H. Based on how the arrays are represented in the 2d array, I could I figure out if the edges form cycles? One way I was thinking is using union-find to say if the newly added edge is in the union as the other edges then don't add it because it creates a cycle but I'm having trouble implementing that.
P.S: I also didn't post the code for when I decide to keep the edge but basically that's where I update the boolcount for when I determine when to stop traversing.

Pick a starting vertex. Traverse the graph breadth first while negating the edge weights as you pass them. If you come across a an edge whose weight is <0, then you have a cyclic graph.

Related

Algorithm not showing right output

The Question was:
You are given a binary matrix (i.e. each element of matrix is either 0 or 1) of size n × n. You want to re-arrange 1's in such a way that they form a rectangular region. Note that the rectangular region should be made of only 1's, and all the 1's of the entire matrix should be in this rectangular region.
For achieving rectangular region, you can swap any two elements of the matrix. Please find out the minimum number of swaps needed. If it is not possible to re-arrange 1's in the desired way, please print -1.
Input
First line of the input contains a single integer T denoting number of test cases.
Description of T test cases follows.
First line of each test case will contain a single integer n denoting dimension of matrix.
Each of next n lines will contain n space separated integers denoting the ith row of the matrix.
Output
For each test case, print a single line containing a single integer denoting minimum number of swaps needed or -1 depending on the situation.
Example
Input:
2
2
0 1
1 0
2
1 1
1 0
Output:
1
-1
Explanation
Example case 1. You can swap 1 of second row first column with 0 of first row first column.
After the swap, matrix will look as follows.
1 1
0 0
Here all the 1's form a rectangular region of dimension 1 × 2. In this case, 1 swap will be needed.
Note that you can also swap 1 at first row second column with 0 at second row second column too.
Matrix after this swap will be following.
0 0
1 1
So you need 1 swap in this case too.
So overall, you need 1 swap.
Example case 2. There is no way to create a rectangular region containing 3 1's in a matrix of dimension 2 × 2, hence answer is -1.
My Algorithm [Edit]
First i am Taking Number of Cases from user
Then the order of matrix [will be of nxn order].
So logic is that if matrix is 1x1 then it will simply print 0
else while taking input from user [that will be only 1 or 0] i am counting 1's because the logic i develop that when in a matrix of odd order the 1's will be even then it cannot be arranged in rectangular form.and for even order of matrix if 1's are odd , not arrange able .
Next i am traversing each index if i find one then i move to next element else i try to find 1 in the same colomn if dont find than i am breaking loop showing -1 that it is not arrange able in rectangular form
Than after arranging a row i check the next row whether it is already arranged or not if it is than i break everything and moves to next case
n rectangular form
My Solution
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
static long startTime;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int numberOfOnes = 0;
int T = scanner.nextInt();
for (int t = 1; t <= T; t++) {
int n = scanner.nextInt();
int loopCounter, swapCounter = 0;
boolean rowContainsZero = false;
int array[][] = new int[n][n];
boolean reject = true;
//Worst and the most simpler conditions
if (n == 1) {
System.out.print("0");
exitingSystem();
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
array[i][j] = scanner.nextInt();
if (array[i][j] == 1) {
numberOfOnes++;
}
}
}
if (n % 2 == 0 && numberOfOnes % 2 != 0) {
System.out.println("-1");
if (t == T) {
exitingSystem();
}
continue;
} else if (n % 2 != 0 && numberOfOnes % 2 == 0) {
System.out.println("-1");
if (t == T) {
exitingSystem();
}
continue;
}
// System.out.println("Here i am");
//From here swaping processes will take the place
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (array[i][j] == 1) {
continue;
} else if (array[i][j] == 0) {
loopCounter = i;
reject = true;
while (loopCounter < n) {
if (array[loopCounter][j] == 1) {
int temp = array[loopCounter][j];
array[loopCounter][j] = array[i][j];
array[i][j] = temp;
reject = false;
swapCounter += 1;
break;
}
loopCounter++;
}
if (rowContainsZero) {
System.out.println("" + swapCounter);
break;
}
if (reject == true) {
System.out.println("-1");
break;
} else {
for (int m = i + 1; m < n; m++) {
for (int k = 0; k < n; k++) {
if (array[m][k] == 0) {
rowContainsZero = true;
} else {
rowContainsZero = false;
break;
}
}
}
}
} else {
System.out.println("0's and 1's were Expected :(");
exitingSystem();
}
}
if (reject == true) {
break;
}
}
}
}
public static void exitingSystem() {
System.exit(0);
}
}
BUT THE CODECHEF COMPUTER SAYING WRONG ANSWER + They allowed to take input from keyboard too
I think your algorithm isn't fully correct.
I think the following is a counter-example for your step 4 / odd order (n=3) and even number of ones (numberOfOnes=4):
1 1 0
1 1 0
0 0 0
This should give 0.
Similar for n=4 and numberOfOnes=3:
1 1 1 0
0 0 0 0
0 0 0 0
0 0 0 0
This should give 0 as well.
I haven't yet deeply analyzed your steps 5 and 6.
Here are some more examples:
1 1 1 0
1 1 0 0
1 1 1 0
1 1 0 0
This should give -1, as from 10 ones you can only form rectangles of the form 2*5 or 1*10, which both don't fit into the 4*4 frame.
1 1 1 0
1 1 0 0
1 1 1 0
1 0 0 0
This should give 1, as by moving the lower-left 1 two palces up and right, you get a 3*3 rectangle.
This is not the way you are trying to solve problem. Suppose you have
0 0 1
0 1 1
0 0 1
This is a perfect example of solveable matrix but you can't simply use random swap and then acquire result. You need to use A* search algorithm with manhatten distance.
Make a priority queue
Define manhatten distance.
Create a function which creates succesors of each board. Like if i have above board then it will give you a collection of boards back:
0 0 1
0 1 1 ==> colection
0 0 1
0 1 1
0 0 1
0 0 1
0 0 1
0 0 1
0 1 1
0 1 0
0 1 1
0 0 1
0 0 1
0 1 1
0 1 0
Description of A:*
an initial lis to store visited boar so that you don't visit them again.
i will call MinPriority queue a pq
`insert the initial_board in pq
while(!pq.isEmpty() && !foundGoal(pq.min)) //You find goal when your
manhatten distance is 0.
board = pq.delMin(); //you have to override the distance method in
priority queue so it will return you that board whoms manhatten
distance is smallest.
for(boards b :board.getSuccesors(); // give you collection of boards.
if(notvisited(b,vistiedList)) // so that you don't come in same state again and
again.
pq.insert(b);
visitedList.add(b);`
In first year i had to solve 8-puzzle and you can solve this way however you can also use hamming distance but that's not efficient and here is 8-puzzle code(with A* implementation).

How to use memoization in counting a large number of matrices

I have been given a program, which requires me to count the number of previous states for a matrix.
The given matrix is a boolean matrix. I will use 1 for true and 0 for false to explain the program.
The next state of a cell in a matrix is 1 if, considering these four cells:
the cell itself
the cell right to it
the cell below it
the cell below it, and to its right,
there is only one 1 in all these 4 cells, i.e., there are exactly 3 0s and exactly 1 1 in these 4 cells.
If the given matrix (M) is :
1 1 0 0
0 0 0 1
0 0 1 0
Then for the first cell (M[0][0]), the four cells to be considered are M[0][0], M[0][1], M[1][0] and M[1][1]. So, the next state of the first cell is 0, because we have 2 1 in these 4 cells.
For the second cell (M[0][1]), the four cells to be considered are M[0][1], M[0][2], M[1][1], M[1][2]. So the next state for this cell is 1 because there is only 1 1 in these four cells.
Going this way, the next state for this matrix(M) would be the matrix (N):
0 1 1
0 1 0
The next state will, obviously, be 1 row and 1 column less than the previous state. Thus, a given state of the matrix can have many previous states, for example, besides matrix M, the given matrix :
1 0 1 0
1 0 0 0
1 1 0 0
will also have the next state N.
I have to count the number of previous states that a given matrix has.
I have written the following code :
public class Answer2 {
static boolean[][] main_array,answer_array; // answer_array is the 2D array given to me. main_array is the 2D array which I recurse through, and then find its solution to compare with answer_array.
static int c; // counter
static int answer_array_height,answer_array_width; // matrix height and matrix width
public static int answer(boolean[][] boolean_array)
{
answer_array = boolean_array;
main_array = new boolean[answer_array.length+1][answer_array[0].length+1];
c=0;
answer_array_height = answer_array.length;
answer_array_width = answer_array[0].length;
recurse(1,1);
main_array[0][0] = true;
recurse(1,1);
return c;
}
public static String pad(String s, int l){ //Add 0's to the beginning of the string till its length equals l
for(int i=s.length(); i<l; i++)
s='0'+s;
return s;
}
public static void recurse(int w, int h){
if(w==answer_array_width+1 && h==answer_array_height+1){
c++;
return;
}
//System.out.println(java.util.Arrays.deepToString(main_array).replace("],","],\n").replace("true","1").replace("false","0"));
if(h==answer_array_height+1 || h>=w){//Add column
int x = 0;
for(int i=0; i<h; i++) x+=(int)Math.pow(2,i); //This will give me the integer representation of max value(whose binary representation will be used to put values in the matrix) to handle.
for(int i=0; i<=x; i++){
String str = pad(Integer.toBinaryString(i),h);
for(int j=0; j<h; j++){
main_array[j][w]= str.charAt(j)=='1'; //set main_array[j][w] true wherever the binary representation of i has 1. This recurses through all the combinations.
}
if(check(w+1,h,false)){
recurse(w+1, h);
}else{
for(int j=0; j<h; j++){
main_array[j][w]=false;
}
}
}
}else{//Add row
int x = 0;
for(int i=0; i<w; i++) x+=(int)Math.pow(2,i);
for(int i=0; i<=x; i++){
String str = pad(Integer.toBinaryString(i),w);
for(int j=0; j<w; j++){
main_array[h][j]= str.charAt(j)=='1';
}
if(check(w,h+1,true)){
recurse(w, h+1);
}else{
for(int j=0; j<w; j++){
main_array[h][j]=false;
}
}
}
}
}
// w is the effective width, h is the effective height, height_was_increased is true if height was increased, false if width was increased.
//height_was_increased helps to shorten the time used for comparison as the matrix was correct before the width or height was increased. So it just needs to check the increased portion.
public static boolean check(int w, int h, boolean height_was_increased){
if(height_was_increased){
for(int j=0; j<w-1; j++){
//I know this part is complex. It just finds out the answer of the four cells to be considered and matches it with the given matrix.
if(answer_array[h-2][j] != (main_array[h-2][j]^main_array[h-2+1][j]^main_array[h-2][j+1]^main_array[h-2+1][j+1] && !(main_array[h-2][j] && main_array[h-2+1][j]) && !(main_array[h-2][j+1] && main_array[h-2+1][j+1]))) return false;
}
}else{
for(int i=0; i<h-1; i++){
if(answer_array[i][w-2] != (main_array[i][w-2]^main_array[i+1][w-2]^main_array[i][w-2+1]^main_array[i+1][w-2+1] && !(main_array[i] [w-2] && main_array[i+1][w-2]) && !(main_array[i][w-2+1] && main_array[i+1][w-2+1]))) return false;
}
}
return true;
}
}
What it basically does, is that it begins with an empty matrix (of the appropriate size for its next state that gives the matrix asked for) and starts from the top left corner, increasing the effective width and height alternately by 1, and checking if the next state of the matrix till now corresponds to the given state. If not, it skips the rest of the matrix. Then, if a matrix whose next state is the same as the given state is found, it increases the counter by 1.
This code works for small matrices (no. of cells <40), but it takes a lot of time for large matrices. The maximum width of the matrix can be 50 and the maximum height can be 9. So this code doesn't quite work for that purpose.
I know that I have to use memoization here (doing c++ thousands of times is just not right!) But I can't imagine how to implement it. I have previously written programs using dynamic programming, but have no idea where it would be used here. Any help would be appreciated.
There are lot of possible matrices that produce given next state. If next state matrix N is given and initial matrix M is partially filled, for example elements m[x][y+1], m[x+1][y], and m[x+1][y+1]
are filled, than possibilities for element m[x][y] are checked with value s = m[x][y+1] + m[x+1][y] + m[x+1][y+1], in a way:
if n[x][y] == 1:
if s == 0 than m[x][y] = 1
if s == 1 than m[x][y] = 0
if s > 1 than m[x][y] can't be filled
if n[x][y] == 0:
if s == 0 than m[x][y] = 0
if s == 1 than m[x][y] = 1
if s > 1 than m[x][y] = 0 or 1
It looks like values 1 in N 'filter' combinations and values 0 in N 'multiply' them.
Since height is bounded by smaller value I suggest approach first to fill last column with possible
values, than pass columns backward, fill last column element and than by upper check fill element by element.
Python implementation:
import numpy
from itertools import product
num_results = 0
def fill_xy(m, s, x, y):
if y < 0:
fill_x_last(m, s, x-1)
return
_sum = s[x+1, y] + s[x+1, y+1] + s[x, y+1]
if m[x, y] == 1:
if _sum == 0:
s[x, y] = 1
elif _sum == 1:
s[x, y] = 0
else:
return
else:
if _sum == 0:
s[x, y] = 0
elif _sum == 1:
s[x, y] = 1
else:
s[x, y] = 0
fill_xy(m, s, x, y-1)
s[x, y] = 1
fill_xy(m, s, x, y-1)
def fill_x_last(m, s, x):
global num_results
if x < 0:
print s
num_results += 1
else:
s[x, s.shape[1]-1] = 0
fill_xy(m, s, x, s.shape[1]-2)
s[x, s.shape[1]-1] = 1
fill_xy(m, s, x, s.shape[1]-2)
def solve(m):
global num_results
height = m.shape[1]+1
s = numpy.zeros((m.shape[0]+1, height), dtype=numpy.uint8)
for p in product((0, 1), repeat=height):
s[-1, :] = p
fill_x_last(m, s, s.shape[0]-2)
print num_results
solve(numpy.array([[0, 1, 1], [0, 1, 0]], dtype=numpy.uint8))

Adding Two Elements in an Array (Java)

I need to write a method that adds the two last elements of two arrays together and add the sum on another array. But if the two elements are more than 10, then I would need to carry that number to the next array.
This program should be similar to what an odometer does.
Here is my sample code.
int [] sum(int []number1, int []number2)
{
int [] total;
int carry = 0;
for ( int k = numbers1 - 1; k >= 0; k++)
{
sum = number1[k] + number2[k] + carry;
carry = sum/10;
total[k] = sum
}
return total;
}
An example output would be:
0 1 2 3 4
0 8 9 9 9
4 5 7 0 3
5 4 7 0 2
So the top array is just a visual aid that tells where the position for the number is.
The program is suppose to add the next two arrays together. i.e. 9 + 3 = 12
Since 12 is above 9, it carries over the 10 to the next set of arrays, that is why the third array only has a 2 in its place, and that is why the next array is 9 + 0 = 0; because the 10 was carried over.
I am not sure why my code won't work. I don't get the right numbers. Could anyone give any hints or solution to the problem?
-Thanks
I assume numbers1 is the number of elements in the array.
In this case it should be k-- instead of k++ because you are starting from the last element and moving backword.

Creating a looping square with java

Full Disclosure: Homework.
Explanation: I cant understand my teacher.
Problem:
Write a method called printSquare that takes in two integer
parameters, a min and a max, and prints the numbers in the range from
min to max inclusive in a square pattern. The square pattern is
easier to understand by example than by explanation, so take a look at
the sample method calls and their resulting console output in the
table below. Each line of the square consists of a circular sequence
of increasing integers between min and max. Each line prints a
different permutation of this sequence. The first line begins with
min, the second line begins with min + 1, and so on. When the
sequence in any line reaches max, it wraps around back to min. You
may assume the caller of the method will pass a min and a max
parameter such that min is less than or equal to max
I cannot for the life of me figure out how to make the numbers stop at the 'max' value and start over in the middle of the line.
This is what I have so far, apologies but I have trouble with for loops.
for(int i = 0; i < row; i++)
{
for(int d = 0; d < row; d++)
{
System.out.print(d+1);
}
System.out.println(i);
}
I know I used row twice, but its the only way i can get the compiler to form a square shape with the loop. Does anyone even remotely understand what i'm trying to do? :/
This is actually a nice mathematical problem. Assume:
int side = to - from + 1; /// the size/width of the square.
the value at any point in the square (row, col) is:
from + ((row + col) % side)
you should be able to put that in your loops and "smoke it".
Edit based on comment asking for explanation.
The trick is to loop through all the positions in the 'matrix'. Given that the matrix is square, the loops are relatively simple, just two loops (nested) that traverse the system:
final int side = to - from + 1;
for (int row = 0; row < side; row++) {
for(int col = 0; col < side; col++) {
... magic goes here....
}
}
Now, in this loop, we have the variables row and col which represent the cell in the matrix we are interested in. The value in that cell needs to be proportional to the distance it is from the origin..... let me explain.... If the origin is the top left (which it is), then the distances from the origin are:
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
The distance is the sum of the row and the column...... (rows and columns start counting from 0).
The values we put in each matrix are limited to a fixed range. For the above example, with a square of size 5, it could have been specified as printSquare(1,5).
The value in each cell is the from value (1 in this example) plus the distance from the origin... naively, this would look like:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
here the values in the cell have exceeded the limit of 5, and we need to wrap them around... so, the trick is to 'wrap' the distances from the origin..... and the 'modulo' operator is great for that. First, consider the original 'origin distance' matrix:
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
if we instead populate this matrix with 'the remainder of the distance when dividing by 5' (the modulo 5, or %5) we get the matrix:
0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
Now, if we add this 'modulo' result to the from value (1), we get our final matrix:
1 2 3 4 5
2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4
in a sense, all you need to know is that the value at each cell is:
the from value plus the remainder when you divide the 'distance' by the width.
Here's the code I tested with:
public static final String buildSquare(final int from, final int to) {
final StringBuilder sb = new StringBuilder(side * side);
final int side = to - from + 1;
for (int row = 0; row < side; row++) {
for(int col = 0; col < side; col++) {
sb.append( from + ((row + col) % side) );
}
sb.append("\n");
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(buildSquare(1, 5));
System.out.println(buildSquare(3, 9));
System.out.println(buildSquare(5, 5));
System.out.println(buildSquare(0, 9));
System.out.println(buildSquare(0, 3));
}
Since this is homework, I'll just give a hint.
I cannot for the life of me figure out how to make the numbers stop at the 'max' value and start over in the middle of the line.
Here's one way to do it.
Create the first number twice in an array. Taking the printSquare(1, 5) example, create an int array of 1, 2, 3, 4, 5, 1, 2, 3, 4, 5.
Use a loop to loop through the array, starting with element zero and ending with element 4, and another loop to display 5 digits (max - min + 1).
try this
int i,j,k;
for(i=min;i<=max;i++) {
for(j=i;j<=max;j++) {
System.out.print(j);
}
for(k=min;k<i;k++){
System.out.print(k);
}
System.out.println();
}
you can try
loop from min value to max value and put all the numbers in an array
now loop again from min value to max value
each time print the array and do a circular shift (for circular shift you can find lot of example in SO)
I think #rolfl's solution is the cleanest. I'd recommend going with that.
You can find another simple solution by observing that each output in your "square" simply shifts the first element to the end the list of numbers. To imitate this, you can put all the numbers from min to max in a data structure like LinkedList or ArrayDeque where you can easily add/remove items from both ends, then you'd print the contents in order, and shift the first entry to the end. E.g., coll.addLast(coll.removeFirst()). If you repeat that process max - min + 1 times, you should get the desired output.
no array no problem you can easily solve.
it work with any range of number.
static void printSquare(int min, int max){
int len = max - min + 1;
int copy_min = min, permanent_min = min;
for(int i = 0; i < len; i++){
for(int j = 0; j< len; j++){
if(min > max)
if(min % len < permanent_min)
System.out.print((min % len )+ len);
else
System.out.print(min % len);
else
System.out.print(min);
min++;
}
min = ++copy_min;
System.out.println();
}
}
public static void printSquare(int min, int max)  {
    
    for (int i = min; i <= (max -min)+min; i++)  {
        
        for( int j =i; j <= max ; j++) {                
            System.out.print(j);   
            } 
        for (int j1= min; j1<= i * 1 - 1; j1++) {
            System.out.print(j1);
            }
        System.out.println();
    }
    
}

How does this Java code snippet "work"?

The Java interview question is:
Without using a temporary buffer, separate the 0's and 1's from an array, placing all 0's to the left and 1's to the right. Print the result as a string. For example, given {0,1,1,0,0,1}, the output is "000111".
And an answer is:
public class ZeroOneSeparator {
public static void zeroOneSeparator(int[] inputArr){
// for each index, store number of 1's up to the index
for (int i = 1; i < inputArr.length; i++) {
inputArr[i] = inputArr[i-1] + inputArr[i];
}
// This is the "magical math" block I don't understand.
// Why does this "work"?
for (int i = inputArr.length - 1; i > 0; i--) {
if (inputArr[i] > 0) {
inputArr[i-1] = inputArr[i] - 1;
inputArr[i] = 1;
} else {
inputArr[i-1] = 0;
}
}
for (int i = 0; i < inputArr.length; i++) {
System.out.print(inputArr[i]);
}
}
public static void main(String[] args) {
int[] inputArr1 = {1,0,1,0,1,1};
ZeroOneSeparator.zeroOneSeparator(inputArr1);
System.out.println();
int[] inputArr2 = {1,1,1,0,0,0,0,0,0,1};
ZeroOneSeparator.zeroOneSeparator(inputArr2);
int[] inputArr3 = {}; // intentionally empty
System.out.println();
ZeroOneSeparator.zeroOneSeparator(inputArr3);
int[] inputArr4 = {0,0,0,0,0,0};
System.out.println();
ZeroOneSeparator.zeroOneSeparator(inputArr4);
int[] inputArr5 = {0,1,0,1,0,1,0,1};
System.out.println();
ZeroOneSeparator.zeroOneSeparator(inputArr5);
int[] inputArr6 = {1,1,1,1,1,1,0,0,0,0,0};
System.out.println();
ZeroOneSeparator.zeroOneSeparator(inputArr6);
}
}
I stepped through this code with a debugger, but I still don't understand why it works. Can someone please walk me through it?
Let's try an example to see what's going on. Suppose we had the following array:
0 1 1 0 1 1 0 0
The first loop (as the comment specifies) counts up the total number of 1's seen so far, where each 1 increases the count, and each 0 leaves it the same. So for our array, we end up with this:
0 1 2 2 3 4 4 4
Note that the final element of the array is now 4, which is the total number of 1s. We use that fact in the next loop.
We start at the last element, and check if it's greater than 0. If it is, then we replace that element with a 1, and then decrement that count by 1 and assign it to the previous element. We keep doing that, filling in 1s as we go, until the count reaches 0. At that point, we set each element we encounter to 0.
What's really happening here is that once we know how many 1s there are, we can "count down" from the end of the array, filling in that number of 1s. At that point, we know the rest of the elements must be 0, so we can set the rest of the elements to 0 at that point.
Visually, it looks like this (with the "current element" surrounded by []'s)
0 1 2 2 3 4 4 [4] ->
0 1 2 2 3 4 [3] 1 ->
0 1 2 2 3 [2] 1 1 ->
0 1 2 2 [1] 1 1 1 ->
0 1 2 [0] 1 1 1 1 ->
0 1 [0] 0 1 1 1 1 ->
0 [0] 0 0 1 1 1 1 ->
0 0 0 0 1 1 1 1
Note that the "countdown" seems very obvious when viewed this way.
Since you have the count of 1's in the array at the last index, you will just loop from the back and replace it with 1's. The trick that is done in the code is that, it reduces the count of 1's and store it in the previous array element in every step, so that in the next step, it knows whether the current element is 0 or 1. It works nicely even in the case of all 1's or almost all 1's (only one 0 in the array), since the count of 1's left when it modifies the element at index 0 is just the right number.
Personally, I would record the count of 1's, derive the count of 0's and use 2 loops to print out/set the numbers in the array.
First for loop counts how many 1s are there to the left of each item. If the input is {0,1,1,0,0,1}, as result of counting, the input array becomes {0,1,2,2,2,3}.
The last index 3 says that there are three 1s in the list. Second for loop walks the list in reverse and marks 1 for the last three items.

Categories