SelectionSort implemented with Generic Method - Wrong Results - java

I'm working through Deitel & Deitel's "Java - How To Program" and I'm stumped on why this implementation I came up with of a generic selection sort method is not working. I'm sure I must be missing some small detail, but after researching the API and a few resources on Generics, I'm coming up cold. While the program runs and performs some kind of sort, it definitely does NOT come out sorted in numerical order! I can't tell if I'm misunderstanding Generics, or just the selection sort algorithm. Any help would be appreciated!
The output I receive on running the selection sort on the intArray is:
0, 1, -23, 7, 54
The output for the floatArray after the sort is:
-1.1, -10.3, 0.4, 4.5
UPDATE I just tried this without using negative values and it sorts fine, what is that about???
Here is the complete Sorter class that performs the selection sort:
import java.util.Arrays;
import java.util.ArrayList;
public class Sorter {
public static void main(String[] args) {
Integer[] intArray = {1, 7, -23, 54, 0};
Float[] floatArray = {0.4f, -10.3f, 4.5f, -1.1f};
ArrayList<Integer> intList = new ArrayList<>(Arrays.asList(intArray));
ArrayList<Float> floatList = new ArrayList<>(Arrays.asList(floatArray));
System.out.printf("Lists before selectionSort: %n%s%n%s%n%n",
intList, floatList);
selectionSort(intList);
selectionSort(floatList);
System.out.printf("Lists after selectionSort: %n%s%n%s%n%n",
intList, floatList);
}
public static <T extends Comparable<T>> void selectionSort(ArrayList<T> list) {
// helps determine whether or not a swap will occur
boolean needsSorting = false;
// keeps track of the index of the smallest value
int smallest = 0;
// outer for walks the portion of the list that will be swapped
for (int i = 0; i < list.size() - 1; i++) {
// inner for searches for a smaller value than the front of list
for (int j = i + 1; j < list.size(); j++) {
// if the inner value is less than the outer value
if (list.get(j).compareTo(list.get(i)) < 0) {
// store the index of the smaller value
smallest = j;
// set the boolean flag to true so the sort will happen
needsSorting = true;
}
}
// if the list needs sorting
if (needsSorting) {
// get the value of the outer loop, store in generic variable
T temp = list.get(i);
// replace value of outer loop with value at the smallest index
list.set(i, list.get(smallest));
// replace value at what was smallest index with the value that
// was at the index of the outer loop
list.set(smallest, temp);
needsSorting = false;
}
}
}
}

The problem may be occurring at this line,
if (list.get(j).compareTo(list.get(i)) < 0)
Instead of list.get(i), it should be list.get(smallest)
Also, you're not updating smallest, when you should be doing so in each iteration of the outer for loop. Add this line of code right after for (int i = 0; i < list.size() - 1; i++)
smallest = i;

