I'm trying to turn an array like this:
0, 1, 2, 2, 1, 0, 1, 0, 0, 1, 2
into this:
0 0 0 0 1 1 1 1 2 2 2
Here is my code:
public static int[] sortDNF(int[] tape) {
int smaller = 0; // everything with index < smaller is 0
int bigger = tape.length - 1; // everything with index > bigger is 2
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (tape[current] == 0) {
tmp = tape[smaller];
tape[smaller] = tape[current];
tape[current] = tmp;
smaller++;
}
if (tape[current] == 2) {
tmp = tape[bigger];
tape[bigger] = tape[current];
tape[current] = tmp;
bigger--;
}
current++;
}
return tape;
}
This is what it produces:
0 0 0 1 1 1 1 0 2 2 2
What is my problem?
A guess:
You should not be increasing current every time through the loop since that is supposed to represent the partition between the 1's and the unknowns. For example, when you hit the first 2 you end up swapping it with another 2 and then moving on. The fact that the 2 later gets swapped for a 0 is accidental. The elements between smaller and current should always be 1's and that is broken.
current++ should only be done in the tape[current] != 2 case. It's ok to do it when tape[current] = 0 because you haven't changed the [smaller -> current] = 1-only condition. And it's ok to move it when tape[current] = 1 because that satisfies the 1-only condition.
...but I haven't tried it.
For those who haven't studied the problem, sorting is sufficient to provide a solution, but it is (or can be) more than necessary. Solving the DNF problem only requires that all like items be moved together, but you don't have to place unequal items in any particular order.
It's pretty easy to solve DNF with expected O(N) complexity, where (most normal forms of) sorting have O(N lg N) complexity. Instead of rearranging the input elements, it's much easier to simply count the elements with any given value, then print out the right number of each. Since the order of unequal elements doesn't matter, you normally store your counts in a hash table.
The problem is that you reply on (possible nonexistent) values later in the chain to swap incorrectly skipped 1 values, try your algo on the trivial case of:
1 2 0
the 2 gets swapped in the right spot, but the current index has been advanced over the index 0 ends up in resulting in:
1 0 2
So don't increment current before you inspect the new value at that index.
You need to increment current only during the check for 1 and check for 2. When you are checking for 3 you just need to decrement bigger. Here the working solution. BTW this works for the array of values between 1-3. You can change it to 0-2 if you so wish.
The program produces the following output given a random number array as follows:
Original Array : [1, 3, 3, 3, 2, 2, 2, 1, 2, 2, 1, 3, 3, 2, 1, 1, 3]
Sorted Array : [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3]
private static int[] sortUsingDutchNationalFlagProblem(int[] array)
{
System.out.println("Original Array : " + Arrays.toString(array));
int smaller = 0; // everything with index < smaller is 1
int bigger = array.length - 1; // everything with index > bigger is 3
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (array[current] == 1) {
tmp = array[smaller];
array[smaller] = array[current];
array[current] = tmp;
smaller++;current++;
}
if(array[current] == 2)
current++;
if (array[current] == 3) {
tmp = array[bigger];
array[bigger] = array[current];
array[current] = tmp;
bigger--;
}
}
System.out.println("Sorted Array : " + Arrays.toString(array));
return array;
}
Here is my approach using Java
public static void dutchNationalFlagProblem(int[] input) {
int low=0, mid=0, high = input.length -1;
while(mid <=high) {
switch (input[mid]) {
case 0:
swap(input, low++, mid++);
break;
case 1:
mid++;
break;
default :
swap(input, mid, high--);
break;
}
}
}
private static void swap(int[] input, int firstIndex, int secondIndex) {
int temp = input[firstIndex];
input[firstIndex] = input[secondIndex];
input[secondIndex] = temp;
}
Here is the corresponding test cases
#Test
public void dutchNationalFlagProblemTest() {
int[] input = new int[]{0,1,0,2,2,1};
ArrayUtils.dutchNationalFlagProblem(input);
assertThat(input, equalTo(new int[]{0,0,1,1,2,2}));
}
I'm going to propose an easier solution :-)
public static int[] sortDNF(int[] tape) {
Arrays.sort(tape);
return tape;
}
As far as what's wrong with your solution, when the current element is 2 and it's swapped with the element near the end of the list, the element it's swapped with may not be 2, but you're incrementing current and not looking at this position again.
public static int[] sortDNF(int[] tape) {
int smaller = 0; // everything with index < smaller is 0
int bigger = tape.length - 1; // everything with index > bigger is 2
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (tape[current] == 0) {
tmp = tape[smaller];
tape[smaller] = tape[current];
tape[current] = tmp;
smaller++;
}
else if (tape[current] == 2) {
tmp = tape[bigger];
tape[bigger] = tape[current];
tape[current] = tmp;
bigger--;
}
current++;
}
return tape;
}
Below program is giving right result, do you guys see any issue in this simple innocent programming approach?
public class Test {
public static void main(String[] args) {
int arr[] = {1, 2, 0, 2, 1, 1, 1, 0, 0, 2, 2, 1, 0, 1, 2, 0, 2};
int x0 = 0, x1 = 0, x2 = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0)
x0++;
if (arr[i] == 1)
x1++;
if (arr[i] == 2)
x2++;
}
int i = 0;
for (int j = i; j < x0; j++)
arr[i++] = 0;
for (int j = i; j < x0+x1; j++)
arr[i++] = 1;
for (int j = i; j < x0+x1+x2; j++)
arr[i++] = 2;
printArray(arr);
}
private static void printArray(int[] arr) {
for (int a : arr) {
System.out.print(a + " ");
}
}
}
Related
I have an array of integers representing a deck of cards with 52 cards in it, with numbers ranging from 1-52 to represent the cards. I am trying to write a method which will take two positions within the array as parameters to divide the array into three blocks. Block 1 is all the values below the first position, block 2 is all the values lying between the two positions(inclusive of the values at position 1 and 2) and block 3 is all the values lying above the second position. I then want to switch the positions of blocks 1 and 3 within the array.
So for an array with the values of:
1,2,3,4,5,6,7,8,9,10,11,12
Setting positionOne(1), positionTwo(9) would give the array:
11,12,2,3,4,5,6,7,8,9,10,1
Here's what I have so far, which almost works but I think has bugs in it:
public void switchPositions(int pos1, int pos2) {
int[] newCards = new int[cards.length];
int sizeChunkA = 0;
int sizeChunkC = 0;
int sizeChunkB = 0;
int counter = 0;
for(int i = pos2+1; i<cards.length; i++) {
sizeChunkC++;
}
for(int i = 0; i<pos1; i++) {
sizeChunkA++;
}
for(int i = pos1; i<=pos2; i++) {
sizeChunkB++;
}
for(int i = 1; i<=sizeChunkC; i++) {
newCards[counter] = cards[pos2+i];
counter++;
}
for(int i=pos1; i<=pos2; i++) {
newCards[counter] = cards[i];
counter++;
}
for(int i=0; i<sizeChunkA; i++) {
newCards[counter] = cards[i];
counter++;
}
cards = newCards;
}
Is there a better way to do this?
Instead of loops use Arrays.copyOfRange and System.arraycopy:
// make a copy of the first section before overwriting it
int[] copy = Arrays.copyOfRange (inputArray, from, to);
// overwrite the first section with the second section
System.arraysCopy(inputArray, sourcePosition, inputArray, from, copy.length);
// copy the original content of the first section to the second section
System.arraysCopy(copy, 0, inputArray, sourcePosition, copy.length);
You have to change the indices according to your requirements.
I would approach it this way.
Create three arraylists for your three temp blocks.
Iterate through your array starting at 0-> position 1. Moving the values into your first array list.
Iterate through your starting at position 1 and ending at position 2. Moving all values into the sec0nd array list.
Then iterate through your array starting at position 2+1 through to the end.
Combine the three array lists.
There is a bit clearer of a way to do this. If you want to code it yourself rather than just use the existing Arrays.copyOfRange (perhaps if you would like to do it in place rather than generating a new array).
int lengthOfDeck = cards.length;
int[] newCards = new int[lengthOfDeck];
for (int i = 1; i <= lengthOfDeck; i++) {
if (i >=pos1 && i <=pos2) {
newCards[i-1]=i;
}
else if (i < pos1) {
newCards[i-1]= i + pos2;
}
else {
newCards[i-1] = i - pos2;
}
}
What about approach without using additional memory?!
Main idea, is that if you want to shift array to the offs positions right or left you can do it in place with 2 full for loops. I give you example:
Initial array {1,2,3,4,5}; we want to move it 2 positions right (i.e. offs=2), and have result {4,5,1,2,3}
1st loop for reverse all elements: {5,4,3,2,1}
2nd loop for reverse first offs elements: {4,5,3,2,1}
3rd loop for reverse other elements: {4,5,1,2,3}.
That's all! To solve your task, you have to do it twice, but second time you have to reduce array's length to not move last elements.
This is example:
public static void main(String... args) throws IOException {
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
replace(arr, 1, 9);
// arr = [11, 12, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1]
}
public static void replace(int[] arr, int pos1, int pos2) {
rotate(arr, arr.length, -pos1);
rotate(arr, arr.length - pos1, -pos2);
}
// offs > 0 - to the right; offs < 0 - to the left
private static void rotate(int[] arr, int length, int offs) {
offs = offs >= length ? length % offs : offs;
length = Math.min(arr.length, length);
for (int i = 0, j = length - 1; i < j; i++, j--)
swap(arr, i, j);
for (int i = 0, j = offs > 0 ? offs - 1 : length + offs - 1; i < j; i++, j--)
swap(arr, i, j);
for (int i = offs > 0 ? offs : length + offs, j = length - 1; i < j; i++, j--)
swap(arr, i, j);
}
private static void swap(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
According to the performance. You have 4 full for loops with one swap in each of them. So performance is O(n), without using additional memory.
Thanks to Eran for his tip, here is a more adapted method for your needs:
public int[] splitArray(int[] i, int position1, int position2) {
position2++;
int[] piece1 = Arrays.copyOfRange(i, 0, position1);
int[] piece2 = Arrays.copyOfRange(i, position1, position2);
int[] piece3 = Arrays.copyOfRange(i, position2, i.length);
System.arraycopy(piece3, 0, i, 0, piece3.length);
System.arraycopy(piece2, 0, i, piece3.length, piece2.length);
System.arraycopy(piece1, 0, i, piece3.length+piece2.length, piece1.length);
return i;
}
I had an assignment due today ( its late already so i wont be getting credit for this but the problem is eating at me) that I could not figure out for the life of me. The assignment goes as followed: withoutTen: Return a version of the given array where all the 10's have been removed.
The remaining elements should shift left towards the start of the array as needed,
and the empty spaces at the end of the array should be 0.
So {1, 10, 10, 2} yields {1, 2, 0, 0}.
You may modify and return the given array or make a new array.
withoutTen({1, 10, 10, 2}) --> {1, 2, 0, 0}
withoutTen({10, 2, 10}) --> {2, 0, 0}
withoutTen({1, 99, 10}) --> {1, 99, 0}
I have tried various things to make the program work but failed.
`
// The arraylist is already defined to an Integer class with a name of list
int i= 0;
//loop
for(i = 0; i < list.size(); i++)
{
if( list.get(i) == 10)
{
list.remove(i);
list.add(0);
}
}
return list;
`
This shows the right result {1,2,0,0} but that is the only one. Can anyone explain to me if there is a way to change the value of the index if it equals 10 and send it to the back of the line as a 0?
I think you do not fully understand Java syntax. I do not say that to feel smug, but the code probably does not do what you think it does. Review your Java syntax knowledge and then try again. :-)
You could simply do
int nbOccur = Collections.frequency(yourList, 10);
yourList.removeAll(Collections.singleton(10));
yourList.addAll(Collections.nCopies(nbOccur, 0));
Get the number of occurences(n) of 10.
Remove them all.
Add a List that contains n times 0.
A one liner with Java 8 would be
yourList.stream()
.filter(i -> i != 10)
.collect(Collectors.toList())
.addAll(Collections.nCopies(Collections.frequency(yourList, 10), 0));
Basically - you treat it as a copy process where you walk a from and a to up the array making sure from skips 10 whenever it sees it.
public int[] withoutTen(int[] a) {
// Where we are copying to.
int to = 0;
// Where we are copying from.
int from = 0;
// Zero padding at the end so carry on 'till to gets there.
while (to < a.length) {
// Skip all 10s.
while (from < a.length && a[from] == 10) {
// Skip it.
from += 1;
}
// Copy it (or 0 if we're past the end).
a[to++] = from < a.length ? a[from++] : 0;
}
return a;
}
public void test() {
int[][] tests = new int[][]{
{1, 10, 10, 2},
{10, 2, 10},
{1, 99, 10}
};
for (int[] a : tests) {
System.out.println("\t" + Arrays.toString(a) + " -> " + Arrays.toString(withoutTen(a)));
}
}
prints
[1, 10, 10, 2] -> [1, 2, 0, 0]
[10, 2, 10] -> [2, 0, 0]
[1, 99, 10] -> [1, 99, 0]
Equivalent using for loop is a little tidier:
public int[] withoutTen(int[] a) {
// Zero padding at the end so carry on 'till to gets there.
for (int to = 0, from = 0; to < a.length; to++, from++) {
// Skip all 10s.
while (from < a.length && a[from] == 10) {
// Skip it.
from += 1;
}
// Copy it (or 0 if we're past the end).
a[to] = from < a.length ? a[from] : 0;
}
return a;
}
Do you have to worry about the data space complexity ? if yes then -- obviously you can do better --but this should work for your purpose..
psudo code
List<Integer> beforeList = Arrays.asList(1,10,10,0,2);
//create array
int[] myFinalArray = new int[beforeList.size]()
int arrayIdx =0;
for (Integer i : beforeList){
if (i != 10){
myFinalArray[arrayIdx++ ] = i;
}
}
//print/return your finalArray
Thank you guys for the help, it seems that my code was almost correct but i did have my list.get() method wrong. I had placed a (1) instead of (i) which in fact caused me a lot of pain and errors. for(int i=0; i<list.size();i++)
{
if(list.get(i)==10)
{
list.remove(i);
list.add(0);
}
Take a look at my answer for this
public int[] withoutTen(int[] nums) {
if (nums == null) {
return nums;
}
int non10pos = 0;
for (int i = 0; i < nums.length; ++i) {
if (nums[i] != 10) {
int temp = nums[i];
nums[i] = nums[non10pos]; // move the non-zero number to position i
nums[non10pos] = temp; // move the non-10 number toward the front
++non10pos;
}
else{
nums[i] = 0;
}
}
return nums;
}
My solution passes all the tests:
public int[] withoutTen(int[] nums) {
if (nums.length == 0) {
return nums;
}
int[] withoutTen = new int[nums.length];
int counter = 0;
for (int i = 0; i < withoutTen.length; i++) {
if (nums[i] != 10) {
for (int j = counter; j < withoutTen.length; j++) {
withoutTen[j] = nums[i];
break;
}
counter++;
}
}
return withoutTen;
}
I have been trying to get the range of a Maximum Product of a subarray (studying for job interviews).
This has been already asked here (but no valid answers have been provided).
Getting range of max product subarray using Kadanes algorithm
The trick/algorithm is explained well here: http://www.geeksforgeeks.org/maximum-product-subarray/
I am able to get the maximum product easily, but after a lot of tries, still can't figure out how to get the range (left and right indexes properly). Can anyone please help??
I have pasted my code, so you can just copy and run it quickly..
import java.util.*;
public class ArrayMax {
// maximum product
public static int[] getMaxProduct(int[] list)
{
int max = 1, min = 1, maxProd = 0;
int l = 0, left = 0, right = 0;
for (int i = 0; i < list.length; i++) {
// positive number!
if (list[i] > 0) {
max = max * list[i];
min = Math.min(1, min * list[i]);
}
else if (list[i] == 0) {
max = 1; // reset all
min = 1;
l = i + 1;
}
else {
// hold the current Max
int tempMax = max;
// need to update left here (but how??)
max = Math.max(min * list[i], 1); // [-33, 3]
min = tempMax * list[i]; // update min with prev max
}
// System.out.printf("[%d %d]%n", max, min);
if (max >= maxProd) {
maxProd = max;
right = i;
left = l;
}
}
System.out.println("Max: " + maxProd);
// copy array
return Arrays.copyOfRange(list, left, right + 1);
}
// prints array
public static void printArray(int[] list) {
System.out.print("[");
for (int i = 0; i < list.length; i++) {
String sep = (i < list.length - 1) ? "," : "";
System.out.printf("%d%s", list[i], sep);
}
System.out.print("]");
}
public static void main(String[] args) {
int[][] list = {
{5, 1, -3, -8},
{0, 0, -11, -2, -3, 5},
{2, 1, -2, 9}
};
for (int i = 0; i < list.length; i++) {
int[] res = getMaxProduct(list[i]);
printArray(list[i]);
System.out.print(" => ");
printArray(res);
System.out.println();
}
}
}
Here are sample outputs:
Max: 120
[5,1,-3,-8] => [5,1,-3,-8]
Max: 30
[0,0,-11,-2,-3,5] => [-11,-2,-3,5]
Max: 9
[2,1,-2,9] => [2,1,-2,9]
As you can see, I am getting the maximum product, but the range is wrong.
Case#2, Max is 30 (correct answer: [-2,-3,5], showing: [-11,-2,-3,5])
Case#3, Max is 9 (correct answer: [9], giving: [2,1,-2,9])
Please help.
Easier way is to try to find left position/marker when you have calculated the maxProd (at the end). Your right position is accurate, so set left to right and divide maxProd by list[left] until you hit 1, while decrementing left. Thats when you have reached left.
The following code before the return should solve it.
int temp = maxProd;
left = right;
while (temp != 1) {
temp = temp / list[left--];
}
left++;
// copy array
return Arrays.copyOfRange(list, left, right + 1);
I think you need to keep track of 2 values for l. One will represent the start index for the subarray of numbers that multiply to make max, while the other will represent the start index for the subarray of numbers that multiply to make min.
However, an even easier way is to simply wait until you have found the maximum answer (in maxProd) and its position (in right). At this point you can then loop over the array multiplying the elements of the list until your total reaches maxProd (starting at right and iterating backwards). The last element you multiplied must be the start of the subarray.
Please read the question before marking it as duplicate
I have written following code to remove duplicates from array without using Util classes but now I am stuck
public class RemoveDups{
public static void main(String[] args) {
int[] a = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 3, 1, 4, 52, 1, 45, };
int temp;
for (int i : a) {
for (int j = 0; j < a.length - 1; j++) {
if (a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
a = removeDups(a);
for (int i : a) {
System.out.println(i);
}
}
private static int[] removeDups(int[] a) {
int[] result = new int[a.length];
int j = 0;
for (int i : a) {
if (!isExist(result, i)) {
result[j++] = i;
}
}
return result;
}
private static boolean isExist(int[] result, int i) {
for (int j : result) {
if (j == i) {
return true;
}
}
return false;
}
}
and now the output is
1
2
3
4
5
6
45
52
0
0
0
0
0
0
0
0
0
0
Here my problem is
My code is not working in case of 0s
I am not able to understand how sorting an array can reduce time of execution
Is there any way to remove elements from array without using Util classes I know one way to remove convert array into list and then remove but for that also we need Util classes is there any way to implement by myself.
Since the numbers you deal with are limited to a small range you can remove duplicates by a simple "counting sort": mark the numbers you have found in a set-like data structure and then go over the data structure. An array of boolean works just fine, for less memory usage you could create a basic bitset or hash table. If n is the number of elements in the array and m is the size of the range, this algorithm will have O(n+m) complexity.
private static int[] removeDups(int[] a, int maxA) {
boolean[] present = new boolean[maxA+1];
int countUnique = 0;
for (int i : a) {
if (!present[i]) {
countUnique++;
present[i] = true;
}
}
int[] result = new int[countUnique];
int j = 0;
for (int i=0; i<present.length; i++) {
if (present[i]) result[j++] = i;
}
return result;
}
I am not able to understand how sorting an array can reduce time of execution
In a sorted array you can detect duplicates in a single scan, taking O(n) time. Since sorting is faster than checking each pair - O(n log n) compared to O(n²) time complexity - it would be faster to sort the array instead of using the naive algorithm.
As you are making the result array of the same length as array a
so even if you put only unique items in it, rest of the blank items will have the duplicate values in them which is 0 for int array.
Sorting will not help you much, as you code is searching the whole array again and again for the duplicates. You need to change your logic for it.
You can put some negative value like -1 for all the array items first in result array and then you can easily create a new result array say finalResult array from it by removing all the negative values from it, It will also help you to remove all the zeroes.
In java , arrays are of fixed length. Once created, their size can't be changed.
So you created an array of size18.
Then after you applied your logic , some elements got deleted. But array size won't change. So even though there are only 8 elements after the duplicate removal, the rest 10 elements will be auto-filled with 0 to keep the size at 18.
Solution ?
Store the new list in another array whose size is 8 ( or whatever, calculate how big the new array should be)
Keep a new variable to point to the end of the last valid element, in this case the index of 52. Mind you the array will still have the 0 values, you just won't use them.
I am not able to understand how sorting an array can reduce time of execution
What ? You sort an array if you need it to be sorted. Nothing else. Some algorithm may require the array to be sorted or may work better if the array is sorted. Depends on where you are using the array. In your case, the sorting will not help.
As for your final question , you can definitely implement your own duplicate removal by searching if an element exists more than once and then deleting all the duplicates.
My code is not working in case of 0
There were no zeroes to begin with in your array. But because its an int[], after the duplicates are removed the remaining of the indexes are filled with 0. That's why you can see a lot of zeroes in your array. To get rid of those 0s, you need to create another array with a lesser size(size should be equal to the no. of unique numbers you've in your array, excluding 0).
If you can sort your array(I see that its already sorted), then you could either bring all the zeroes to the front or push them to the last. Based on that, you can iterate the array and get the index from where the actual values start in the array. And, then you could use Arrays.copyOfRange(array, from, to) to create a copy of the array only with the required elements.
try this
package naveed.workingfiles;
public class RemoveDups {
public static void main(String[] args) {
int[] a = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 3, 1, 4, 52, 1, 45, };
removeDups(a);
}
private static void removeDups(int[] a) {
int[] result = new int[a.length];
int j = 0;
int count = 0;
for (int i : a) {
if (!isExist(result, i)) {
result[j++] = i;
count++;
}
}
System.out.println(count + "_____________");
for (int i=0;i<count;i++) {
System.out.println(result[i]);
}
// return result;
}
private static boolean isExist(int[] result, int i) {
for (int j : result) {
if (j == i) {
return true;
}
}
return false;
}
}
public class RemoveDups {
public static void main(String[] args) {
int[] a = { 1, 2, 0, 3, 1,0, 3, 6, 2};
removeDups(a);
}
private static void removeDups(int[] a) {
int[] result = new int[a.length];
int j = 0;
int count = 0;
boolean zeroExist = false;
for (int i : a) {
if(i==0 && !zeroExist){
result[j++] = i;
zeroExist = true;
count++;
}
if (!isExist(result, i)) {
result[j++] = i;
count++;
}
}
System.out.println(count + "_____________");
for (int i=0;i<count;i++) {
System.out.println(result[i]);
}
// return result;
}
private static boolean isExist(int[] result, int i) {
for (int j : result) {
if (j == i) {
return true;
}
}
return false;
}
}
// It works even Array contains 'Zero'
class Lab2 {
public static void main(String[] args) {
int[] a = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 3, 1, 4, 52, 1, 45 };
removeDups(a);
}
private static void removeDups(int[] a) {
int[] result = new int[a.length];
int j = 0;
int count = 0;
for (int i : a) {
if (!isExist(result, i)) {
result[j++] = i;
count++;
}
}
System.out.println(count + "_____________");
for (int i = 0; i < count; i++) {
System.out.println(result[i]);
}
}
private static boolean isExist(int[] result, int i) {
for (int j : result) {
if (j == i) {
return true;
}
}
return false;
}
}
I am not using HeapSort to sort an array that is already filled, but am using HeapSort as the array is filled.
For a heap where the smallest values are at the top, my understanding was that when you insert a new value to the heap you checked the parent node to see if the new child is larger. If it is you do nothing, if it isn't you check and swap as need up the tree?
Is this not right because my implementation of it is not working at all:
public class HeapSort{
static int[] numbers = new int[] { 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
static int[] array = new int[16];
public static void main(String[] args) {
for (int i = 1; i < 15; i++) {
array[i] = numbers[i];
if (i > 1)
sort(i);
}
for (int i = 1; i < 15; i++) {
System.out.println(array[i]);
}
}
public static void sort(int i) {
int parentLocation = i / 2;
int childLocation = i;
int parentValue = array[parentLocation];
int childValue = array[childLocation];
if(parentValue > childValue){
array[parentLocation] = childValue;
array[childLocation] = parentValue;
}
if(parentLocation != 1){
sort(parentLocation);
}
}
}
TIA
If its anyhelp this is the output when I give it 1-15 backwards:
2
6
3
9
7
5
4
15
12
13
8
14
10
11
But you all seem as stumped as I am!
It looks like your not sorting the entire array. Say that you have 10 fields correctly sorted and you insert number. So you have
-, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11 and insert 10 (should go second last and the - is cause you never put anything there)
Now your algorithm compares parentLocation 5 (11/2) and childLocation 11 right? Well, 5 is smaller than 11 so nothing is swapped. Then you continue to sort again with input 5.
This time you compare parentLocation 2 (5/2) and childLocation 5. 2 is smaller than 5, still not change.
Until done. But you never test to see if 10 and 11 is in the correct order at all, you start half way down.
Easiest fix is to change your two iterations to
for (int i = 1; i < numbers.length; i++) {...}
...
for (int i = 1; i < array.length; i++) {...}
As your missing the end positions in your current code.
Then change the first line in sort() to
int parentLocation = i - 1;
That way your recursive check checks the entire array.
But this is regular sorting, nothing heapy about it :)
Added complete new solution :)
I'm sure this is not the optimal solution but it's easy to follow. I've replaced the target heap with an ArrayList to be able to shrink it easy.
package se.wederbrand.stackoverflow;
import java.util.ArrayList;
public class HeapSort {
static int[] numbers = new int[]{0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
static ArrayList<Integer> heap = new ArrayList<Integer>();
public static void main(String[] args) {
// add 0 at the first position
heap.add(0);
for (int i = 1; i < numbers.length; i++) {
heap.add(numbers[i]);
if (i > 1) {
reheapifyBottomUp(i);
}
}
while (heap.size() > 1) {
System.out.println(removeFirstFromHeap());
}
}
private static int removeFirstFromHeap() {
// the value at index 1 should be returned
int returnValue = heap.get(1);
// the last node is replacing the removed one
if (heap.size() > 2) {
heap.set(1, heap.remove(heap.size() - 1));
reheapifyTopDown(1);
}
else {
heap.remove(1);
}
return returnValue;
}
public static void reheapifyBottomUp(int childLocation) {
int parentLocation = childLocation / 2;
int parentValue = heap.get(parentLocation);
int childValue = heap.get(childLocation);
if (parentValue > childValue) {
heap.set(parentLocation, childValue);
heap.set(childLocation, parentValue);
}
if (parentLocation != 1) {
reheapifyBottomUp(parentLocation);
}
}
public static void reheapifyTopDown(int parentLocation) {
int childLocation1 = parentLocation * 2;
int childLocation2 = childLocation1 + 1;
int parentValue = heap.get(parentLocation);
int childValue1 = Integer.MAX_VALUE;
if (heap.size() > childLocation1) {
childValue1 = heap.get(childLocation1);
}
int childValue2 = Integer.MAX_VALUE;
if (heap.size() > childLocation2) {
childValue2 = heap.get(childLocation2);
}
if (childValue1 <= childValue2 && parentValue > childValue1) {
// swap them and continue down
heap.set(parentLocation, childValue1);
heap.set(childLocation1, parentValue);
reheapifyTopDown(childLocation1);
}
else if (childValue2 < childValue1 && parentValue > childValue2) {
// swap them and continue down
heap.set(parentLocation, childValue2);
heap.set(childLocation2, parentValue);
reheapifyTopDown(childLocation2);
}
}
}
All you have done here is add a bunch of numbers to a heap (which looks correct at first glance). The array contains values in a heap -- not a sorted list. To get the sorted list out, in heap sort, you need to keep popping the smallest element and re-heaping. You're missing this step; you're just printing out the final heap before you start that process.
Your sort method should be renamed as heapify(). Your current sort() method is just re-arranging the heap.
A min/max heap is never sorted, and hence you can never traverse it the way you can traverse Binary Search Tree. You will have to implement something like removeMin() which will remove the topmost element from the heap and the sort the remaining heap.