We have a collection of Comparables held in a bag and have to find the kth largest element. I copied the collection to a HashSet to remove duplicates, then converted the HashSet to an array to be sorted and consequently the kth element accessed. The code compiles, but fails the testing, and I can't figure out what's wrong. Any ideas?
public E kth(int k) {
uniqueSet();
Object[] uniqueArr = hashSet.toArray();
startQuick(uniqueArr);
return (E) uniqueArr[k - 1];
}
private void startQuick(Object[] uniqueArr) {
int i = 0, j = uniqueArr.length;
quickSort(uniqueArr, 0, j);
}
private void quickSort(Object[] uniqueArr, int i, int j) {
int index = partition(uniqueArr, i, j);
if (i < index - 1) {
quickSort(rankBagArr, index - 1, j);
}
if (index < j) {
quickSort(rankBagArr, i, index - 1);
}
}
private int partition(Object[] uniqueArr, int i, int j) {
E tmp;
E pivot = (E) rankBagArr[(i + j) / 2];
while (i <= j) {
while (rankBagArr[i].compareTo(pivot) < 0) {
i++;
}
while (rankBagArr[j].compareTo(pivot) > 0) {
j--;
}
if (i <= j) {
tmp = (E) rankBagArr[i];
rankBagArr[i] = rankBagArr[j];
rankBagArr[j] = tmp;
i++;
j--;
}
}
return i;
}
For a start this part is highly suspect:
if (i < index - 1)
quickSort(rankBagArr, index-1 ,j);
if (index < j)
quickSort(rankBagArr, i, index-1);
Don't you mean:
if (i < index - 1)
quickSort(rankBagArr, i, index-1);
if (index + 1 < j)
quickSort(rankBagArr, index + 1, j);
?
I'm not familiar with your approach to partitioning, so I don't know whether that's correct or not. I think I understand it, and it looks okay on inspection, but it's very easy to get off-by-one errors which are hard to see without careful study.
Here's a partition method I wrote in C# recently - you should be able to translate it into Java quite easily if you want to.
private static int Partition<T>(T[] array, int left, int right,
IComparer<T> comparer) {
// Pivot on the rightmost element to avoid an extra swap
T pivotValue = array[right];
int storeIndex = left;
for (int i = left; i < right; i++) {
if (comparer.Compare(array[i], pivotValue) < 0) {
Swap(array, i, storeIndex);
storeIndex++;
}
}
Swap(array, right, storeIndex);
return storeIndex;
}
static void Swap<T>(T[] array, int x, int y) {
T tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
Any reason for not just using Arrays.sort though?
If you want to solve the problem by sorting, then
Use sorting methods from API (Arrays.sort or Collections.sort). Reinventing the wheel is pointless.
Sort contents of your collection once, not every time you look for k-th element.
The quicksort partitioning is good for finding k-th element without sorting entire collection - you partition, if lowest range is larger then k, you recurrently go with partition to lower range, if it's smaller then k, you go to higher range and look for (k - size of lower range)-th element. It has better complexity than sorting whole collection. You can read more about it here
Anyway, your methods have parameter named uniqueArr, but some operations you perform on rankBagArr. Is it a typo? There is no definition of rankBagArr in your code.
May you could have a bit less of manipulations (and improve performance), and correct the default you are seeing...
Starting with a List (ArrayList), you could ask to sort it (using the comparator, and Collections.sort(list)). Then you could loop down and:
memorizing the last element
if you find the new element is not equals, increment a counter
when your counter reaches the k value, the current element is your target
Related
try {
for (i = 0; i < data.length; i++) {
working[i] = data[i];
}
FileWriter fw3 = new FileWriter("quicksort.dat");
long startTime3 = System.nanoTime();
quickSort(working,0, working.length - 1);
long endTime3 = System.nanoTime();
long totalTime3 = endTime3 - startTime3;
System.out.println("The time to complete the quick sort was :" + totalTime3 + " nano seconds");
for (i = 0; i < working.length; i++) {
fw3.write(Integer.toString(working[i]) + "\n");
}
System.out.println("The file has been sorted by quick sort and output to quicksort.dat \n");
fw3.close();
} catch (IOException e) {
System.out.println(e);
}
static void quickSort(int data[], int left, int right) {
int i, j;
int partition;
if (right > left) {
partition = data[right];
i = left - 1;
j = right;
for (;;) {
while (data[++i] < partition);
while (data[--j] > partition);
if (i >= j) {
break;
}
swap(data[i], data[j]);
swap(data[i], data[right]);
quickSort(data, left, i - 1);
quickSort(data, i + 1, right);
}
}
}
static void swap(int left, int right) {
int array[] = new int[19];
int temp = array[left];
array[left] = array[right];
array[right]=temp;
This is just snippets of the code whole program is other sorts. I get an out bounds error every time it gets down to the quicksort function. I use 19 as my array because that is how big the file is I have tried 20 to see if it fixes it with no luck.
EDIT: I provided updated code down below to reflect changes that were made I still get an out of bounds error.
static void quickSort(int data[], int left, int right) {
int i, j;
int partition;
if (right > left) {
partition = data[right];
i = left - 1;
j = right;
for (;;) {
while (data[++i] < partition);
while (data[--j] > partition);
if (i >= j) {
break;
}
swap(data, i, j);
swap(data, i, right);
quickSort(data, left, i - 1);
quickSort(data, i + 1, right);
}
}
}
static void swap(int array[], int left, int right) {
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
You have declared swap as :
void swap(int left, int right)
So the arguments are the indexes into the array.
However, you call it with :
swap(data[i], data[j]);
So you are passing the VALUES in the array.
Try changing the call to :
swap(i, j);
EDIT TO ADD:
It must be pointed out that the issue above is just ONE issue with the sorting algorithm presented. Another problem is that the swap algorithm does not actually swap anything. It creates a local array variable array, operates on that and then returns (so discarding that local variable).
If your assignment is to output a sorted list of numbers, and that the numbers must be sorted using the sorting code presented - then that assignment is impossible because the sorting code presented is hopelessly flawed as a sorting algorithm; it does NOT sort (as you have found yourself), and has blatant severe issues compared to a correct implementation of quicksort (eg, you can compare to the answer here : https://stackoverflow.com/a/63811974/681444 )
EDIT TO ADD FOLLOWING REVISED CODE:
The revised sorting code is STILL hopelessly busted. It is not a correct implementation of QuickSort, and still does not sort correctly.
For example, I tried running it with the final element being the largest :
int data[] = {23, 8, 19, 35, 2, 12, 7, 64 };
Result : No sorting - the method hit the if (i >= j) {break;} with i==7 and j==6 straight off and so did nothing.
By contrast, the method in the answer I linked to above sorted correctly, so you can compare the method you have been given against that - or against other established articles (eg, do an internet search for java quicksort, there are plenty around)
As already noted by 'racraman', the sort definitely requires fixing.
Try re-implementing the sort function with the following method signature.
void sort(int[] arr, int leftIndex, int rightIndex) {
// swap code goes here
}
I've been looking for a recursive selection sort, using only 2 parameters:
The array that has to be sorted
a value k, which indicates till which
element it has to be sorted.
Example: SelectionSort(array[] a, int k) with a being {6,3,5,7,2} and k being 2 will sort the first 3 elements, and will keep the last elements untouched.
I was thinking about starting with an if-statement for k being 0, and if that was the case, it would just return the array as it is, since you cant sort an array of size 1.
Something like:
public int[] sort(int[] a){
a = selectionSort(a, n-1);
return a;
}
public int[] selectionSort(int[] a, int k){
if (k = 0){
return a;
}
else{
selectionSort(a, k-1 );
... (part i really don't know)
}
I have no clue how to do the 'else' part since I only know that it has to call the method again.
I'm not allowed to create other methods. I also need to make sure I use exactly 2 parameters, nothing more, nothing less.
I have to work it out in pseudocode, but I understand some Java, so if someone could help me by using either pseudo, or Java, it would be so helpful
First some remarks to your code:
Your methods sort and selectionSort don't need to return an int[] array,
since the array object a stays the same all the time.
It is only the content within this array which changes.
Hence, you can use void as return-type.
In your if use (k == 0) instead of (k = 0)
You already figured out the first part.
Here it is how you can do the second part in pseudo code:
public void selectionSort(int[] a, int k) {
if (k == 0) {
return;
}
else {
selectionSort(a, k-1 );
find x such that a[x] is the smallest of a[k] ... a[a.length - 1]
if (a[k-1] > a[x]) {
swap a[k-1] and a[x]
}
}
}
I'm sure you are able to refine the pseudo code to real Java code.
By doing a simple google search, I found the biggest part of the code below on this site. I added the selectionSort method myself to suit your parameters.
public void selectionSort(int a[], int n)
{
recurSelectionSort(a, n, 0);
}
// Recursive selection sort. n is size of a[] and index
// is index of starting element.
static void recurSelectionSort(int a[], int n, int index)
{
// Return when starting and size are same
if (index == n)
return;
// calling minimum index function for minimum index
int k = minIndex(a, index, n-1);
// Swapping when index nd minimum index are not same
if (k != index){
// swap
int temp = a[k];
a[k] = a[index];
a[index] = temp;
}
// Recursively calling selection sort function
recurSelectionSort(a, n, index + 1);
}
// Return minimum index
static int minIndex(int a[], int i, int j)
{
if (i == j)
return i;
// Find minimum of remaining elements
int k = minIndex(a, i + 1, j);
// Return minimum of current and remaining.
return (a[i] < a[k])? i : k;
}
I am trying to write code to determine the n smallest item in an array. It's sad that I am struggling with this. Based on the algorithm from my college textbook from back in the day, this looks to be correct. However, obviously I am doing something wrong as it gives me a stack overflow exception.
My approach is:
Set the pivot to be at start + (end-start) / 2 (rather than start+end/2 to prevent overflow)
Use the integer at this location to be the pivot that I compare everything to
Iterate and swap everything around this pivot so things are sorted (sorted relative to the pivot)
If n == pivot, then I think I am done
Otherwise, if I want the 4 smallest element and pivot is 3, for example, then I need to look on the right side (or left side if I wanted the 2nd smallest element).
-
public static void main(String[] args) {
int[] elements = {30, 50, 20, 10};
quickSelect(elements, 3);
}
private static int quickSelect(int[] elements2, int k) {
return quickSelect(elements2, k, 0, elements2.length - 1);
}
private static int quickSelect(int[] elements, int k, int start, int end) {
int pivot = start + (end - start) / 2;
int midpoint = elements[pivot];
int i = start, j = end;
while (i < j) {
while (elements[i] < midpoint) {
i++;
}
while (elements[j] > midpoint) {
j--;
}
if (i <= j) {
int temp = elements[i];
elements[i] = elements[j];
elements[j] = temp;
i++;
j--;
}
}
// Guessing something's wrong here
if (k == pivot) {
System.out.println(elements[pivot]);
return pivot;
} else if (k < pivot) {
return quickSelect(elements, k, start, pivot - 1);
} else {
return quickSelect(elements, k, pivot + 1, end);
}
}
Edit: Please at least bother commenting why if you're going to downvote a valid question.
This won't fix the issue, but there are several problems with your code :
If you do not check for i < end and j > start in your whiles, you may run into out of bounds in some cases
You choose your pivot to be in the middle of the subarray, but nothing proves that it won't change position during partitioning. Then, you check for k == pivot with the old pivot position, which obviously won't work
Hope this helps a bit.
Alright so the first thing I did was rework how I get my pivot/partition point. The shortcoming, as T. Claverie pointed out, is that the pivot I am using isn't technically the pivot since the element's position changes during the partitioning phase.
I actually rewrote the partitioning code into its own method as below. This is slightly different.
I choose the first element (at start) as the pivot, and I create a "section" in front of this with items less than this pivot. Then, I swap the pivot's value with the last item in the section of values < the pivot. I return that final index as the point of the pivot.
This can be cleaned up more (create separate swap method).
private static int getPivot(int[] elements, int start, int end) {
int pivot = start;
int lessThan = start;
for (int i = start; i <= end; i++) {
int currentElement = elements[i];
if (currentElement < elements[pivot]) {
lessThan++;
int tmp = elements[lessThan];
elements[lessThan] = elements[i];
elements[i] = tmp;
}
}
int tmp = elements[lessThan];
elements[lessThan] = elements[pivot];
elements[pivot] = tmp;
return lessThan;
}
Here's the routine that's calls this:
private static int quickSelect(int[] elements, int k, int start, int end) {
int pivot = getPivot(elements, start, end);
if (k == (pivot - start + 1)) {
System.out.println(elements[pivot]);
return pivot;
} else if (k < (pivot - start + 1)) {
return quickSelect(elements, k, start, pivot - 1);
} else {
return quickSelect(elements, k - (pivot - start + 1), pivot + 1, end);
}
}
I have written a recursive method for a partition sort that sorts the array however when I use an array of more than 10-20 elements the program takes a really long time to complete (On my computer a bubble sort of a 100,000 int array will take about 15-20 seconds but with an array of only 30 ints my partition sort is taking around 45 seconds to be sorted.
Here is the code.
public static int[] partitionSortRecursive(int[] array, int beginning, int end)
{
if (end < beginning)
return array;
int pivot = (array[beginning] + array[end]) / 2;
int firstUnknown = beginning;
int lastS1 = beginning - 1;
int firstS3 = end + 1;
while (firstUnknown < firstS3)
{
if (array[firstUnknown] == pivot)
{
firstUnknown++;
}
else if (array[firstUnknown] > pivot)
{
firstS3--;
int temp = array[firstUnknown];
array[firstUnknown] = array[firstS3];
array[firstS3] = temp;
}
else
{
lastS1++;
int temp = array[firstUnknown];
array[firstUnknown] = array[lastS1];
array[lastS1] = temp;
firstUnknown++;
}
}
partitionSortRecursive(array, 0, lastS1);
partitionSortRecursive(array, firstS3, end);
return array;
}
You do not use the correct pivot element. You calculate the average of the left and right value but you have to take a sample value from the sub array to partition instead.
You may take the rightmost, the center or any other element. So your first line of codes should look like this
int pivot = array[(beginning + end) / 2];
// or
int pivot = array[end];
You could also take any other element (e.g. random)
EDIT: This does not solve the performance issue.
To my understanding, quick sort will divide an array into two sub arrays A and B where all elements in A are smaller than any element in B and then perform the same operation onto the two sub arrays.
So the basic call structure should be like this
void DoSort (array, i, j)
{
pivot = Partition (array, i, j)
DoSort (array, i,pivot)
DoSort (array, pivot + 1, j)
}
Put your implementation is basically
void DoSort (array, i, j)
{
pivot = Partition (array, i, j)
DoSort (array, 0, pivot) // <<<<<< notice the '0' instead of 'i'
DoSort (array, pivot + 1, j)
}
So you always start from the very beginning of the original array which will most likely take a while
Instead of direct recoursive call like this
partitionSortRecursive(array, 0, lastS1);
partitionSortRecursive(array, firstS3, end);
Organize internal stack where you can save index pairs. While the stack is not empty get the next pair from the stack. In the end of function don't call the same function but put in the stack 2 pairs (0, lastS1) and (firstS3, end)
I am facing some problem with the recursive quick sort algorithm. The elements are not sorted in the correct order. The input data are Strings with duplicate entries as well. I am using an integer to differentiate between ascending order( 1 ) and descending order ( -1) sort. The pivot is the mid element. Whenever the duplicate entries are compared( compareTo(String s) will return 0), i compare the current indices and return a negative number if the index of the string being compared is more than the index of the other.I am not sure as to what exactly is going wrong.
Below is the code
// Quick sort Algorithm
public void sort(int left, int right)
{
int i = left, j = right;
String mid;
mid = (String)vect.elementAt((left + right)/2);
while (i <= j)
{
// Compare the elements to the left
while ((i < right) &&
(compare((String)vect.elementAt(i), mid) < 0))
i++;
// Compare the elements to the right
while ((j > left) && (compare((String)vect.elementAt(j), mid) > 0))
j--;
if (i <= j)
{
if(i!=j)
swap(i, j);
i++;
j--;
}
}
// Recursively call the sort method until indices cross.
if(left < j)
sort(left, j);
if(i < right)
sort(i, right);
}
/*
* Compare method to compare the elements.
* type = 1, for ascending sort
* type = -1, for descending sort
*/
public int compare(String firstObj, String secondObj)
{
int resCompare = firstObj.compareTo(secondObj)*type;
if(resCompare == 0)
{
int index_firstObj = vect.indexOf(firstObj);
int index_secObj = vect.indexOf(secondObj);
if(index_firstObj < index_secObj)
return -1;
else
return 1;
}
return resCompare;
}
// Swap the elements at i and j.
public void swap(int i, int j)
{
String tmp1 = (String)vect.elementAt(i);
String tmp2 = (String)vect.elementAt(j);
vect.setElementAt(tmp1, j);
vect.setElementAt(tmp2, i);
}
Example :
input = {"AA","BB","zz","cc","aa","AA","PP","hh" };
Ascending sort
output = {"AA","AA","BB","PP","aa","cc","hh","zz"};
Descending sort
output = {"zz","hh","cc","BB","aa","PP","AA","AA"};
The problem in the algorithm may not work for ascending order sort as well on some other input data. So any help in finding the glitch in the code/logic will be really helpful.Thanks in advance.
Solved:
There is no need to find index_firstObj and index_SecondObj. Just don't do anything if the resCompare is zero.
public int compare(String firstObj, String secondObj)
{
int resCompare = firstObj.compareTo(secondObj)*type;
return resCompare;
}
What if i or j become the mid? and the other one is yet not the mid? doesnt that arise problems also? You might need to check if one of them becomes the mid.