DaneBrick set me in the right direction. I needed to set the smallest variable to be equal to i, the counter for the outer loop, for each iteration of the outer loop. Here is the corrected portion of the code:
for (int i = 0; i < list.size() - 1; i++) {
// inner for searches for a smaller value than the front of list
//*** NEW PORTION, DIDN'T HAVE THIS BEFORE ***//
smallest = i;
for (int j = i + 1; j < list.size(); j++) {
// if the inner value is less than the outer value
if (list.get(j).compareTo(list.get(smallest)) < 0) {
// store the index of the smaller value
smallest = j;
// set the boolean flag to true so the sort will happen
needsSorting = true;
}
}

Related

Swapping lowest value in the array to order from least to greatest

I have this code
public static int[] swapLowest(int[] array)
{
int smallestValue = 0;
for(int i = 0; i < array.length; i++)
{
for(int k = i + 1; k < array.length; k++)
{
if(array[i] > array[k])
{
smallestValue = array[i];
array[i] = array[k];
array[k] = smallestValue;
}
}
}
}
and it works how I want it to: swapping values to make it from least to greatest (e.g [5, 1, 2, 1, 6] would turn to [1, 1, 2, 5, 6]).
However, the logic behind this is wrong. Apparently, I did not meet the criteria:
Find the smallest number in the array from the starting point
Swap the lowest value with the current value, if necessary
Move to the next index 
Repeat until it reaches the last index
I'm not too sure why. Did I understand it wrong? Regardless, I need to fix my code, and any pointers to help me understand/what to do instead will be greatly appreciated
Seems like you are trying to write a variation of selection sort (exchange sort) and there is nothing wring with your code. There are couple of ways you could implement selection sort.
swap the elements outside the inner for loop
swap the elements inside the inner for loop
you are following up the second method
inside your inner for loop you are looking for the smallest value compared to your current value and if there is a one you are swapping them.
But regarding the time complexity second method (which you have used) could be bit more expensive. Because rather than finding the lowest element in the array and swapping with it, you swap elements every time there is an lower element compared to your current element inside the inner loop.
What you could do is record the index if it's lower than your element and continue to traverse the array inside the inner for loop checking if there are other elements lower than the current (if there are update the lowest element index)and once you found it and out of inner loop you could swap the elements.
public static int[] swapLowest(int[] array)
{
for(int i = 0; i < array.length; i++)
{
int index = i;
for(int k = i + 1; k < array.length; k++)
{
if(array[index] > array[k])
{
index = k;
}
}
int smallestValue = array[index];
array[index] = array[i];
array[i] = smallestValue;
}
return array;
}

Trying to create a array with the intersection of two arrays but fails at creating array with the proper structure

So, I am trying to create 2 randomly generated arrays,(a, and b, each with 10 unique whole numbers from 0 to 20), and then creating 2 arrays with the info of the last two. One containing the numbers that appear in both a and b, and another with the numbers that are unique to a and to b. The arrays must be listed in a "a -> [1, 2, 3,...]" format. At the moment I only know how to generate the 2 arrays, and am currently at the Intersection part. The problem is, that I can create a array with the correct list of numbers, but it will have the same length of the other two, and the spaces where it shouldn't have anything, it will be filled with 0s when its supposed to create a smaller array with only the right numbers.
package tps.tp1.pack2Arrays;
public class P02ArraysExtractUniqsAndReps {
public static void main(String[] args) {
int nbr = 10;
int min = 0;
int max = 20;
generateArray(nbr, min, max);
System.out.println();
}
public static int[] generateArray(int nbr, int min, int max) {
int[] a = new int[nbr];
int[] b = new int[nbr];
int[] s = new int[nbr];
s[0] = 0;
for (int i = 0; i < a.length; i++) {
a[i] = (int) (Math.random() * (max - min));
b[i] = (int) (Math.random() * (max - min));
for (int j = 0; j < i; j++) {
if (a[i] == a[j]) {
i--;
}
if (b[i] == b[j]) {
i--;
}
}
}
System.out.println("a - > " + Arrays.toString(a));
System.out.println("b - > " + Arrays.toString(b));
for (int k = 0; k < a.length; k++) {
for (int l = 0; l < b.length; l++) {
if (a[k] == b[l]) {
s[l] = b[l];
}else {
}
}
}
System.out.println("(a ∪ (b/(a ∩ b)) - > " + Arrays.toString(s));
return null;
}
public static boolean hasValue(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
}
Is there any way to create the array without the incorrect 0s? (I say incorrect because it is possible to have 0 in both a and b).
Any help/clarification is appreciated.
First, allocate an array large enough to hold the intersection. It needs to be no bigger that the smaller of the source arrays.
When you add a value to the intersection array, always add it starting at the beginning of the array. Use a counter to update the next position. This also allows the value 0 to be a valid value.
Then when finished. use Array.copyOf() to copy only the first part of the array to itself, thus removing the empty (unfilled 0 value) spaces. This works as follow assuming count is the index you have been using to add to the array: Assume count = 3
int[] inter = {1,2,3,0,0,0,0};
inter = Arrays.copyOf(inter, count);
System.out.println(Arrays.toString(inter);
prints
[1,2,3]
Here is an approach using a List
int[] b = {4,3,1,2,5,0,2};
int [] a = {3,5,2,3,7,8,2,0,9,10};
Add one of the arrays to the list.
List<Integer> list = new ArrayList<>();
for(int i : a) {
list.add(i);
}
Allocate the intersection array with count used as the next location. It doesn't matter which array's length you use.
int count = 0;
int [] intersection = new int[a.length];
Now simply iterate thru the other array.
if the list contains the value, add it to the intersection array.
then remove it from the list and increment count. NOTE - The removed value must be converted to an Integer object, otherwise, if a simple int value, it would be interpreted as an index and the value at that index would be removed and not the actual value itself (or an Exception might be thrown).
once finished the intersection array will have the values and probably unseen zeroes at the end.
for(int i = 0; i < b.length; i++) {
int val = b[i];
if (list.contains(val)) {
intersection[count++] = val;
list.remove(Integer.valueOf(val));
}
}
To shorten the array, use the copy method mentioned above.
intersection = Arrays.copyOf(intersection, count);
System.out.println(Arrays.toString(intersection));
prints
[3, 2, 5, 0, 2]
Note that it does not matter which array is which. If you reverse the arrays for a and b above, the same intersection will result, albeit in a different order.
The first thing I notice is that you are declaring your intersection array at the top of the method.
int[] s = new int[nbr];
You are declaring the same amount of space for the array regardless of the amount you actually use.
Method Arrays.toString(int []) will print any uninitialized slots in the array as "0"
There are several different approaches you can take here:
You can delay initializing the array until you have determined the size of the set you are dealing with.
You can transfer your content into another well sized array after figuring out your result set.
You could forego using Array.toString, and build the string up yourself.

How to implement compareTo in a generic method that takes a generic array as argument?

I'm trying to implement a method, that, given a generic array, and two index values, slice the array, and find the largest element between the two given numbers.
<T extends Comparable<? super T>> T max(T[] array, int firstIndx, int secondIndx) { //requires comparable
T maxElement = array[0]; //8
System.out.println(Arrays.toString(array));
for (int i = firstIndx; i < secondIndx - 1; i++) {
for (int j = firstIndx + 1; j < secondIndx; j++) {
if (array[i].compareTo(array[j]) > 0) {
maxElement = array[i];
array[i] = array[j];
array[j] = maxElement;
}
}
}
System.out.println(Arrays.toString(array));
return maxElement;
}
But for an arrays of ints [8, 4, 6, 20, 1], is swapping correctly just the first two elements, giving me the wrong maximum elements. What's wrong with the code ?
There are two issues with your sort. The first is that you're using firstIndx and secondIndx, but based on how your code is structured, it's treating that second number as if it were the second index minus 1.
The second issue is that your inner loop is starting back at firstIndx every time, which breaks the bubble sort. It needs to start at i.
Try this modification to your for loops:
for (int i = firstIndx; i <= secondIndx - 1; i++) { // Notice the "<=".
for (int j = i + 1; j <= secondIndx; j++) { // j starts at i
// ... existing bubble sort code goes here
}
}
Edit: I failed to mention that your approach won't find the max if the max is already in its sorted position. You should just grab the max from array[secondIndx] after you're done sorting.
As an aside, firstIndx is a pretty bad variable name. It's only one letter more to write it out in full: firstIndex.

What if the declared variable in for each loop will print outside of the foor loop

I tried for each loop to find duplicates in an array, if I printing the variable "i" outside of the for each loop, it is providing unexpected output.
Expected: Related errors as like the variable is not declared(as the declared variable is local)
package Login;
public class DupsArray {
public static void main(String[] args) {
int[] a = {1, 2, 3, 3};
int length = a.length;
for (int i : a) {
for (int j = i + 1; j <= length - 1; j++) {
if (a[i] == a[j]) {
System.out.println("Found duplicate" + a[i]);
break;
}
System.out.print(i);
}
}
}
}
11Found duplicate3
You are using i to iterate the values (not indexes) of array a and j to iterate indexes.
Suggestion: instead of using an array you can use an ArrayList and make you code much simpler:
Iterate the list and for any item compare array.indexOf(item) with array.lastIndexOf(item) - if they're different you found a duplicate!
I think you should do it without an enhanced for-loop, because index comparison is needed to avoid false positives, e.g. you compare element i == 3 to element a[j] == 3, which might be the same, but how do you want to determine that?
To get around it, you would need an indexOf, so it would boil down to index comparison again.
I would use two classic for-loops and compare the indexes, skipping equal ones:
public static void main(String args[]) throws Exception {
// the source to be investigated
int[] a = {1, 2, 3, 3};
// a container for duplicates found
Set<Integer> dups = new HashSet<>();
// iterate your elements of the source array
for (int i = 0; i < a.length; i++) {
// compare each one to the others
for (int j = i + 1; j < a.length; j++) {
// find out if the elements are equal
if (a[i] == a[j]) {
// if they are, add it to the set of duplicates
dups.add(a[i]);
// as an alternative, you could print them here, too
// System.out.println("Duplicate found: " + a[i]);
}
}
}
// print the duplicates found
System.out.println("Duplicates found: ");
dups.forEach(d -> System.out.println(d));
}
Please read the code comments and note that you don't have to store the duplicates in case you just want to print them. Storing is needed for further processing or printing it some time later, maybe on demand.

Finding Median of Array with Selection Sort

I'm trying to find the median from an unsorted array in Java. First, I need to use the selection sort technique to sort the array, and I cannot use any Java library methods for sorting (so no Arrays.sort(array)). Also, I cannot sort the entire array either. I can only sort as many elements as necessary to find the median of the array. I suppose for an even array, it would be just half of the elements plus one (then find the average of the last two elements), and for an odd array it would just be half of the elements (the last being the median).
So I'm not sure how to stop the selection sort at just the right time and find the median from the last element or two of the partly sorted array. Below is what I have so far.
import java.util.Arrays;
public class EfficientMedian
{
public static void median(int[] values)
{
int i, j, temp;
double median;
//selection sort below
for (i = 0; i < values.length - 1; i++)
{
for (j = i + 1; j < values.length; j++)
{
if (values[i] > values[j])
{
temp = values[i];
values[i] = values[j];
values[j] = temp;
}
}
}
if (values.length % 2 == 0) //if the array is even
{
median = values[values.length/2]; //just a placeholder
}
else //if the array is odd
{
median = values[values.length/2];
}
System.out.println(Arrays.toString(values));
System.out.println(median);
}
public static void main(String[] args)
{
int[] array1 = {567, 2, 600, 6, 601}, array2 = {45, 300, 46, 49};
median(array1);
median(array2);
}
}
Your first loop selects elements to sort. If you only need median, you only need to sort values.length/2 elements. So you should edit this:
for (i = 0; i < values.length - 1; i++)
{
...
}
to
for (i = 0; i < values.length/2; i++)
{
...
}
and fyi in the "length of the array is odd" case, the convention is to average middle two values.

Categories