shorten bubble sort loop - java

I have change my bubble sort from implementing the application traverses the entire list during each step of the sort.
This is not necessary since after the first traversal the smallest item will be at the end of the list and after the second traversal, the second smallest item will be in its correct position (2nd last) and so on. i'm a little unsure on what needs to changed exactly.
I need to modify the bubble sort so it doesn't perform unnecessary comparisons.
private void bubbleSort() {
int currentCount = 0;
showStatus("Sorting ...");
boolean swap = true;
while (swap) {
swap = false;
for (int i = 0; i < items.length - 1; i++) {
if (greaterThan(items[i], items[i + 1])) {
swapItems(items[i], items[i + 1]);
swap = true;
currentCount++;
}
} // for
} // while
showStatus("Sort complete, number of swaps = " + currentCount);
} // bubbleSort private void bubbleSort() {

A bubble sort will perform unnecessary comparisons, that's why you don't use it in production for any dataset of any reasonable size. Regarding your code in particular. A simple look at the reference implementation on Rosettacode(because I don't feel like actually writing it again, you only do that in school) looks to show it doing exactly the same number of comparisons. If you want an actually good sorting algorithm for sufficiently large N try merge-sort.
Reference
public static <E extends Comparable<? super E>> void bubbleSort(E[] comparable) {
boolean changed = false;
do {
changed = false;
for (int a = 0; a < comparable.length - 1; a++) {
if (comparable[a].compareTo(comparable[a + 1]) > 0) {
E tmp = comparable[a];
comparable[a] = comparable[a + 1];
comparable[a + 1] = tmp;
changed = true;
}
}
} while (changed);
}

First of all, at the end of first iteration of the for loop the biggest element will be at the end of the array and not the smallest.
If you want to modify your code to save unneeded comparison you can check last comparison and use if as the end of the next for-loop.
Consider the following code: (I hope java allow while(int) - I'm not a java expert...)
integer swap = items.length - 1;
while (swap) {
swap = 0;
integer lastSwap = swap ;
for (int i = 0; i < lastSwap ; i++) {
if (greaterThan(items[i], items[i + 1])) {
swapItems(items[i], items[i + 1]);
swap = i;
currentCount++;
}
} // for
} // while

Related

BinarySearch in a SortedArrayList

I am trying to develop a faster way than what I currently have to add an element to a sorted array list. Currently this is my strategy
public void insertSorted(E value) {
add(value);
for (int i = size() - 1; i > 0 && value.compareTo(get(i - 1)) < 0; i--) {
this.swap(i);
}
}
and my add method...
public void add(E element) {
ensureCapacity();
array[size++] = element;
}
So I read that using a binary search algorithm I could more efficiently find the best way to put an element even faster.
I tried developing that, but somehow it always outputs me 0.
private int binarySearch(E value) {
int low = 0;
int high = this.size()-1;
while (low <= high) {
int mid = (low + high) / 2;
E midVal = this.get(mid);
int cmp = midVal.compareTo(value);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid;
}
return low;
}
public void insertSorted(E value) {
int searchResult = binarySearch(value);
add(value, searchResult);
System.out.println("Value: " + value + ". Position = " + searchResult);
}
Could someone help me out? If necessary I will show full code
Rather than developing your own binary search, use built-in Arrays.binarySearch implementation. However, this wouldn't give you much improvement over your original version in terms of time.
To see why, consider the steps that you take to place the value in the sorted sequence:
Find the insertion position
Move items to the right of insertion position by one
Place the element into insertion position
The first step can be done in O(log2N). The second step takes O(N). The last step takes O(1). Overall, insertion's time complexity is O(log2N + N + 1), which is the same as O(N). The algorithm is dominated by the second step, so you might as well use linear search as you move items to the right by one.

Java Insertion Sort with an Array List of objects?

I have a class Card which contains value (int), suit (String) and faceValue (String). It seems like a regular insertion sort on Card.value should work fine. I just use the whole object when moving things around. For some reason, this crashes and burns. It ends up duplicating the highest card into every element except for a random element that I can't understand.
value, suit, and faceValue are pulic, also.
This is my code:
public static void insertionSort(ArrayList<Card> Array) {
int i,j;
Card key = new Card(0, "","");
for (i = 1; i < Array.size(); i++) {
key.value = Array.get(i).value;
key.suit = Array.get(i).suit;
key.faceValue = Array.get(i).faceValue;
j = i;
while((j > 0) && (Array.get(j - 1).value > key.value)) {
Array.set(j,Array.get(j - 1));
j--;
}
Array.set(j,key);
}
}
I checked this against Wikipedia's pseudo code, and I can't find any fundamental difference. I've been through the debugger a dozen times, and I can't see any reason for the compiler to do what it's doing. Does anyone have an idea why it's not working?
Thanks.
I would like to extend ginz's answer.
Java objects are passed by reference.
So you are changing one object and setting it to multiple indexes.
To visualize (before and after):
For after: Please note that not all indexes must reference to same object. Some of them could remain unchanged.
Better approach would be to move objects, instead of trying to duplicate them.
Also, by Java standard, name of properties (variables) should always start with small letter.
Here is working code:
public static void insertionSort(ArrayList<Card> array) {
int i, j;
for (i = 1; i < array.size(); i++) {
Card tmp = array.get(i);
j = i;
while ((j > 0) && (array.get(j - 1).value > tmp.value)) {
array.set(j, array.get(j - 1));
j--;
}
array.set(j, tmp);
}
}
At every cycle, you insert the object "key" into the list (Array.set(j,key);). So, at the end your whole list will be made of references to the object "key". So when you set key.value, key.suit and key.faceValue at the end, you are setting the fields of every element of your list, because your list consists of references of the same object.
move Card key = new Card(0, "",""); inside the for cycle. Like this:
public static void insertionSort(ArrayList<Card> Array) {
int i,
j;
for (i = 1; i < Array.size(); i++) {
Card key = new Card(0, "","");
key.value = Array.get(i).value;
key.suit = Array.get(i).suit;
key.faceValue = Array.get(i).faceValue;
j = i;
while((j > 0) && (Array.get(j - 1).value > key.value)) {
Array.set(j,Array.get(j - 1));
j--;
}
Array.set(j,key);
}
}
gl with your studies :)
You're setting all the fields of Array(OMG, please rename it!) with the same element: key. So, all the elements would be the same.
The basic algorithm is
for each element
search for the first smaller element going downward
insert element right after that
So, in your case:
public static void insertionSort(ArrayList<Card> cards) {
for (int i = 1; i < cards.size(); i++) {
int value = cards.get(i).value;
j = i;
for (j = i-1; j >= 0; j--) {
if (cards.get(j).value <= key.value) {
break;
}
}
cards.add(j,cards.remove(i));
}
}
One important point here is that at no point does the array contains duplicated values (which happens when you use set)
Use Iterator for getting the elements.
public static void insertionSort(ArrayList<Integer> arrL) {
Iterator<Integer> it = arrL.iterator();
while(it.hasNext()){
int new_element = it.next();
int j = arrL.indexOf(new_element);
while(j>0 && arrL.get(j-1)>new_element){
arrL.set(j, arrL.get(j-1));
j--;
}
arrL.set(j, new_element);
}
}

Insertion sorting an endogenous list of cards

I have a method that sorts a hand of cards in an endogenous doubly linked list.
public void sort(Comparator<Card> cmp) {
// source code that I am adapting my code from
// for (int i=0; i < a.length; i++) {
// /* Insert a[i] into the sorted sublist */
// Object v = a[i];
// int j;
// for (j = i - 1; j >= 0; j--) {
// if (((Comparable)a[j]).compareTo(v) <= 0) break;
// a[j + 1] = a[j];
// }
// a[j + 1] = v;
// }
for(Card f = first; f!=null; f = f.next){
Card insert = f;
for(Card l = last; l!=null || l!= f; l = l.prev)
{
Card compare = l;
if(cmp.compare(compare, insert) <=0)
remove(insert);
this.add(insert);
break;
}
}
// Make sure to test invariant before and after
// You may find it helpful to use "remove" (but watch about size!)
}
When I run this code it is not sorting correctly. Any pointers?
At a quick glance, I see three bugs. There may be more.
Instead of l != null || l != f, I'm pretty sure you want && - the condition as you've written it is always true.
You should use {} to delimit the "true" branch of your if. Currently, this.add(insert); and break; will run whether the if condition is true or false - and your indentation suggests that this isn't what you're expecting.
In this.add(insert), you're not specifying where in the list to add the new node. I would expect to see something that specifies you'll be adding it at the position indicated by l.

algorithm removing duplicate elements in array without auxillay storage

I am working on this famous interview question on removing duplicate elements in array without using auxillary storage and preserving the order;
I have read a bunch of posts; Algorithm: efficient way to remove duplicate integers from an array, Removing Duplicates from an Array using C.
They are either implemented in C (without explanation) or the Java Code provided just fails when there is consecutive duplicates such as [1,1,1,3,3].
I am not quite confident with using C, my background is Java. So I implemented the code myself;
it follows like this:
use two loops, the outer-loop traverses the array and inner loop checks for duplicates and if present replace it with null.
Then I go over the duplicate-replaced-null array and remove null elements and replacing it with the next non-null element.
The total run-time I see now is O(n^2)+O(n) ~ O(n^2). Reading the above posts, I understood this is the best we can do, if no sorting and auxiliary storage is allowed.
My code is here: I am looking for ways to optimize any further (if there is a possibility) or a better/simplisitc logic;
public class RemoveDup {
public static void main (String[] args){
Integer[] arr2={3,45,1,2,3,3,3,3,2,1,45,2,10};
Integer[] res= removeDup(arr2);
System.out.println(Arrays.toString(res));
}
private static Integer[] removeDup(Integer[] data) {
int size = data.length;
int count = 1;
for (int i = 0; i < size; i++) {
Integer temp = data[i];
for (int j = i + 1; j < size && temp != null; j++) {
if (data[j] == temp) {
data[j] = null;
}
}
}
for (int i = 1; i < size; i++) {
Integer current = data[i];
if (data[i] != null) {
data[count++] = current;
}
}
return Arrays.copyOf(data, count);
}
}
EDIT 1; Reformatted code from #keshlam throws ArrayIndexOutofBound Exception:
private static int removeDupes(int[] array) {
System.out.println("method called");
if(array.length < 2)
return array.length;
int outsize=1; // first is always kept
for (int consider = 1; consider < array.length; ++consider) {
for(int compare=0;compare<outsize;++compare) {
if(array[consider]!=array[compare])
array[outsize++]=array[consider]; // already present; advance to next compare
else break;
// if we get here, we know it's new so append it to output
//array[outsize++]=array[consider]; // could test first, not worth it.
}
}
System.out.println(Arrays.toString(array));
// length is last written position plus 1
return outsize;
}
OK, here's my answer, which should be O(N*N) worst case. (With smaller constant, since even worstcase I'm testing N against -- on average -- 1/2 N, but this is computer science rather than software engineering and a mere 2X speedup isn't significant. Thanks to #Alexandru for pointing that out.)
1) Split cursor (input and output advanced separately),
2) Each new value only has to be compared to what's already been kept, and compare can stop if a match is found. (The hint keyword was "incremental")
3) First element need not be tested.
4) I'm taking advantage of labelled continue where I could have instead set a flag before breaking and then tested the flag. Comes out to the same thing; this is a bit more elegant.
4.5) I could have tested whether outsize==consider and not copied if that was true. But testing for it would take about as many cycles as doing the possibly-unnecessary copy, and the majority case is that they will not be the same, so it's easier to just let a possibly redundant copy take place.
5) I'm not recopying the data in the key function; I've factored out the copy-for-printing operation to a separate function to make clear that removeDupes does run entirely in the target array plus a few automatic variables on the stack. And I'm not spending time zeroing out the leftover elements at the end of the array; that may be wasted work (as in this case). Though I don't think it would actually change the formal complexity.
import java.util.Arrays;
public class RemoveDupes {
private static int removeDupes(final int[] array) {
if(array.length < 2)
return array.length;
int outsize=1; // first is always kept
outerloop: for (int consider = 1; consider < array.length; ++consider) {
for(int compare=0;compare<outsize;++compare)
if(array[consider]==array[compare])
continue outerloop; // already present; advance to next compare
// if we get here, we know it's new so append it to output
array[outsize++]=array[consider]; // could test first, not worth it.
}
return outsize; // length is last written position plus 1
}
private static void printRemoveDupes(int[] array) {
int newlength=removeDupes(array);
System.out.println(Arrays.toString(Arrays.copyOfRange(array, 0, newlength)));
}
public static void main(final String[] args) {
printRemoveDupes(new int[] { 3, 45, 1, 2, 3, 3, 3, 3, 2, 1, 45, 2, 10 });
printRemoveDupes(new int[] { 2, 2, 3, 3 });
printRemoveDupes(new int[] { 1, 1, 1, 1, 1, 1, 1, 1 });
}
}
LATE ADDITION: Since folks expressed confusion about point 4 in my explanation, here's the loop rewritten without labelled continue:
for (int consider = 1; consider < array.length; ++consider) {
boolean matchfound=false;
for(int compare=0;compare<outsize;++compare) {
if(array[consider]==array[compare]) {
matchfound=true;
break;
}
if(!matchFound) // only add it to the output if not found
array[outsize++]=array[consider];
}
Hope that helps. Labelled continue is a rarely-used feature of Java, so it isn't too surprising that some folks haven't seen it before. It's useful, but it does make code harder to read; I probably wouldn't use it in anything much more complicated than this simple algorithm.
Here one version which doesn't use additional memory (except for the array it returns) and doesn't sort either.
I believe this is slightly worse than O(n*log n).
Edit: I'm wrong. This is slightly better than O(n^3).
public class Dupes {
private static int[] removeDupes(final int[] array) {
int end = array.length - 1;
for (int i = 0; i <= end; i++) {
for (int j = i + 1; j <= end; j++) {
if (array[i] == array[j]) {
for (int k = j; k < end; k++) {
array[k] = array[k + 1];
}
end--;
j--;
}
}
}
return Arrays.copyOf(array, end + 1);
}
public static void main(final String[] args) {
System.out.println(Arrays.toString(removeDupes(new int[] { 3, 45, 1, 2, 3, 3, 3, 3, 2, 1, 45, 2, 10 })));
System.out.println(Arrays.toString(removeDupes(new int[] { 2, 2, 3, 3 })));
System.out.println(Arrays.toString(removeDupes(new int[] { 1, 1, 1, 1, 1, 1, 1, 1 })));
}
}
and here's a modified version which doesn't shift all of the elements from after the dupe. Instead it simply switches the dupe with the last, non-matching element. This obviously can't guarantee order.
private static int[] removeDupes(final int[] array) {
int end = array.length - 1;
for (int i = 0; i <= end; i++) {
for (int j = i + 1; j <= end; j++) {
if (array[i] == array[j]) {
while (end >= j && array[j] == array[end]) {
end--;
}
if (end > j) {
array[j] = array[end];
end--;
}
}
}
}
return Arrays.copyOf(array, end + 1);
}
Here you have a worst case of O(n^2) where the return points to the first non unique element. So everything before it is unique.
Instead of C++ iterators indices in Java can be used.
std::vecotr<int>::iterator unique(std::vector<int>& aVector){
auto end = aVector.end();
auto start = aVector.begin();
while(start != end){
auto num = *start; // the element to check against
auto temp = ++start; // start get incremented here
while (temp != end){
if (*temp == num){
std::swap(temp,end);
end--;
}
else
temp++; // the temp is in else so that if the swap occurs the algo should still check the swapped element.
}
}
return end;
}
Java equivalent code: (the return will be an int which is the index of the first not unique element)
int unique(int[] anArray){
int end = anArray.length-1;
int start = 0;
while(start != end){
int num = anArry[start]; // the element to check against
int temp = ++start; // start get incremented here
while (temp != end){
if (anArry[temp] == num){
swap(temp,end); // swaps the values at index of temp and end
end--;
}
else
temp++; // the temp is in else so that if the swap occurs the algo should still check the swapped element.
}
}
return end;
}
The slight difference in this algo and yours is in your point 2. Where instead of replacing the current element with null you go with swapping it with the last possibly unique element which on the first swap is the last element of array, on second swap the second last and so on.
You might as well consider looking at the std::unique implementation in C++ which is linear in one less than the distance between first and last: Compares each pair of elements, and possibly performs assignments on some of them., but as it was noted by #keshlam it is used on sorted arrays only. The return value is the same as in my algo. Here is the code directly from the standard library:
template<class _FwdIt, class _Pr> inline
_FwdIt _Unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
{ // remove each satisfying _Pred with previous
if (_First != _Last)
for (_FwdIt _Firstb; (_Firstb = _First), ++_First != _Last; )
if (_Pred(*_Firstb, *_First))
{ // copy down
for (; ++_First != _Last; )
if (!_Pred(*_Firstb, *_First))
*++_Firstb = _Move(*_First);
return (++_Firstb);
}
return (_Last);
}
To bring in a bit perspective - one solution in Haskell, it uses lists instead of arrays
and returns the reversed order, which can be fixed by applying reverse at the end.
import Data.List (foldl')
removeDup :: (Eq a) => [a] -> [a]
removeDup = foldl' (\acc x-> if x `elem` acc then acc else x:acc) []

Binary Search w/o Library Methods

I am trying to construct a binarySearch method that goes through a sorted array, and evaluates whether a given element, given as int target, is present. It will do so by evaluating whether the mean value is greater or less than the target value, and will loop through the first or second half of the array accordingly.
I think I have the basic code down, but I am running into some problems:
int target = 0; (returns true) => correct
int target = (3, 6, 9); (returns false) => should return true
int target = (15, 19, 21, 90); returns "java.lang.ArrayIndexOutOfBoundsException: 15" => should be true
I imagine it has to do with my for statements in the respective if cases, but I have tried to debug and cannot. Also, I not permitted to use library methods.
Hopefully this question is helpful for other beginners like me. I would think it explores some java concepts like syntax, logic, and basic use Thanks for the help.
public class ArrayUtilities
{
public static void main(String[] args)
{
int[] arrayBeingSearched = {0, 3, 6, 9, 12, 15, 19, 21, 90};
int target = 90;
System.out.println("linear: " + linearSearch(arrayBeingSearched, target));
System.out.println("binary: " + binarySearch(arrayBeingSearched, target));
}
public static boolean binarySearch(int[] arrayBeingSearched, int target)
{
boolean binarySearch = false;
for (int i = 0; i < arrayBeingSearched.length; i++){
int left = 0; //array lower bound
int right = arrayBeingSearched.length - 1; //array upper bound
int middle = ((right - left) / (2)); //array mean
if(arrayBeingSearched[middle] == target){
binarySearch = true;
}
else if(arrayBeingSearched[middle] < target){
for(int j = middle + 1; j < arrayBeingSearched.length - 1; j ++){
int newLeft = arrayBeingSearched[j ++];
if(arrayBeingSearched[newLeft] == target){
binarySearch = true;
break;
}
else{
binarySearch = false;
}
}
}
else if(arrayBeingSearched[middle] > target)
for(int l = 0; l < middle - 1; l ++){
int newRight = arrayBeingSearched[l ++];
if(arrayBeingSearched[newRight] == target){
binarySearch = true;
break;
}
else{
binarySearch = false;
}
}
else{
binarySearch = false;
}
}
return binarySearch;
}
}
Okay, based on the comments, would this be a better representation? The first comment answered my question mostly but I just wanted to follow up:
public static boolean binarySearch(int[] array, int target)
{
int start = 0;
int end = array.length - 1;
while (start <= end)
{
int middle = start + (end - start)/2;
if (array[middle] == target) {
return true;
}
else if (array[middle] > target)
{
end = middle - 1;
}
else start = middle + 1;
}
return false;
}
}
This is a bad start:
for (int i = 0; i < arrayBeingSearched.length; i++)
That's a linear search, with something else within it. I haven't followed exactly what you're doing, but I think you should probably start again... with a description of binary search in front of you.
Typically a binary search loop looks something like:
int left = 0; // Inclusive lower bound
int right = arrayBeingSearch.length; // Exclusive upper bound
while (left < right) {
// Either change left, change right, or return success
// based on what you find
}
When your middle element is smaller than the target, you do this
int newLeft = arrayBeingSearched[j ++];
if(arrayBeingSearched[newLeft] == target) //...
And the equivalent when it's larger.
That is, you are taking an element of the array and using it as an index. Your array could contain only one element with a value of 1000, which is why you're running into an ArrayIndexOutOfBoundsException.
I'm sure there are other problems (see Jon's answer), but I wanted to mention that code like this:
for(int j = middle + 1; j < arrayBeingSearched.length - 1; j ++){
int newLeft = arrayBeingSearched[j ++];
will not do what you want. The for statement says that each time the program goes through the loop, it will add 1 to j (at the end of the loop code). But the next statement will use j as an index and then add 1 to it. The result is that each time you go through the loop, 1 will be added to j twice, so you're basically looking only at every other element. If this were otherwise correct (which I don't think it is), I'd say you definitely need to remove the ++ from the second line.

Categories

Resources