Background Information:
This is a daily coding problem from Google.
We can determine how "out of order" an array A is by counting the number of inversions it has. Two elements
A[i] and A[j] form an inversion if A[i] > A[j] but i < j.That is, a smaller element appears after a larger element.
Given an array, count the number of inversions it has.Do this faster than O(N ^ 2) time.
You may assume each element in the array is distinct.
For example, a sorted list has zero inversions.The array[2, 4, 1, 3, 5] has three inversions:
(2, 1), (4, 1), and (4, 3).The array[5, 4, 3, 2, 1] has ten inversions : every distinct pair forms
// an inversion.
Brute force solution:
auto num_inversions(std::vector<int>& nums)
{
int count = 0;
for (int i = 0; i <= nums.size(); ++i)
{
for (int j = i + 1; j < nums.size(); ++j)
{
if (nums[i] > nums[j])
++count;
}
}
return count;
}
Potential better solution idea:
My idea is to use a priority_queue to achieve this something like this:
auto num_inversions1(std::vector<int>& nums)
{
auto compare = [](int lhs, int rhs)
{
return lhs > rhs;
};
std::priority_queue<int, std::vector<int>, decltype(compare)> q(compare);
for (auto num : nums)
q.push(num);
print_queue(q);
}
Now if I know how many times the compare lambda was used then I think that will determine the number of inversions I had. Is it possible to count the number of times the lambda expression was used in the priority queue? If so, would this approach work?
Update:
Following the advice from the link provided without looking at the answer too much besides one modifying mergesort I tried it out but I am not getting the correct result.
Here is my code:
#include <iostream>
#include <vector>
int merge(std::vector<int>& data, std::vector<int>& temp, int low, int middle, int high) {
// create a temporary array ... O(N) memory complexity !!!
// copy the data to a temporary array
for (int i = low; i <= high; i++) {
temp[i] = data[i];
}
int i = low;
int j = middle + 1;
int k = low;
int inv_count = 0;
// Copy the smallest values from either the left or the right side back
// to the original array
while ((i <= middle) && (j <= high)) {
if (temp[i] <= temp[j]) {
data[k] = temp[i];
i++;
}
else {
data[k] = temp[j];
j++;
inv_count = inv_count + (middle - i);
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
data[k] = temp[i];
k++;
i++;
}
// Copy the rest of the right side of the array into the target array
while (j <= high) {
data[k] = temp[j];
k++;
j++;
}
return inv_count;
}
int merge_sort(std::vector<int>& data, std::vector<int>& temp, int low, int high)
{
int mid, inv_count = 0;
if (high > low)
{
mid = (low + high) / 2;
inv_count = merge_sort(data, temp, low, mid);
inv_count += merge_sort(temp, temp, mid + 1, high);
inv_count += merge(data, temp, low, mid, high);
}
return inv_count;
}
int sort(std::vector<int>& data, std::vector<int>& temp)
{
return merge_sort(data, temp, 0, data.size() - 1);
}
int main()
{
std::vector<int> data = { 2, 4, 1, 3, 5 };
auto n = data.size();
std::vector<int> temp(n, 0);
std::cout << "The number of inversions is " << sort(data, temp);
std::cin.get();
}
The answer should be 3 but I am getting just 1
Use a counter:
auto num_inversions1(std::vector<int>& nums)
{
int cmpCounter = 0;
auto compare = [&cmpCounter](int lhs, int rhs)
{
cmpCounter++;
return lhs > rhs;
};
std::priority_queue<int, std::vector<int>, decltype(compare)> q(compare);
for (auto num : nums)
q.push(num);
print_queue(q);
}
Related
I tried to implement merge sort using C++, however, something went wrong. I have no idea what is wrong.
The following code is what I wrote based on CLRS. I think it is quite easy to understand the meaning.
#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int>& nums, int p, int q, int r);
void mergeSort(vector<int>& nums, int p, int r){
if (p < r) {
int q = (p + r) / 2;
mergeSort(nums, p, q);
mergeSort(nums, q + 1, r);
merge(nums, p, q, r);
}
}
void merge(vector<int>& nums, int p, int q, int r) {
int s1 = p, s2 = q + 1;
vector<int> l1, l2;
for (int i = s1; i <= q; i++) {
l1.push_back(nums[i]);
}
for (int i = s2; i <= r; i++) {
l2.push_back(nums[i]);
}
int left = 0, right = 0;
int idx = 0;
while (left < l1.size() && right < l2.size()) {
if (l1[left] < l2[right]) {
nums[idx] = l1[left++];
}
else {
nums[idx] = l2[right++];
}
idx++;
}
while (left < l1.size()) {
nums[idx++] = l1[left++];
}
while (right < l2.size()) {
nums[idx++] = l2[right++];
}
}
int main() {
vector<int> vect;
vect.push_back(1);
vect.push_back(3);
vect.push_back(12);
vect.push_back(23);
vect.push_back(4);
vect.push_back(11);
vect.push_back(44);
vect.push_back(322);
mergeSort(vect, 0, vect.size() - 1);
for (int i = 0; i < vect.size(); i++) {
cout << vect[i] << endl;
}
return 0;
}
I want to use the program to sort some integers, however, it only shows many duplicate numbers. What's going on? I don't think there is a problem of the merge function.
The code needs a one line fix:
int idx = p; // not idx = 0
Optimized top down using arrays from Wiki article (note bottom up is slightly faster):
void TopDownMerge(int A[], int bgn, int mid, int end, int B[])
{
int i, j, k;
i = bgn, j = mid, k = bgn;
while(1){
if(A[i] <= A[j]){ // if left smaller
B[k++] = A[i++]; // copy left element
if(i < mid) // if not end of left run
continue; // continue
do // else copy rest of right run
B[k++] = A[j++];
while(j < end);
break; // and break
} else { // else right smaller
B[k++] = A[j++]; // copy right element
if(j < end) // if not end of right run
continue; // continue
do // else copy rest of left run
B[k++] = A[i++];
while(i < mid);
break; // and break
}
}
}
void TopDownSplitMerge(int B[], int bgn, int end, int A[])
{
if (end - bgn <= 1) // if run size == 1
return; // consider it sorted
int mid = (end + bgn) / 2;
TopDownSplitMerge(A, bgn, mid, B);
TopDownSplitMerge(A, mid, end, B);
TopDownMerge(B, bgn, mid, end, A);
}
void TopDownMergeSort(int A[], int n) // n = size (not size-1)
{
if(n < 2)
return;
int *B = new int [n]; // 1 time allocate and copy
for(size_t i = 0; i < n; i++)
B[i] = A[i];
TopDownSplitMerge(B, 0, n, A); // sort data from B[] into A[]
delete B;
}
Afterwards, I finally get to fix the bugs of my program. After modification, here is the code:
class Solution {
public:
vector<int> temp;
vector<int> sortArray(vector<int>& nums) {
temp.resize((int)nums.size(), 0);
mergeSort(nums, 0, nums.size() - 1);
return nums;
}
void mergeSort(vector<int>& nums, int start, int end) {
if (start >= end) return;
int middle = (start + end) / 2;
mergeSort(nums, start, middle);
mergeSort(nums, middle + 1, end);
merge(nums, start, middle, end);
}
void merge(vector<int>& nums, int leftStart, int middle, int rightEnd) {
int leftEnd = middle;
int rightStart = middle + 1;
int i = leftStart, j = rightStart;
int index = 0;
while (i <= leftEnd && j <= rightEnd) {
if (nums[i] < nums[j]) {
temp[index] = nums[i++];
}
else {
temp[index] = nums[j++];
}
index++;
}
while (i <= leftEnd) {
temp[index++] = nums[i++];
}
while (j <= rightEnd) {
temp[index++] = nums[j++];
}
for (int i = 0; i < rightEnd - leftStart + 1; i++) {
nums[i + leftStart] = temp[i];
}
}
};
Here is something should be careful next time:
In the merge part, it is difficult to merge in place. It'd be better to use another temp array to store the merged results and update to the target array (nums in this case).
Readable identifers is very recommended (Although the pseudocode of CLRS may not use that part).
Need to use debuggers to find the bug of program {However, it takes like forever to load local variables of VS Code debugers.
I've run into a problem with my recursive merge sort implementation. The vector I'm feeding to the function gets sorted just fine but then the program terminates with an munmap_chunk(): invalid pointer error.
#include <iostream>
#include <vector>
#include <fstream>
#include <time.h>
#include <stdlib.h>
using namespace std;
// function prototypes
void insertionSort(vector<int> &target, int first, int last);
void mergeSort(vector<int> &target);
void merge(vector<int> &target, vector<int> &left, vector<int> &right); int &i, int &j);
int medianThree(vector<int> &avector, int left, int right);
int ninther(vector<int> &avector, int left, int right);
void printVector(vector<int> &avector);
void generateRandom(vector<int> &avector, int count, char t);
void reverseVector(vector<int> &avector);
int main() {
// initialize random seed from system clock
srand(time(NULL));
vector<int> myVect = { 3, 5, 1, 9, 0, -3, -1, 44, 14, 420, 69, 305, 7 };
mergeCutoff(myVect, 5);
printVector(myVect);
// generates inversely sorted vectors of sizes 10-49 (10 times each) and uses basic quicksort with lazy
// pivot to sort it. after 10 loops at the same vector size, divide the total comparisons/swaps/memory
// costs by 10 and record this average to text file
for (int vectSize = 10; vectSize < 100; vectSize++) {
resetCounters();
for (int experiment = 1; experiment <= 4; experiment++) {
if (experiment == 1) {
for (int loop = 0; loop < 10; loop++) {
vector<int> badVect(vectSize);
generateRandom(badVect, vectSize, 'I');
quickSort(badVect, 0, badVect.size() - 1, 'l');
}
} // end experiment 1
if (experiment == 2) {
for (int loop = 0; loop < 10; loop++) {
vector<int> badVect(vectSize);
generateRandom(badVect, vectSize, 'I');
quickSort(badVect, 0, badVect.size() - 1, 'm');
}
for (int loop = 0; loop < 10; loop++) {
vector<int> badVect(vectSize);
generateRandom(badVect, vectSize, 'I');
quickSort(badVect, 0, badVect.size() - 1, 'n');
}
} // end experiment 2
if (experiment == 3) {
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'R');
quickDual(randVect, 0, randVect.size() - 1);
} // dual pivot runs
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'R');
quickSort(randVect, 0, randVect.size(), 'm');
}
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'R');
quickSort(randVect, 0, randVect.size(), 'n');
}
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'R');
mergeSort(randVect);
} // merge sort runs
} // end experiment 3
if (experiment == 4) {
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'D');
quickDual(randVect, 0, randVect.size() - 1);
} // dual pivot sort runs
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'D');
quickEntropy(randVect, 0, randVect.size() - 1, 'n');
} // three way partition sort runs
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'D');
quickSort(randVect, 0, randVect.size() - 1, 'm');
} // median of threes normal quicksort runs
for (int loop = 0; loop < 10; loop++) {
vector<int> randVect(vectSize);
generateRandom(randVect, vectSize, 'D');
quickSort(randVect, 0, randVect.size() - 1, 'n');
} // ninther pivot normal quicksort runs
}
}
}
return 0;
} // end main method
void insertionSort(vector<int> &target, int first, int last) {
addMem(8);
for (unsigned index = first + 1; index <= last; index++) {
addMem(8);
int currentValue = target[index];
int position = index;
comparisons++;
while (position > first && target[position - 1] > currentValue) {
target[position] = target[position - 1];
position--;
swaps++;
}
target[position] = currentValue;
swaps++;
memory -= 8;
}
memory -= 8;
return;
} // end insertion sort method, some quick sort implementations will switch to this when subarrays get small enough
void mergeSort(vector<int> &target) {
addMem(4);
int size = target.size();
if (size > 1) {
addMem(4);
int mid = size / 2;
addMem(4 * size);
vector<int> leftHalf(target.begin(), target.begin() + mid);
vector<int> rightHalf(target.begin() + mid, target.end());
mergeSort(leftHalf);
mergeSort(rightHalf);
merge(target, leftHalf, rightHalf);
memory -= (4 * size);
}
memory -= 4;
return;
} // end merge sort function
void merge(vector<int> &target, vector<int> &left, vector<int> &right) {
addMem(12);
unsigned targetIndex = 0;
unsigned leftIndex = 0;
unsigned rightIndex = 0;
while (leftIndex < left.size() && rightIndex < right.size()) {
if (left[leftIndex] < right[rightIndex]) {
target[targetIndex] = left[leftIndex];
leftIndex++;
}
else {
target[targetIndex] = right[rightIndex];
rightIndex++;
}
targetIndex++;
} // loop here until one of the lists is exhausted
// then loop through the non-exhausted list adding its remaining elements to the target array
while (leftIndex < left.size()) {
target[targetIndex] = left[leftIndex];
leftIndex++;
targetIndex++;
}
while (rightIndex < right.size()) {
target[targetIndex] = right[rightIndex];
rightIndex++;
targetIndex++;
}
memory -= 12;
} // end merge method, interleaves two vectors into single sorted vector (target)
int medianThree(vector<int> &avector, int left, int right) {
addMem(12);
int center = (left + right) / 2;
comparisons += 3;
if (avector[left] > avector[center]) { swap(avector[left], avector[center]); swaps++; }
if (avector[left] > avector[right]) { swap(avector[left], avector[right]); swaps++; }
if (avector[center] > avector[right]) { swap(avector[center], avector[right]); swaps++; }
swaps++;
swap(avector[center], avector[right]);
memory -= 12;
return avector[right];
} // end helper method to get the median of three of a given (sub)vector, also places first, mid and last items in correct partitions
int ninther(vector<int> &avector, int left, int right) {
addMem(16);
unsigned firstThird = left + ((right - left) / 3);
unsigned secondThird = firstThird + ((right - left) / 3);
medianThree(avector, left, firstThird);
medianThree(avector, firstThird + 1, secondThird);
medianThree(avector, secondThird + 1, right);
memory -= 16;
return medianThree(avector, left, right);
} // end helper method to get the ninther value of an array
void printVector(vector<int> &avector) {
cout << "Vector contains:";
for (int i = 0; i < avector.size(); i++) {
cout << " " << avector[i];
if (i < avector.size() - 1) {
cout << ",";
}
}
cout << " (" << avector.size() << " items)" << endl;
} // end function to print all of a vector's elements to terminal
void generateRandom(vector<int> &avector, int count, char t) {
int min = 0;
int max = 500;
for (int i = 0; i < count; i++) {
int num = (rand() % max) + min;
// if partial sort is selected and current index isn't divisible by 3
// adjust min and max values so those items will be sequentially sorted
if (t == 'I' || (t == 'P' && (i % 3 != 0))) {
min = num;
max += 300;
}
// in any case, add value of num to array at next position
avector[i] = num;
// having reached the end of the inverse-sorted list, flip the list
if (t == 'I' && i == count - 1) { reverseVector(avector); }
}
if (t == 'D') {
for (int i = 0; i < (avector.size() / 3); i++) {
int index = rand() % avector.size();
int index2 = rand() % avector.size();
avector[index] = avector[i];
avector[index2] = avector[i];
}
}
return;
} // modifies vector to populate with [count] random numbers, possibly sorted depending on t value
void reverseVector(vector<int> &avector) {
int last = avector.size() - 1;
for (int i = 0; i < avector.size() / 2; i++) {
swap(avector[i], avector[last - i]);
}
return;
} // helper method to put ascending-ordered vectors in descending order
I used my debugger to see where the issue was occurring and it's happening after the function has finished executing. The new vectors get created just fine, the recursive mergeSort calls go through just fine and the subvectors get sorted appropriately. The merge call at the end works properly and I can see either by printing the vector contents inside the function or by checking my variable tracker in the debugger that the entire list is sorted. And then just before the function returns, execution moves back to the subvectors and throws the munmap error.
I've read on similar posts here that this error has to do with freeing memory pointed to by an invalid pointer. I don't know why my subvectors would be invalid—they don't get destroyed by the merge function, and there are no issues with the subvectors at any recursive level—or what I can do to fix this. Any thoughts?
The function also goes off without an error when called at the top of main.
Apologies for the code dump. The sections I've included earlier were either incomplete or uncompilable. I'm not sure how to whittle down to only the relevant blocks when I don't know where the issue lies. Hopefully this is more useful
My multiway_merge function is incorrect but not sure why. Seems to be correct logically but if the sorting is correct then it must be the merging. I am supposed to make a min heap of the first element of each sorted vector and then add them to the output_list in the correct sorted order and keep doing that until all the numbers have been added to the output_list.
int partition(vector<int>& list, int first, int last) {
// The pivot should be the median of the
// first, middle, and last elements.
int middle = first + (last - first) / 2;
if (list[first] > list[middle])
swap(list[first], list[middle]);
if (list[first] > list[last])
swap(list[first], list[last]);
if (list[middle] > list[last])
swap(list[middle], list[last]);
swap(list[middle], list[first]);
int pivot = list[first];
int i = first - 1;
int j = last + 1;
while(true) {
do {
i++;
} while(list[i] < pivot);
do {
j--;
} while(list[j] > pivot);
if(i >= j)
return j;
swap(list[i], list[j]);
}
}
void quicksort(vector<int>& list, int first, int last) {
if(first < last) {
int pivotLocation = partition(list, first, last);
quicksort(list, first, pivotLocation);
quicksort(list, pivotLocation + 1, last);
}
}
void multiway_merge(vector<vector<int> >& input_lists,
vector<int>& output_list) {
int numLists = (int) (input_lists.size());
int numElements = (int) (input_lists[0].size());
priority_queue<int, vector<int>, greater<int> > minHeap;
for(int i = 0; i < numLists; i++) {
for(int j = 0; j < numElements; j++) {
minHeap.push(input_lists[i][j]);
if (minHeap.size() > numLists) {
output_list.push_back(minHeap.top());
minHeap.pop();
}
}
}
while (minHeap.size()) {
output_list.push_back(minHeap.top());
minHeap.pop();
}
}
int main(int argc, char** argv) {
int n, m;
cin >> n >> m;
vector<vector<int> > input_lists(n, vector<int>(m));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> input_lists[i][j];
}
}
// Quicksort k sublists
for (int i = 0; i < input_lists.size(); ++i)
quicksort(input_lists[i], 0, m-1);
// Merge n input sublists into one sorted list
vector<int> output_list;
multiway_merge(input_lists, output_list);
for(int i = 0; i < output_list.size(); ++i)
cout << output_list[i] << " ";
cout << endl;
}
I couldn't find any logical error with your partition function, Maybe your problem source is in the way that you call this function from sort function and you didn't share that function with us so I had to guess.
Anyway, using your implementation of partition function the quick sort algorithm would be something like this :
#include <iostream>
#include <vector>
#include <algorithm>
int partition(std::vector<int> &vec, int low, int high)
{
int middle = low + (high - low) / 2;
if((vec.at(middle) >= vec.at(low) && vec.at(middle) <= vec.at(high))
|| (vec.at(middle) <= vec.at(low) && vec.at(middle) >= vec.at(high)))
std::swap(vec.at(high), vec.at(middle));
if((vec.at(high) >= vec.at(low) && vec.at(high) <= vec.at(middle))
|| (vec.at(high) <= vec.at(low) && vec.at(high) >= vec.at(middle)))
std::swap(vec.at(low), vec.at(high));
int pivot = vec.at(low);
int i = low - 1, j = high + 1;
while(true)
{
do
{
i++;
} while(vec.at(i) < pivot);
do
{
j--;
} while(vec.at(j) > pivot);
if(i >= j)
return j;
std::swap(vec.at(i), vec.at(j));
}
}
void quickSort(std::vector<int> &vec, int low, int high)
{
if(low < high)
{
int pi = partition(vec, low, high);
quickSort(vec, low, pi);
quickSort(vec, pi + 1, high);
}
}
int main()
{
std::vector<int> input = {10, 7, 15, 9, 1, 33};
quickSort(input, 0, input.size() - 1);
for(auto iter = input.begin(); iter != input.end(); ++iter)
std::cout << *iter << " ";
std::cout << "\n";
system("pause");
return 0;
}
EDIT : Your quick sort wasn't the source of the problem, You have problem merging sorted vectors, so the source of the problem is in logic of your multiway_merge function.
You used priority_queue to achieve this merge and it's a valid approach, but I think you miss understood how priority_queue actually works.
you can enqueue as many elements you want but the order of outgoing elements are determined by the way you set your priority. Take a loot at priority_queue.
Your compare condition to determine priority is right too, I mean using greater<int>. So the only problem in your code is one extra condition that you don't need it at all. I mean these lines of code :
if(minHeap.size() > numLists)
{
output_list.push_back(minHeap.top());
minHeap.pop();
}
Remove this condition and your multiway_merge will work fine. The final implementation of this function would be :
void multiway_merge(vector<vector<int> >& input_lists,
vector<int>& output_list)
{
unsigned int numLists = (int)(input_lists.size());
int numElements = (int)(input_lists[0].size());
priority_queue<int, vector<int>, greater<int> > minHeap;
for(int i = 0; i < numLists; i++)
{
for(int j = 0; j < numElements; j++)
{
minHeap.push(input_lists[i][j]);
}
}
while(minHeap.size())
{
output_list.push_back(minHeap.top());
minHeap.pop();
}
}
I was trying to solve the quick sort - 2 challenge on hackerrank. It said that we had to repeatedly call partition till the entire array was sorted. My program works for some test cases but for some it crashes, "Quick Sort - 2.exe has stopped working". I couldn't find the reason as to why it's happening.
The first element of the array/sub-array was to be taken as pivot element each time.
#include <iostream>
#include <conio.h>
using namespace std;
void swap(int arr[], int a, int b)
{
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
void qsort(int arr[], int m, int n) //m - lower limit, n - upper limit
{
if (n - m == 1)
{
return;
}
int p = arr[m], i, j, t; //p - pivot element, t - temporary
//partition
for (int i = m+1; i < n; i++)
{
j = i;
if (arr[j] < p)
{
t = arr[j];
while (arr[j] != p)
{
arr[j] = arr[j-1];
j--;
}
arr[j] = t; //pivot is at j and j+1
}
}
//check if sorted
int f = 1;
while (arr[f] > arr[f-1])
{
if (f == n-1)
{
f = -1;
break;
}
f++;
}
if (f == -1)
{
cout << "Sub Array Sorted\n";
}
else
{
if (p == arr[m]) //pivot is the smallest in sub array
{
qsort(arr, m+1, n); //sort right sub array
}
else
{
qsort(arr, m, j+1); //sort left sub array
qsort(arr, j+1, n); //sort right sub array
}
}
}
int main()
{
int n;
cin >> n;
int arr[n];
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
qsort(arr, 0, n);
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
return 0;
}
You have an index out of range problem.
This will not give you the solution, but it may help you to find the reason why your program fails.
I have modified your program so it uses a vector of int rather than a raw array of int, and when you run this program you get an index out of range exception.
The sequence 4 3 7 1 6 4 that triggers the problem is hardcoded, so you don't need to type it each time.
#include <iostream>
#include <vector>
using namespace std;
void swap(vector<int> & arr, int a, int b)
{
int c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
void qsort(vector<int> & arr, int m, int n) //m - lower limit, n - upper limit
{
if (n - m == 1)
{
return;
}
int p = arr[m], j, t; //p - pivot element, t - temporary
//partition
for (int i = m + 1; i < n; i++)
{
j = i;
if (arr[j] < p)
{
t = arr[j];
while (arr[j] != p)
{
arr[j] = arr[j - 1];
j--;
}
arr[j] = t; //pivot is at j and j+1
}
}
//check if sorted
int f = 1;
while (arr[f] > arr[f - 1])
{
if (f == n - 1)
{
f = -1;
break;
}
f++;
}
if (f == -1)
{
cout << "Sub Array Sorted\n";
}
else
{
if (p == arr[m]) //pivot is the smallest in sub array
{
qsort(arr, m + 1, n); //sort right sub array
}
else
{
qsort(arr, m, j + 1); //sort left sub array
qsort(arr, j + 1, n); //sort right sub array
}
}
}
int main()
{
vector<int> arr = { 4,3,7,1,6,4 };
qsort(arr, 0, arr.size());
for (unsigned int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
return 0;
}
First of all, what you made is not quick sort, but some combination of divide-ans-conquer partitioning and insert sort.
Canonical quicksort goes from from lower (p) and upper (q) bounds of array, skipping elements arr[p]m respectively. Then it swaps arr[p] with arr[q], increments/decrements and checks if p>=q. Rinse and repeat until p>=q. Then make calls on sub-partitions. This way p or q holds pivot position and subcalls are obvious.
But you are doing it different way: you insert elements from right side of subarray to left side. Such thing can produce O(N^2) time complexity for one iteration. Consider 1,0,1,0,1,0,1,0,1,0,1,0,... sequence, for example. This can increase worst case complexity over O(N^2).
Out of time complexity... The problem in your function lies in assumption that j holds pivot location in subcalls:
qsort(arr, m, j+1); //sort left sub array
qsort(arr, j+1, n); //sort right sub array
Actually, j is set again and again equal to i in your main for loop. If last element is equal or greater than pivot, you end up with j=n-1, the you call qsort(arr, n, n) and first lines check is passed (sic!), because n-n != 1.
To fix this you should do two things:
1) find pivot location directly after rearrange:
for (int i = m; i < n; i++)
if (p == arr[i])
{
j = i;
break;
}
or initialize it in different variable, update after this line:
arr[j] = t; //pivot is at j and j+1
and update recursive calls to use new variable instead of j
2) make a more bulletproof check in the beginning of your function:
if (n - m <= 1)
the latter will be enough to get some result, but it will be much less effective than your current idea, falling down to probably O(N^3) in worst case.
I'm not looking to copy a qsort algorithm. I'm practicing writing qsort and this is what I've come up with and I'm interested in what part of my code is wrong. Please don't tell me that this is homework cause I could just use the code in the link below.
Reference: http://xoax.net/comp/sci/algorithms/Lesson4.php
When this runs I get this in the console:
Program loaded.
run
[Switching to process 10738]
Running…
Current language: auto; currently c++
Program received signal: “EXC_ARITHMETIC”.
void myQSort(int min, int max, int* myArray)
{
// Initially find a random pivot
int pivotIndex = rand() % max;
int pivot = myArray[pivotIndex];
int i = 0 , j = max-1;
// Pointer to begining of array and one to the end
int* begin = myArray;
int* end = &myArray[max-1];
// While begin < end
while( begin < end )
{
// Find the lowest bound number to swap
while( *begin < pivot )
{
begin++;
}
while( *end > pivot )
{
// Find the highest bound number to swap
end--;
}
// Do the swap
swap(begin,end);
}
// Partition left
myQSort(0, pivotIndex-1, myArray);
// Partiion right
myQSort(pivotIndex+1,max, myArray);
}
EDIT--
Code for Swap:
void swap(int* num, int* num2)
{
int temp = *num;
*num = *num2;
*num2 = temp;
}
// sort interval [begin, end)
void myQSort(int* begin, int* end)
{
if(end - begin < 2)
return;
int* l = begin;
int* r = end - 1;
// Initially find a random pivot
int* pivot = l + rand() % (r - l + 1);
while(l != r)
{
// Find the lowest bound number to swap
while(*l < *pivot) ++l;
while(*r >= *pivot && l < r) --r;
// Do the swap
if(pivot == l) { pivot = r; }
std::swap(*l, *r);
}
// Here l == r and numbers in the interval [begin, r) are lower and in the interval [l, end) are greater or equal than the pivot
// Move pivot to the position
std::swap(*pivot, *l);
// Sort left
myQSort(begin, l);
// Sort right
myQSort(l + 1, end);
}
You're not using the min parameter in your code, anywhere. You need to set begin and your pivot value using that.
I tried working out the codes above. But, they don't compile.
#Mihran: Your solution is correct algorithmically but the following line generates an error:
myQSort(min, begin - myArray, myArray);
This is because begin is of type int* and myArray is of type long, following which the compiler shows this error message:
implicit conversion loses integer precision
Here's a working solution in C++:
#include <iostream>
using namespace std;
void mySwap(int& num1, int& num2){
int temp = num1;
num1 = num2;
num2 = temp;
}
void myQsort(int myArray[], int min, int max){
int pivot = myArray[(min + max) / 2];
int left = min, right = max;
while (left < right) {
while (myArray[left] < pivot) {
left++;
}
while (myArray[right] > pivot) {
right--;
}
if (left <= right) {
mySwap(myArray[left], myArray[right]);
left++;
right--;
}
}
if (min < right) {
myQsort(myArray, min, right);
}
if (left < max) {
myQsort(myArray, left, max);
}
}
int main()
{
int myArray[] = {1, 12, -5, 260, 7, 14, 3, 7, 2};
int min = 0;
int max = sizeof(myArray) / sizeof(int);
myQsort(myArray, min, max-1);
for (int i = 0; i < max; i++) {
cout<<myArray[i]<<" ";
}
return 0;
}
Here's a clear C++ implementation, for reference:
#include <iostream>
#include <vector>
using namespace std;
int partition(std::vector<int>& arr, int low, int high) {
// set wall index
int wall_index = low;
int curr_index = low;
int pivot_elem = arr[high]; // taking last element as pivot_element
// loop through the entire received arr
for (int i = curr_index; i < high; ++i) {
// if element is less than or equal to pivot_elem
// swap the element with element on the right of the wall
// i.e swap arr[i] with arr[wall_index]
if (arr[i] <= pivot_elem) {
// swap
int temp = arr[wall_index];
arr[wall_index] = arr[i];
arr[i] = temp;
// move the wall one index to the right
wall_index++;
curr_index++;
} else {
// if the element is greater than the pivot_element
// then keep the wall at the same point and do nothing
curr_index++;
}
}
// need to swap the pivot_elem i.e arr[high] with the element right of the wall
int temp = arr[wall_index];
arr[wall_index] = arr[high];
arr[high] = temp;
return wall_index;
}
void quick_sort(std::vector<int>& arr, int low, int high) {
if (low < high) { // element with single arr always have low >= high
int split = partition(arr, low, high);
quick_sort(arr, low, split-1);
quick_sort(arr, split, high);
}
}
int main() {
std::vector<int> data = {6,13,8,4,2,7,16,3,8};
int N = data.size();
quick_sort(data, 0, N-1);
for (int i : data) {
cout << i << " ";
}
return 0;
}
I don't see a clean implementation of Quicksort on SO, so here is my easy to understand implementation
PLEASE DONT USE IN PRODUCTION CODE
This is only for your understanding
// Swap position a with b in an array of integer numbers
void swap(int *numbers, int a, int b){
int temp = numbers[a];
numbers[a] = numbers[b];
numbers[b] = temp;
}
static int partition(int *data, int low, int high) {
int left = low, right = high, pivot = data[low];
while (left < right) {
// Everthing on the left of pivot is lower than the pivot
while ((left <= right) && data[left] <= pivot) // <= is because left is the pivot initially
left++;
// Everything on the right of the pivot is greater than the pivot
while((left <= right) && data[right] > pivot)
right--;
if (left < right)
swap(data, left, right);
}
// Put the pivot in the 'rigthful' place
swap(data, low, right);
return right;
}
// Quicksort
static void quick_sort(int *numbers, int low, int high)
{
if (high > low) {
int p_index = partition(numbers, low, high);
quick_sort(numbers, low , p_index - 1);
quick_sort(numbers, p_index + 1, high);
}
}