I am trying to make a merge sort algorithm with the stl library but am having some issues. Below is the code I am using
template <typename Item, typename SizeType>
void merge_sort(Item array[], SizeType size){
size_t n1; //Size of the first subarray
size_t n2; //Size of the second subarray
if(size > 1){
//Compute the size of the subarrays
n1 = size/2;
n2 = size - n1;
//create the temp array.
int* n1Temp = new int[n1];
int* n2Temp = new int[n2];
int i;
for(i = 0; i < n1; i++)
n1Temp[i] = array[i];
for(i = 0; i < n2; i++)
n2Temp[i] = array[i + n1];
//recursive calls
merge_sort(n1Temp, n1);//sort from array[0] through array[n1 - 1]
merge_sort(n2Temp, n2);//sort from array[n1] to the end
//Merge the two sorted halves.
vector<int> v(array, array + size);
merge(n1Temp, n1Temp + n1, n2Temp, n2Temp + n2, v.begin());
copy(v.begin(), v.end(), array);//copy the vector back to the array
delete[] n1Temp;
delete[] n2Temp;
}
}
The code sorts fine but the problem is that it acts like a O(n^2) algorithm instead of O(n \log n), which is due to the creation of the vector in each merge sort call (I think). I tried removing the vector and just using an array in the merge function which can be seen below
//Merge the two sorted halves.
int* finalArray = new int[n1 + n2];
merge(n1Temp, n1Temp + n1, n2Temp, n2Temp + n2, begin(finalArray));
array = finalArray;
But this gets me nothing but errors. Is there any thing I can do to salvage my merge sort algorithm?
As both Vaughn and user93353 pointed out, you should be able to merge directly into the target array at each merge-point. But you can still use std::vector<> to make this significantly easier on yourself.
Also, your temp arrays are of direct type 'int', and I'm fairly sure that was intended to be the type of the template parameter Item. I'm not sure what the SizeType parameter is for, but I left it in case you had special ideas for it. Whatever it is, it better be compatible with size_t :
template <typename Item, typename SizeType>
void merge_sort(Item array[], SizeType size)
{
if(size > 1)
{
//Compute the size of the subarrays
size_t n1 = size/2;
//create the temp array
std::vector<Item> n1Temp(array, array+n1);
std::vector<Item> n2Temp(array+n1, array+size);
//recursive calls
merge_sort(&n1Temp[0], n1); //sort array[0] through array[n1-1]
merge_sort(&n2Temp[0], size-n1); //sort array[n1] through array[size-1]
// merge the sorted halves
std::merge(n1Temp.begin(), n1Temp.end(),
n2Temp.begin(), n2Temp.end(), array);
}
}
The above technique splits the sub-sequences top-down via copy, then merges in-place the split-copies into the original array. You can reduce this algorithm by one sublist allocation time (but no less space) by doing the splits on the original array, then merging into temp-space and copying after, which i think you were trying to do in the first place:
template <typename Item>
void merge_sort(Item ar[], size_t n)
{
if (n > 1)
{
// Compute the size of the subarrays
size_t n1 = n/2;
// invoke recursion on the submerges
merge_sort(ar, n1); //sort array[0] through array[n1-1]
merge_sort(ar+n1, n-n1); //sort array[n1] through array[size-1]
// create merge-buffer
std::vector<Item> mrg;
std::merge(ar, ar+n1, ar+n1, ar+n, back_inserter(mrg));
std::copy(mrg.begin(), mrg.end(), ar);
}
}
General Iterator-Based Solution
For a general solution that allows even more flexibility you can define your merge-sort based on iterators rather than Item pointers. It gets a little more hairy, but the benefits are very std-lib-ish.
template <typename Iterator>
void merge_sort(Iterator first, Iterator last)
{
typedef typename std::iterator_traits<Iterator>::value_type value_type;
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
difference_type n = std::distance(first, last)/2;
if (n == 0)
return;
// invoke recursion on the submerges
merge_sort(first, first + n);
merge_sort(first + n, last);
// create merge-buffer
std::vector<value_type> mrg(std::distance(first, last));
std::merge(first, first+n, first+n, last, mrg.begin());
std::copy(mrg.begin(), mrg.end(), first);
}
Finally, if you find yourself sorting a ton of fixed-length C-arrays you may find the following helpful (it uses the general-iterator solution above):
// front-loader for C arrays
template<typename Item, size_t N>
void merge_sort(Item (&ar)[N])
{
merge_sort(std::begin(ar), std::end(ar));
}
It make the following code rather convenient:
int arr[1024];
... fill arr ...
merge_sort(arr);
Related
I am trying to merge two sorted arrays using pointers. The third loop is working food upto first arr[5] but then show garbage values afterwards. is the sorting techniqure wrong or am I using pointers incorrectly?
#include<iostream>
using namespace std;
int main() {
int arr1[] = { 1,3,4,6 };
int arr2[] = { 2,3,4,5 };
int n1 = 4;
int n2 = 4;
int arr3[10];
int* endptr = &arr1[0];
int* endptr2 = &arr2[0];
int* endptr3 = arr3;
int k = 0;
while (endptr < &arr1[n1-1] &&endptr2 < &arr2[n2-1]) {
if (endptr[0] < endptr2[0]) {
endptr3[k++] = endptr[0];
endptr++;
}
else {
endptr3[k++] = endptr2[0];
endptr2++;
}
}
while (endptr < &arr1[n1 - 1]) {
endptr3[k++] = endptr[0];
endptr++;
}
while (endptr2 < &arr2[n2 - 1]) {
endptr3[k++] = endptr[0];
endptr2++;
}
cout << arr3[5];
}
I got the results up to first 6 elements and then get garbage values.
The way you are using the pointers is negatively affecting your thought process.
Let’s abstract it to a function to help make it cleaner:
void merge(
int * first1, int * last1, // elements of array1
int * first2, int * last2, // elements of array2
int * dest ) // where to put sorted data
This is the “iterator” idiom used by C++ algorithms. first points to the first element. last points to one-past the last element. (And we assume that dest points to enough space to contain the results.) So:
int array1[] = { 1,3,4,6 };
int array2[] = { 2,3,4,5,7 };
int array3[9]; // enough room for array1+array2
merge(
array1, array1+4, // first array has 4 elements
array2, array2+5, // second array has 5 elements
array3 ); // destination array has 4+5=9 elements
C++ actually gives us a couple of functions to get the first and last pointers to whatever underlying sequence container you’ve got:
merge(
std::begin(array1), std::end(array1),
std::begin(array2), std::end(array2),
std::begin(array3) );
Now we can reconsider our merge algorithm.
void merge(...)
{
// while both array1 and array2 have elements:
while ((first1 != last1) and (first2 != last2))
{
if (*first2 < *first1)
*dest++ = *first2++;
else
*dest++ = *first1++;
}
// if array1 has any leftover elements:
while (first1 != last1)
*dest++ = *first1++;
// if array2 has any leftover elements:
while (first2 != last2)
*dest++ = *first2++;
}
In particular, notice:
We can easily check whether an array has elements by comparing the first and last pointers directly. When first == last, there are no elements, since last is one-past the end of the source array.
Comparison is simple enough. We could have written that as if (first2[0] < first1[0]) but that is less idiomatic than just using the * operator.
Copying a value is as simple as a dereference and increment on both sides of the assignment.
The three loops of the merge all work on a simple “are there elements left?” check.
Note also how we kept the test condition to a strict less-than comparison. That has ramifications for generic programming (for example, merge sorting in non-increasing order), but I’ll leave it at that for today...
The C++ function code I wrote:-
values of n=5(no. of elements in array) and d=4(no. of elements by which array is to left rotated)
vector<int> rotateLeft(int d, vector<int> arr)
{
int n=arr.size();
vector<int> temp;
for(int i=0;i<d;i++)
{ temp[i]=arr[i]; }
for(int j=d;j<n;j++)
{ arr[j-d]=arr[j]; }
for(int k=0;k<d;k++)
{ arr[n-d-k]=temp[k];
}
return arr;
}
input given arr[]= 1 2 3 4 5
output required arr[]=5 1 2 3 4
You get Segmentation fault for two reasons:
You have to resize temp to match arr before accessing the elements with the square brackets either with std::vector<int> temp(n); or calling temp.resize(n) afterwards.
You have made an indexing error which results in an access that is out of bounds: arr[n-d-k]=temp[k] should actually be arr[n-d+k]=temp[k]. In order to avoid this you might try using the .at(i) operator instead: It performs an out-of-bound check.
Try it here.
There is also a function in the standard library for that called std::rotate.
std::rotate(arr.begin(), arr.begin() + d, arr.end());
Try it here.
If I had to write my own version quickly I would actually do something like
template <typename T>
std::vector<T> rotateLeft(std::vector<T> const& vec, int const d) {
std::size_t const N = vec.size();
std::vector<T> temp;
temp.resize(N);
for (std::size_t i = 0; i < N; ++i) {
std::size_t const new_i = (N + i - d)%N;
temp.at(new_i) = vec.at(i);
}
return temp;
}
Try it here.
arr[n-d-k]=temp[k];
should have been
arr[n-d+k]=temp[k];
I am trying out some codes that is based on finding all possible combinations that add up to a integer's value that is declared in the main function. However, the problem is when I call the function "findCombinations(n);", it gives an error at "int arr[n];". That is the only line which has an error which is stopping me from running the program. If you know of a solution, do let me know.
#include <iostream>
using namespace std;
void findCombinationsUtil(int arr[], int index,
int num, int reducedNum)
{
// Base condition
if (reducedNum < 0)
return;
// If combination is found, print it
if (reducedNum == 0)
{
for (int i = 0; i < index; i++)
cout << arr[i] << " ";
cout << endl;
return;
}
// Find the previous number stored in arr[]
// It helps in maintaining increasing order
int prev = (index == 0) ? 1 : arr[index - 1];
// note loop starts from previous number
// i.e. at array location index - 1
for (int k = prev; k <= num; k++)
{
// next element of array is k
arr[index] = k;
// call recursively with reduced number
findCombinationsUtil(arr, index + 1, num,
reducedNum - k);
}
}
void findCombinations(int n)
{
// array to store the combinations
// It can contain max n elements
int arr[n];
//find all combinations
findCombinationsUtil(arr, 0, n, n);
}
int main()
{
int n = 10;
findCombinations(n);
return 0;
}
C-style array dimensions must be known at compile-time in Standard C++.
You can make n be a compile-time function parameter like this:
template<int n>
void findCombinations()
{
// array to store the combinations
// It can contain max n elements
int arr[n];
//find all combinations
findCombinationsUtil(arr, 0, n, n);
}
int main()
{
const int n = 10;
findCombinations<n>();
return 0;
}
From http://www.cplusplus.com/doc/tutorial/arrays/:
NOTE: The elements field within square brackets [], representing the number of elements in the array, must be a constant expression, since arrays are blocks of static memory whose size must be determined at compile time, before the program runs.
While some compilers will allow it, you should avoid dynamic size arrays.
Here are a few options:
If the size of the array will always be 10, initiate it to hard-coded const 10.
Use std::shared_ptr to an array pointer:
std::shared_ptr pArray;
pArray=std::make_shared(n)
Use std::vector to dynamically allocate the size. (IMHO this is the preferred option).
use c-style pointers (IMHO should only be used as last resort)
Template class (wasteful, as it created and compiles many instances of the same function)
Type of variable arr must be known at compile time. If you need storage of variable size, you have to allocate it.
Possible alternative (one of many)
#include <vector>
void findCombinations(int n)
{
// array to store the combinations
// It can contain max n elements
std::vector<int> arr(n); // allocate n elements
//find all combinations
findCombinationsUtil( &*arr.begin(), 0, n, n);
}
if compiler at least partially complies to C++11 e.g. it's late gcc 4.6 or higher or VS2010 and higher, then there is method data() that returns pointer to internal storage. But better to rewrite, templatize or overload findCombinationsUtil to use a container or iterator
I'm learning C++ and am playing around with searching/sorting algorithms.
I am trying to do binary search on an unsorted list of items and return the original array index. I have to sort it first, so to preserve the original indices, I created a 2D array, put the data in the first column and the original indices in the second. (I pared it down a little, which is why I had to put //insert data items here.)
template <class TYPE>
int SomeClass<TYPE>::find(TYPE data)
{
TYPE(*ary)[2] = new TYPE[size()][2];
for (int i = 0; i < size(); i++)
{
ary[i][0] = //insert data items here;
ary[i][1] = i;
}
//reading the data items and indexes into the new 2D array works
someSort(ary, size());
return bsearch(ary, 0, size()-1, data);
}
Assume you have some sorting algorithm. As an example, I'll just put bubble sort below because it doesn't take up much space to write.
template <class DT>
void SomeClass<TYPE>::someSort(TYPE A[][2], int n)
{
int i, j;
for (i = 0; i < n- 1; i++)
{
for (j = 0; j < n- i - 1; j++)
{
if (A[j] > A[j + 1])
swap(&A[j][0], &A[j + 1][0]);
}
}
}
Alright, so my question is how would you modify binary search for a 2D array and return the original value (which is in the second slot of the array)? (Feel free to modify my sorting example, too, in case there are issues there.)
template <class TYPE>
int SomeClass<TYPE>::bsearch(TYPE A[][2], int left, int right, TYPE data)
{
while (right>= left)
{
int mid = left + (right+ left) / 2;
if (A[mid][0] == data)
return A[mid][1];
if (A[mid][0] > data)
right= mid- 1;
else
left= mid+ 1;
}
return -1;
}
You need to check your binary search implementation, as it's not correct. Here's improved version:
template <class TYPE>
int SomeClass<TYPE>::bsearch(TYPE A[][2], int left, int right, TYPE data)
{
while (right>= left)
{
int mid = left + (right - left) / 2; // <== corrected here.
if (A[mid][0] == data)
return A[mid][1];
if (A[mid][0] > data)
right= mid- 1;
else
left= mid+ 1;
}
return -1;
}
While learning it's better to code algorithms manually, but you should know that C++ provides implementation for common algorithms like binary search or quick/merge sort.
For example, you could define your value-pair struct vp that holds value and position in original array:
struct vp
{
TYPE val;
int pos;
bool operator<(const vp& vp) const {
return val < vp.val;
}
static bool cmp(TYPE b, const vp& a) {
return b < a.val;
}
};
then you can sort array of value-pairs using std::sort:
vp *arr = ...
std::sort(arr, arr+size());
then, you can use lower_bound or upper_bound to do binary search in your array of value-pairs:
auto p = std::upper_bound(arr, arr+size(), data, vp::cmp);
But you'll need to interpret properly returned value to figure out if value was found in the array or not.
I have a linked list of arrays (struct at bottom of post)
Each array may have values like the below example
Array1[] = {6,36,8,23};
Array2[] = {8,23,5,73};
Array3[] = {2,5,1,9};
I need to sort these so that all 3 arrays are treated as 1 large array...
I need to use quicksort so that it uses in-place processing... I am working with very large arrays and cannot afford to use additional memory..
The result should be something like this
Array1[] = {1,2,5,5};
Array2[] = {6,8,8,9};
Array3[] = {23,23,36,73};
Currently i am only able to sort each array individually... but thats not exactly what i need :(
struct iSection {
unsigned long Section_Count; // Total # of points in this block of memory
int *Section_Arr; // Point cloud for current block of memory
struct iSection *Next; // Pointer to next section
} iSection;
struct iDatabase {
struct iSection *First_Section;
struct iSection *Last_Section;
} iDatabase;
It's not that hard, more an interfacing issue then an algorithmics issue.
Write a wrapper container that provides an interface for accessing members and writing (say operator[] in C++) and internally it maps the size_t index argument to the right array. This wrapper class does need the size of every array though to be able to correctly map the index.
An example pseudocode operator[] would be:
int& JointDatabase::operator[](size_t index) {
// database is an iDatabase
iSection *cur = database.First_Section;
while (cur != database.Last_Section && index >= cur->Section_Count) {
index -= cur->Section_Count;
cur = cur->Next;
}
return cur->Section_Arr[index];
}
Then use this wrapper class as you would use a normal container in your Quicksort algorith.
If you can make sure that Array1, Array2, and Array3 are declared one after another and in continuous memory layout, then you can give the Array1 (the first one) in the sort() and give the combined size of all the arrays.
To check the continuous alignment you can use following trick.
template<size_t SIZE1, size_t SIZE2, size_t SIZE3>
bool Check(int (&a1)[SIZE1], int (&a2)[SIZE2], int (&a3)[SIZE3])
{
return (&a3[SIZE3 - 1] - &a1[0]) == (SIZE1 + SIZE2 + SIZE3);
}
Usage,
bool aligned = Check(Array1, Array2, Array3);
This is an example for 3 arrays, you can make it according to your need. And you can pass Array1,2,3 or Array3,2,1 depending on your machine.
Tested only in my brain:
struct ArrayWrapper {
int** arrays;
int* running_sums;
ArrayWrapper(int **arrays, int *arrays_length, int N) {
running_sums = new int*[N+1];
int sum = 0;
for (int i = 0; i < N; i++) {
running_sums[i+1] = sum;
sum += arrays_length[i];
}
}
int& operator[] (int index) {
int array_start = binary search `running_sum` for the closest number to `index` (round down)
return arrays[array_start][index - running_sums[array_start]]
}
}
so if you have something like:
array1 = {...}
array2 = {...}
...
arrayN = {...}
arrays = {array1, array2, ..., arrayN}
arrays_length = {array1_length, array2_length, ..., arrayN_length}
ArrayWrapper wrapper = new ArrayWrapper(arrays, arrays_length, N);
// wrapper then can be treated like normal array:
wrapper[10] = x;
x = wrapper[10];