good day
I am trying to use quick sort with 10000 numbers but it is giving me stack overflow error. it works with random numbers but it does not with descending and ascending numbers.
'
thank you
void quickSort(long* array, long start, long last)
{
if (start < last)
{
int p = partition(array, start, last);
quickSort(array, start, p-1);
quickSort(array, p + 1, last);
}
}
int partition(long* array, long start, long last)//first partition
{
int j = start + 1;
for (long i = start + 1;i <= last;i++)
{
if (array[i] < array[start])
{
swap(array[i], array[j]);
j++;
}
}
swap(array[start], array[j - 1]);
return j - 1;
}
'
For sorted elements, you can avoid this problem by choosing the median of the three elements array[start], array[last] and array[(start + last + 1)/2] as your pivot value.
int median_of_3(long* array, long start, long last)
{
long a = (start + last + 1)/2, b = start, c = last;
if (array[c] < array[a]) swap(array[c], array[a]);
if (array[b] < array[a]) swap(array[b], array[a]);
if (array[c] < array[b]) swap(array[c], array[b]);
return partition(array, start, last);
}
An additional strategy to avoid a large stack depth is to calculate which partition is smaller, and recursively call the smaller one. The other partition can then be optimized into a loop (tail recursion optimization).
void quickSort(long* array, long start, long last)
{
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
quickSort(array, next_start[i], next_last[i]);
/*
* If the compiler does not optimize the tail call below into
* a loop, it is easy to do the optimization manually.
*/
quickSort(array, next_start[!i], next_last[!i]);
}
Introspection can also be used to avoid a large stack depth. You track your recursive call depth, and if it is "too deep", you fail safe into a different sorting strategy, like merge sort or heap sort. This is the behavior currently used by std::sort.
void introSortImpl(long* array, long start, long last, int depth)
{
if (--depth == 0) {
heapSort(array, start, last);
return;
}
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
introSortImpl(array, next_start[i], next_last[i], depth);
introSortImpl(array, next_start[!i], next_last[!i], depth);
}
void introspectionSort(long* array, long start, long last)
{
introSortImpl(array, start, last, log2(start - last) * 3);
}
the code is okay but your compiler uses stack very ineffectively. you just need to raise reserved stack amount. it happens much more often in debug profiles rather than release ones just because compiler preserves large stack chunks to check if stack was broken during execution of your procedure.
Example of Lomuto partition scheme like quicksort that uses recursion on the smaller partition, updates l or r, then loops back to split the larger partition into two partitions, repeating the process. Worst case stack space is O(log2(n)) which should avoid stack overflow. Worst case time complexity is still O(n^2) (depending on how partition is implemented).
Some call this example a half recursion. It's not an example of tail recursion, since tail recursion means that the recursive function just returns after calling itself. The second call in the original question example is a tail call.
void quicksort(int * tab, int l, int r)
{
int q;
while(l < r)
{
q = partition(tab, l, r);
if(q - l < r - q) { // recurse into the smaller partition
quicksort(tab, l, q - 1);
l = q + 1;
} else {
quicksort(tab, q + 1, r);
r = q - 1;
}
} // loop on the larger partition
}
Related
I've been studying to ' Data Abstraction and Problem Solving with C++ ' book at recently, however i did stuck at some point.
I'm at recursion chapter, and i faced with a problem which is ' Finding largest value in an array'. As you know i have to solve this problem with recursion perspective and actually i already solved that with this algorithm ;
Which is basically, start from first item to last item of an array and algorithm is comparing every value with each other and in the and largest item of array stands alone in array(which is invoking the base case)
int largestValue(int anArray[], int first , int last){
int value;
// base case
if(first == last){
value = anArray[first];
}
else if(anArray[first]>anArray[first+1]){
anArray[first + 1] = anArray[first];
value = largestValue(anArray, first + 1, last);
}else{ // anArray[first] < anArray[first + 1]
value = largestValue(anArray, first + 1, last);
}
return value;
BUT , after i read the description of problem, it is said ' you have to solve this problem with multipath recursion.'
For better understanding i'm putting screenshot of problem:
And i couldn't figured out algorithm with ' Multipath Recursion ' perspective.
Split the array in the middle, then call the function for each half:
template<class Random_it>
// typename std::iterator_traits<Random_it>::value_type
auto recursive_max(Random_it first, Random_it last) {
assert(first != last);
if (last - first == 1)
return *first;
const auto mid = first + (last - first) / 2;
const auto l_max = recursive_max(first, mid);
const auto r_max = recursive_max(mid, last);
return (r_max > l_max) ? r_max : l_max;
}
Usage example:
std::vector<int> vec{/* init values */};
const auto max = recursive_max(vec.begin(), vec.end());
Demo
Note that here first and last represent a half-open interval [first, last), in agreement with a convention that is widely used in the C++ standard library.
I would recommend you use a helper function that calls your largestvalue function like so:
int largestValue(int arr[], int size){
int middle = (size - 1)/2;
int first_max = largestValue(arr, 0, middle);
int second_max = largestValue(arr, middle + 1, largest - 1);
if(first_max < second_max){
return second_max;
}
return first_max;
}
The recursive algorithm just splits the array in two and finds the largest value recursively, then compares the two and returns the highest.
What you need to do is to use the boundaries first and last which tell you which part of the array to calculate the largest value of.
A simple solution would be the following:
int largestValue(int anArray[], int first, int last) {
if (first == last) {
return anArray[first];
}
int middle = (first+last)/2;
int left = largestValue(anArray, first, middle);
int right = largestValue(anArray, middle+1, last);
int max;
if (left > right) {
max = left;
} else {
max = right;
}
return max;
}
I'm trying to convert my partition function shown below from choosing the second last element as the partition to a random element. My original idea was to use the rand function something along the lines of
int *pivot = rand() % last;
However i've realised this wont work trying to parse an integer to a pointer integer. Is there any way i can get around this issue.
int *partition(int *first, int *last)
{
int *pivot = last - 1;
int *i = first;
int *j = last - 1;
for (;;)
{
while (comp_less(*i, *pivot) && i < last)
{
++i;
}
while (*j >= *pivot && j > first)
{
--j;
}
if (i >= j)
break;
swap(*i, *j);
}
swap(*(last - 1), *i);
return i;
}
You need to pick a distance in the range [0, std::distance(first, last)), and add that to first.
Note that rand produces low quality pseudorandom numbers, and rand() % N does not uniformly pick numbers.
int * random_pivot(int * first, int * last)
{
thread_local std::mt19937 random_engine(std::random_device{}());
std::uniform_int_distribution<std::ptrdiff_t> range(0, std::distance(first, last) - 1);
return first + range(random_engine);
}
If you are using random choices elsewhere, it would be better to pass random_engine in by reference, and only initialise one (per-thread) std::mt19937.
I'm trying to create a function to compute the sum of elements in the array recursively. I wanted to try the approach of halving the array every iteration.
Here's what I have so far.
int sumRec(int *A, int n, int start, int end)
{
if (start == end){
return A[end];
}
mid = n/2;
return sumRec(A, n, start, mid) + sumRec(A, n, start, mid + 1);
}
Am I on the right track?
Thanks.
You don't need to pass n to the function.That's not needed.
Currently your program will run into an infinite recursion.
You can use
mid = (start+end)/2;
There are many more errors in your code.
Here's a similar code that could do the job
int sumRec(int *A, int start, int end)
{
if (start <= end)
{
int mid = (start+end)/2;
return A[mid] + sumRec(A,start, mid-1) + sumRec(A,mid+1,end);
}
return 0;
}
So, I was trying to implement the binary search algorithm (as generic as possible which can adapt to different cases). I searched for this on the internet, and some use, while (low != high) and some use, while (low <= high) and some other different condition which is very confusing.
Hence, I started writing the code for finding the first element which is greater than a given element. I wish to know if there is a more elegant solution than this?
Main code:
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <stack>
#include <queue>
#include <climits>
#include <set>
#include <cstring>
using namespace std;
int arr1[2000];
int n;
int main (void)
{
int val1,val2;
cin>>n;
for (int i = 0; i < n; i++)
cin>>arr1[i];
sort(arr1,arr1+n);
cout<<"Enter the value for which next greater element than this value is to be found";
cin>>val1;
cout<<"Enter the value for which the first element smaller than this value is to be found";
cin>>val2;
int ans1 = binarysearch1(val1);
int ans2 = binarysearch2(val2);
cout<<ans1<<"\n"<<ans2<<"\n";
return 0;
}
int binarysearch1(int val)
{
while (start <= end)
{
int mid = start + (end-start)/2;
if (arr[mid] <= val && arr[mid+1] > val)
return mid+1;
else if (arr[mid] > val)
end = mid-1;
else
start = mid+1;
}
}
Similarly, for finding the first element which is smaller than the given element,
int binarysearch2(int val)
{
while (start <= end)
{
int mid = start + (end-start)/2;
if (arr[mid] >= val && arr[mid] < val)
return mid+1;
else if (arr[mid] > val)
end = mid-1;
else
start = mid+1;
}
}
I often get super confused when I have to modify binary search for such abstraction. Please let me know if there is simpler method for the same? Thanks!
As you say, there are different ways to express the end condition for binary search and it completely depends on what your two limits mean. Let me explain mine, which I think it's quite simple to understand and it lets you modify it for other cases without thinking too much.
Let me call the two limits first and last. We want to find the first element greater than a certain x. The following invariant will hold all the time:
Every element past last is greater than x and every element before
first is smaller or equal (the opposite case).
Notice that the invariant doesn't say anything about the interval [first, last]. The only valid initialization of the limits without further knowledge of the vector is first = 0 and last = last position of the vector. This satisfies the condition as there's nothing after last and nothing before first, so everything is right.
As the interval [first, last] is unknown, we will have to proceed until it's empty, updating the limits in consequence.
int get_first_greater(const std::vector<int>& v, int x)
{
int first = 0, last = int(v.size()) - 1;
while (first <= last)
{
int mid = (first + last) / 2;
if (v[mid] > x)
last = mid - 1;
else
first = mid + 1;
}
return last + 1 == v.size() ? -1 : last + 1;
}
As you can see, we only need two cases, so the code is very simple. At every check, we update the limits to always keep our invariant true.
When the loop ends, using the invariant we know that last + 1 is greater than x if it exists, so we only have to check if we're still inside our vector or not.
With this in mind, you can modify the binary search as you want. Let's change it to find the last smaller than x. We change the invariant:
Every element before first is smaller than x and every element
after last is greater or equal than x.
With that, modifying the code is really easy:
int get_last_smaller(const std::vector<int>& v, int x)
{
int first = 0, last = int(v.size()) - 1;
while (first <= last)
{
int mid = (first + last) / 2;
if (v[mid] >= x)
last = mid - 1;
else
first = mid + 1;
}
return first - 1 < 0 ? -1 : first - 1;
}
Check that we only changed the operator (>= instead of >) and the return, using the same argument than before.
It is hard to write correct programs. And once a program has been verified to be correct, it should have to be modified rarely and reused more. In that line, given that you are using C++ and not C I would advise you to use the std C++ libraries to the fullest extent possible. Both features that you are looking for is given to you within algorithm.
http://en.cppreference.com/w/cpp/algorithm/lower_bound
http://en.cppreference.com/w/cpp/algorithm/upper_bound
does the magic for you, and given the awesome power of templates you should be able to use these methods by just adding other methods that would implement the ordering.
HTH.
To answer the question in part, it would be possible to factor out the actual comparison (using a callback function or similar), depending on whether the first element which is larger than the element is to be searched or the first element which is smaller. However, in the first code block, you use
arr[mid] <= val && arr[mid+1] > val
while in the second block, the index shift in the second condition
if (arr[mid] >= val && arr[mid] < val)
is omitted, which seems to be inconsistent.
Your search routines had some bugs [one was outright broken]. I've cleaned them up a bit, but I started from your code. Note: no guarantees--it's late here, but this should give you a starting point. Note the "lo/hi" is standard nomenclature (e.g. lo is your start and hi is your end). Also, note that hi/lo get set to mid and not mid+1 or mid-1
There are edge cases to contend with. The while loop has to be "<" or "mid+1" will run past the end of the array.
int
binarysearch_larger(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
int mid;
int lo;
int hi;
int match;
lo = 0;
hi = cnt - 1;
match = -1;
while (lo < hi) {
mid = (hi + lo) / 2;
if (arr[mid] <= val) && (arr[mid+1] > val)) {
if ((mid + 1) < cnt)
match = mid + 1;
break;
}
if (arr[mid] > val)
hi = mid;
else
lo = mid;
}
return match;
}
int
binarysearch_smaller(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
int mid;
int lo;
int hi;
int match;
lo = 0;
hi = cnt - 1;
match = -1;
while (lo < hi) {
mid = (hi + lo) / 2;
if (arr[mid] <= val) && (arr[mid+1] > val)) {
match = mid;
break;
}
if (arr[mid] > val)
hi = mid;
else
lo = mid;
}
// the condition here could be "<=" or "<" as you prefer
if ((match < 0) && (arr[cnt - 1] <= val))
match = cnt - 1;
return match;
}
Below is a generic algorithm that given a sorted range of elements and a value, it returns a pair of iterators, where the value of the first iterator is the first element in the sorted range that compares smaller than the entered value, and the value of the second iterator is the first element in that range that compares greater than the entered value.
If the pair of the returned iterators points to the end of the range it means that entered range was empty.
I've made it as generic as I could and it also handles marginal cases and duplicates.
template<typename BidirectionalIterator>
std::pair<BidirectionalIterator, BidirectionalIterator>
lowhigh(BidirectionalIterator first, BidirectionalIterator last,
typename std::iterator_traits<BidirectionalIterator>::value_type const &val) {
if(first != last) {
auto low = std::lower_bound(first, last, val);
if(low == last) {
--last;
return std::make_pair(last, last);
} else if(low == first) {
if(first != last - 1) {
return std::make_pair(first, std::upper_bound(low, last - 1, val) + 1);
} else {
return std::make_pair(first, first);
}
} else {
auto up = std::upper_bound(low, last, val);
return (up == last)? std::make_pair(low - 1, up - 1) : std::make_pair(low - 1, up);
}
}
return std::make_pair(last, last);
}
LIVE DEMO
template <typename T>
void reverseVector(vector<T> &vec, int start, int end) {
if(start < end) {
char temp = vec[start];
vec[start] = vec[end];
vec[end] = temp;
reverseVector(vec, start + 1, end – 1); }
}
}
Assuming N = vec.size() what would be the time complexity of this method?
Assuming I am correct, a getting and setting a vector has time O(1). Thus, the first 3 lines in the if statement are all O(1) each. Then, the method recursively calls itself and each time the function becomes smaller, iterating n(n-1)(n-2)... times. So my answer would be O(n!) for this method. Am I correct?
edit: similar syntax, but with linked list
template <typename T>
void reverseLinkedList(list<T> &lst, int start, int end) {
if(start < end) {
char temp = lst[start];
lst[start] = lst[end];
lst[end] = temp;
reverseLinkedList(lst, start + 1, end – 1);
}
}
That is O(n).
You swap elements n/2 times: (0 with n-1), (1 with n-2), ... (n/2 - 1 with n/2 + 1)