I'm trying to implement QuickSort() using random pivot
When I'm not randomizing pivot, and choosing it to be A[0] everything works fine,
but when I implement the randomization things are going crazy.
Partition:
int Partition(int A[], int left, int right, int pivot = 0)
{
swap(A[left], A[pivot]);
int temp = right;
while (pivot != temp)
{
// make a swap if needed
if (A[pivot] > A[temp] && temp > pivot || A[temp] > A[pivot] && temp < pivot)
{
// swap both data and index
swap(A[pivot], A[temp]);
swap(pivot, temp);
}
// advance the temp towards pivot
if (temp < pivot)
temp++;
else
temp--;
}
return pivot;
}
QuickSort:
void QuickSort(int A[], int left, int right)
{
int pivot;
if (left < right) {
// randomize pivot
srand(time(NULL));
pivot = rand() % (right - left + 1);
// partition and call quicksort
pivot = Partition(A, left, right, pivot);
QuickSort(A, left, pivot - 1);
QuickSort(A, pivot + 1, right);
}
}
Here is an issue:
pivot = rand() % (right - left + 1);
needs to be
pivot = left + rand() % (right - left + 1);
Related
https://leetcode.com/problems/search-in-rotated-sorted-array/
The question requires that the solution be O(log n) and I believe that my solution is O(log n) since my process of finding the smallest element is O(log n) and then using binary search to find the target value is also O(log n). However, my code is exceeding the time limit.
int search(vector<int>& nums, int target) {
if(nums.size() == 0){
return -1;
}
int left = 0;
int right = nums.size() - 1;
while(left < right){
int middle = left + (right - left) / 2;
if(nums[left] < nums[middle]){
left = middle;
}
else{
right = middle;
}
}
if(target >= nums[0]){
return binarySearch(nums, target, 0, left - 1);
}
else{
return binarySearch(nums, target, left, nums.size() - 1);
}
}
int binarySearch(vector<int>& nums, int target, int start, int end){
if(nums.size() == 0 || (start == end && nums[start] != target)){
return -1;
}
int mid = start + (end - start) / 2;
if(nums[mid] == target){
return mid;
}
if(nums[mid] > target){
return binarySearch(nums, target, start, mid - 1);
}
else{
return binarySearch(nums, target, mid, end);
}
}
I believe binarySearch can run into an endless loop. When end = start + 1 you will get mid = start so if nums[start] < target you end up making a recursive call with the same parameters as before.
Link to Problem:- https://www.spoj.com/problems/GSS1
I am using Segment tree.
Node Information:-
maxSum --> maximum sum of the segment represented by node.
Sum ---> Total Sum of segment. (Although i think it's not needed)
maxPrefixSum ---> maximum prefix sum possible in segment. i.e starting from first element of segment.
maxSuffixSum ---> same as maxPrefixSum but starting from last element of segment.
Calculation of Node Values:-
("res" is new node, "left" and "right" are its left and right childs respectively)
res.Sum = left.Sum + right.Sum;
res.maxSum = max(max(left.maxSum, right.maxSum), left.maxSuffixSum + right.maxPrefixSum);
res.maxSuffixSum = max(left.maxSuffixSum + right.Sum, right.maxSuffixSum);
res.maxPrefixSum = max(left.maxPrefixSum, left.Sum + right.maxPrefixSum);
CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long int
class node
{
public:
int Sum, maxSuffixSum, maxPrefixSum, maxSum;
node()
{
Sum = maxSuffixSum = maxPrefixSum = maxSum = 0;
}
};
node node_value(node left, node right)
{
node res;
res.Sum = left.Sum + right.Sum;
res.maxSum = max(max(left.maxSum, right.maxSum), left.maxSuffixSum + right.maxPrefixSum);
res.maxSuffixSum = max(left.maxSuffixSum + right.Sum, right.maxSuffixSum);
res.maxPrefixSum = max(left.maxPrefixSum, left.Sum + right.maxPrefixSum);
return res;
}
void makeSegmentTree(vector<int> arr, vector<node> &tree, int idx, int start, int finish)
{
if(finish == start)
{
tree[idx].Sum = tree[idx].maxPrefixSum = tree[idx].maxSuffixSum = tree[idx].maxSum = arr[start];
return;
}
int mid = start + (finish - start)/2;
makeSegmentTree(arr, tree, 2*idx, start, mid);
makeSegmentTree(arr, tree, 2*idx+1, mid+1, finish);
tree[idx] = node_value(tree[2*idx], tree[2*idx + 1]);
}
node queryTree(vector<node> &tree, int idx, int start, int finish, int left, int right)
{
if(start >= left && finish <= right)
{
return tree[idx];
}
else
{
int mid = start + (finish - start)/2;
if(start <= left && right <= mid)
{
return queryTree(tree, 2*idx, start, mid, left, right);
}
else if(left > mid && right <= finish)
{
return queryTree(tree, 2*idx+1, mid+1, finish, left, right);
}
else
{
node ans1 = queryTree(tree, 2*idx, start, mid, left, mid);
node ans2 = queryTree(tree, 2*idx+1, mid+1, finish, mid+1, right);
return node_value(ans1, ans2);
}
}
}
int32_t main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin>>n;
vector<int> arr(n+1, 0);
for(int i=1;i<n+1;i++)
{
cin>>arr[i];
}
vector< node > tree(4*n);
makeSegmentTree(arr, tree, 1, 1, n);
int q;
cin>>q;
while(q--)
{
int x, y;
cin>>x>>y;
cout<<queryTree(tree, 1, 1, n, x, y).maxSum<<endl;
}
return 0;
}
Take quick sort as an example, both recursion and non-recursion methods are listed below.
I think both methods actually implement the same algorithm, as a stack is used to simulate the recursion process in non-recursion method.
However, I got AC with recursion method but Time Limit Exceeded with non-recursion method after 85% test cases passed.
So, any problem with my non-recursion method or there is a time complexity difference between two methods?
THX!
// non-recursion
void sortIntegers(vector<int> &A) {
if (A.empty()) {
return;
}
stack<pair<int, int>> ranges;
ranges.push(pair<int, int>(0, A.size() - 1));
while (!ranges.empty()) {
pair<int, int> r = ranges.top();
ranges.pop();
int mid = A[r.second],
left = r.first,
right = r.second - 1;
if (r.first >= r.second) {
continue;
}
while (left < right) {
while (A[left] < mid && left < right) {
left++;
}
while (A[right] >= mid && left < right) {
right--;
}
swap(A[left], A[right]);
}
if (A[left] < A[r.second]) {
left++;
} else {
swap(A[left], A[r.second]);
}
ranges.push(pair<int, int>(0, left - 1));
ranges.push(pair<int, int>(left + 1, r.second));
}
// recursion
void sortIntegers(vector<int> &A) {
quick(A, 0, A.size() - 1);
}
void quick(vector<int> & A, int start, int end) {
if (start >= end) {
return;
}
int mid = A[end], //5
left = start, // 0
right = end - 1; //3
while (left < right) {
while (A[left] < mid && left < right) {
left++;
}
while (A[right] >= mid && left < right) {
right--;
}
swap(A[left], A[right]);
}
if (A[left] >= A[end]) {
swap(A[left], A[end]);
}else {
left++;
}
quick(A, start, left - 1);
quick(A, left + 1, end);
}
At first glance, you have at the bottom of your loop:
ranges.push(pair<int, int>(0, left - 1));
ranges.push(pair<int, int>(left + 1, r.second));
To me that should be
ranges.push(pair<int, int>(r.first, left - 1));
ranges.push(pair<int, int>(left + 1, r.second));
Attaching what I've done; having a problem that the recursion sticks on the left recursive calls and can't continue to the right recursive calls. can't figure out how to return the recursion to the first position and continue the program to run towards the right recursive calls.
void minMax(int A[], int left, int right, int &min, int &max)
{
if (right == 0)
return;
if (A[(left + right) / 2] <= min)
min = A[(left + right) / 2];
if (A[((left + right) / 2)] >= max)
max = A[((left + right) / 2)];
if (right > 0)
minMax(A, left, (right - left) / 2, min, max);
if(left < right)
minMax(A, (right - left) / 2, right, min, max);
}
Similar approach as the merge sort :
Here is the recursive solution in C :
a is your array, i and j your left and right ...
void minmax (int* a, int i, int j, int* min, int* max) {
int lmin, lmax, rmin, rmax, mid;
if (i == j) {
*min = a[i];
*max = a[j];
} else if (j == i + 1) {
if (a[i] > a[j]) {
*min = a[j];
*max = a[i];
} else {
*min = a[i];
*max = a[j];
}
} else {
mid = (i + j) / 2;
minmax(a, i, mid, &lmin, &lmax);
minmax(a, mid + 1, j, &rmin, &rmax);
*min = (lmin > rmin) ? rmin : lmin;
*max = (lmax > rmax) ? lmax : rmax;
}
}
Although there are a lot of different and easier solutions ...
Edit by Question asker: ...because the question was about C++ program, this would be the C++ version
void minMax(int a[], int left, int right, int &min, int& max) {
int lmin, lmax, rmin, rmax, mid;
if (left == right)
{
min = a[left];
max = a[right];
}
else if (right == left + 1)
{
if (a[left] > a[right])
{
min = a[right];
max = a[left];
}
else
{
min = a[left];
max = a[right];
}
}
else
{
mid = (left + right) / 2;
minMax(a, left, mid, lmin, lmax);
minMax(a, mid + 1, right, rmin, rmax);
if (lmin > rmin)
min = rmin;
else
min = lmin;
if (lmax > rmax)
max = lmax;
else
max = rmax;
}
}
A simpler recursion is:
void minMax(int& A[], int index, const int& lenght, int& min, int& max)
{
if (A[index] < min) min = A[index]);
else if (A[index] > max) max= A [index];
if (++index < lenght) minMax(A, index, lenght, min, max);
}
In this case you just iterate through recursively, and not left-right.
I've been working on this quicksort code for a while now and cant figure out why I get different results based on where I get an array value. If I get the value of pivot outside of my loop it works fine, inside the loop it does not.
I've highlighted the difference between each function with some /*This works/doesnt*/ comments.
#include <stdio.h>
void quicksort(int * x, int left_limit, int right_limit) {
int left = left_limit;
int right = right_limit;
int pivot = x[(right + left) / 2]; /* This works */
while (left <= right) {
while (x[left] < pivot) { left++; } /* This works */
while (x[right] > pivot){ right--; } /* This works */
if (left <= right) {
int temp = x[left];
x[left] = x[right];
x[right] = temp;
left++;
right--;
}
}
if (left_limit < right)
quicksort(x, left_limit, right);
if (left < right_limit)
quicksort(x, left, right_limit);
}
void quicksortBROKEN(int * x, int left_limit, int right_limit) {
int left = left_limit;
int right = right_limit;
int pivot = (right + left) / 2; /* This doesnt */
while (left <= right) {
while (x[left] < x[pivot]) { left++; } /* This doesnt */
while (x[right] > x[pivot]){ right--; } /* This doesnt */
if (left <= right) {
int temp = x[left];
x[left] = x[right];
x[right] = temp;
left++;
right--;
}
}
if (left_limit < right)
quicksort(x, left_limit, right);
if (left < right_limit)
quicksort(x, left, right_limit);
}
int main() {
int x[] = {0,2,1,4,3,5,6,3,7,8,4,3,7,8};
quicksort(x, 0, 13);
for (int i = 0; i < 14; i++) {
printf("%d, ", x[i]);
}
printf("\n");
return 0;
}
What is wrong with the broken function?
In the broken version the value of the pivot (as opposed to its location) changes over the course of the function.
Debugger would probably show it to you best, but there is a distinct difference between the functions.
The first one takes a value from the array to the variable and doesn't change it.
The second one takes a position and compares the value on that position in the array each time, while modifying the array. If the array is modified on the position that is stored, naturally the comparisons differ.