Recursive Merge Sort Function Giving Bad Output - c++

I am trying to figure out why my merge sort function is not working. I believe the problem is within the merge() part of it. here is my code:
int mergeSort(int *data, int left, int right) {
//divide
if (left < right) {
int q = floor((left + right) / 2);
//conquer
mergeSort(data, left, q);
mergeSort(data, q + 1, right);
//combine
merge(data, left, q, right);
}
//print results for testing purposes
for (int i = 0; i < n; i++) {
cout << data[i] << "\n";
}
return 0;
}
And here is the merge() part of it. I believe the problem is within this part.
int merge(int *data, int left, int q, int right) {
int *temp = data;
int i = left, j = q + 1, z = left;
int t1 = 0, t2 = 0;
while (i <= q && j <= right) { //while both variables have not reached the end of their sub arrays
if (temp[i] <= temp[j]) {
data[z] = temp[i];
i++;
}
else {
data[z] = temp[j];
j++;
}
z++;
}
//if subarrays are both sorted and in order, combine them
if (i < q) {
t1 = z, t2 = i;
for (t1; t1 < right;) {
data[t1] = temp[t2];
t1++;
t2++;
}
}
else if (j < right) {
int t1 = z; int t2 = j;
for (t1; t1 <= right;) {
data[t1] = temp[t2];
t1++;
t2++;
}
}
I think that my problem is coming from declaring int *temp = data; at the beginning of merge(). My thought is that I'm getting some kind of memory address conflict between these two arrays, but I'm not sure.
I have tried arranging the data in different order, and have found that when a data needs to be moved from index n to index n-i, each index between these two indices is replaced with the value of n. For example:
the passing in the array {4, 13, 8, 12, 9 } would return {4, 8, 8, 9, 9}
My other thought is that the i and j variables are not incrementing correctly. I have been over this code repeatedly and can't seem to find a solution.

This (first line of the merge function)
int *temp = data;
does NOT copy an array; it just creates another pointer pointing to the same memory. This causes your code to overwrite your data.
You probably need to do something like this:
int * temp = new int [right - left + 1];
memcpy (temp, data + left, (right - left + 1) * sizeof(int));
and don't forget to delete[] the memory for temp at the end of your function:
delete[] temp;
Note that you'll need to change your use of z; it should start from 0, not from left.
(The following are optional performance improvements; you can ignore them.)
Allocating and freeing memory in each merge function is a bad idea. Specially since we exactly know how much total extra memory we need for merging: an array of exactly n integers.
For this reason, it's a better idea to pass in another array of the same size along with data to mergeSort, so it can be used as scratch memory (i.e. temp memory) for merging. Or if you are really clever, you can ping-pong between the actual and the scratch memory to minimize copying.

Related

cpp - Implement a merge sort function without using void return type (recursively)

I intend to create a recursive merge sort function that returns a pointer to the sorted array. Below is my implementation of the program.
The output produced by the code mostly consists of garbage memory locations. Since it is a recursive function, I'm having trouble debugging it. What scares me is whether I have understood pointers, arrays and their interconversion incorrectly. It would be really helpful if someone could take a look at the code and tell me my mistake.
Function that prints an array given a pointer to its first element
void printArr(int *arr, int n){
int *ptr = arr;
for(int i = 0; i < n; i++){
cout << *ptr << " ";
ptr++;
}
cout << endl;
}
Merge Sort function
Here p is the pointer to the given array, n is the length of the array. l and r are the indices of the first and last element of the array respectively.
// returns pointer to the sorted array
int* mergeSort(int *p, int n, int l, int r){
if(l >= r){
return &p[l];
}
int mid = l + (r-l)/2;
int *leftArray = mergeSort(p, n, l, mid);
int *rightArray = mergeSort(p, n, mid+1, r);
int n1 = mid - l + 1;
int n2 = r - mid;
int sortedArray[n1+n2];
int *ptr = sortedArray; // pointer to the sorted array
int p1 = 0; // left array index pointer
int p2 = 0; // right array index pointer
int idx = 0; // sorted array index pointer
int flag = 0; /* flag = 1 => all elements of left array have been placed into the sorted array ; flag = 2 => all elements of right array have been placed into the sorted array */
// putting elements into the sorted array
for(int i = 0; i < n1+n2; i++){
if(p1 == n1){
flag = 1;
break;
}
if(p2 == n2){
flag = 2;
break;
}
if(*(leftArray+i) > *(rightArray+i)){
sortedArray[i] = *(leftArray+p1);
p1++;
idx++;
}
else{
sortedArray[i] = *(rightArray+p2);
p2++;
idx++;
}
}
if(flag == 1){
// put remaining elements of right array into the sorted array
for(int i = idx; i < n1+n2; i++){
sortedArray[i] = *(rightArray+p2);
p2++;
}
}
if(flag == 2){
// put remaining elements of left array into the sorted array
for(int i = idx; i < n1+n2; i++){
sortedArray[i] = *(leftArray+p1);
p1++;
}
}
// return the sorted array
return ptr;
}
Main function
int main(){
int arr[] = {7,2,1,5};
int n = sizeof(arr)/sizeof(int);
int *p = arr;
cout << "Original array: ";
printArr(arr, n);
int *f = mergeSort(arr, n, 0, 3);
cout << "New array: ";
printArr(f, n);
}
Your code have returned the local array's address, which is invalidated after function returned. Then gabarage data is printed:
int sortedArray[n1+n2];
int *ptr = sortedArray; // pointer to the sorted array
Change into
int *ptr = new int[n1 + n2];
auto sortedArray = ptr;
Then we get a non-garbage value, but we hit memory leak and it's hard to deal with memory deallocation since the retuned pointer may point to the array p under certain boundary conditions.
So return pointer is not a good design, and it just wastes the API call to allocate memory and deallocate memory. It's better to split the function into two: the first one allocates a temporary buffer, and the second one handles sorting with the buffer as a parameter and calls itself with a recursive. Or just sort in place, totally avoid the temporary buffer.

In merge sort algorithm, will freeing the left and right sub arrays after the arrays have been merged make any difference in the space complexity?

In one of the tutorial videos for merge sort, it was mentioned that once the right and left sub arrays have to merged to the parent array, in order to reduce the space complexity we need to free the memory allocated for the left and right sub arrays. But whenever we come out of the function call, the local variable will be destroyed. Do correct me if I am wrong. So will the action of freeing the memory make any difference?
Here is the code that I wrote:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void mergeArr(int *rarr, int *larr, int *arr, int rsize, int lsize) {
int i = 0, r = 0, l = 0;
while (r < rsize && l < lsize) {
if (rarr[r] < larr[l]) {
arr[i++] = rarr[r++];
} else {
arr[i++] = larr[l++];
}
}
while (r < rsize) {
arr[i++] = rarr[r++];
}
while (l < lsize) {
arr[i++] = larr[l++];
}
}
void mergeSort(int *arr, int length) {
if (length > 1) {
int l1 = length / 2;
int l2 = length - l1;
int rarr[l1], larr[l2];
for (int i = 0; i < l1; i++) {
rarr[i] = arr[i];
}
for (int i = l1; i < length; i++) {
larr[i - l1] = arr[i];
}
mergeSort(rarr, l1);
mergeSort(larr, l2);
mergeArr(rarr, larr, arr, l1, l2);
// will free(rarr); free(larr); make any difference in space complexity
}
}
int main() {
int arr[5] = { 1, 10, 2, 7, 5 };
mergeSort(arr, 5);
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
}
I have multiple things to say about this. More from a C++ pov:
int rarr[l1],larr[l2]; - this is illegal c++. This is just an extension provided by g++ and is not valid across other compilers. You should either do int* rarr = new int[l1]; or even better use an std::vector: std::vector<int> rarr(l1).
If you are doing the former (dynamic allocation using new i.e int* rarr = new int[l1]), you have to manage the memory on your own. So when you're done using it you have to delete it: delete[] rarr. Mind it, malloc and free are not c++, they are c. new and delete are c++ way of allocating/deallocating memory.
If you use a vector, c++ will handle the deletion/deallocation of memory so you need not worry.
Now coming back to your original question: whether or not an idea like this would improve your space complexity: the answer is NO. It won't.
Why? Think about the max temporary storage you're using. Check the first case of your recursion. Isn't the space that you're using O(N)? because larr and rarr will both be of size N/2. Moreover, the space complexity is O(N) assuming the temporary storage is being freed. If somehow the space is not freed, the space complexity will increase to O(N)+2*(N/2)+4*O(N/4).... which is O(Nlog2N) because each step of recursion is allocating some space which it is not freeeing.
In your implementation, the left and right arrays are defined with automatic storage, so deallocation is automatic when the function returns but it poses 2 problems:
a sufficiently large array will invoke undefined behavior because allocating too much space with automatic storage will cause a stack overflow.
variable sized arrays are not standard C++. You are relying on a compiler specific extension.
The maximum stack space used by your function is proportional to N, so the space complexity is O(N) as expected. You could allocate these arrays with new, and of course you would then have to deallocate them with delete otherwise you would have memory leaks and the amount of memory lost would be proportional to N*log2(N).
An alternative approach would use a temporary array, allocated at the initial call and passed to the recursive function.
Note also that the names for the left and right arrays are very confusing. rarr is actually to the left of larr!
Here is a modified version:
#include <iostream>
using namespace std;
void mergeArr(int *larr, int *rarr, int *arr, int lsize, int rsize) {
int i = 0, r = 0, l = 0;
while (l < lsize && r < rsize) {
if (larr[l] <= rarr[r]) {
arr[i++] = larr[l++];
} else {
arr[i++] = rarr[r++];
}
}
while (l < lsize) {
arr[i++] = larr[l++];
}
while (r < rsize) {
arr[i++] = rarr[r++];
}
}
void mergeSort(int *arr, int length) {
if (length > 1) {
int l1 = length / 2;
int l2 = length - l1;
int *larr = new int[l1];
int *rarr = new int[l2];
for (int i = 0; i < l1; i++) {
larr[i] = arr[i];
}
for (int i = l1; i < length; i++) {
rarr[i - l1] = arr[i];
}
mergeSort(larr, l1);
mergeSort(rarr, l2);
mergeArr(larr, rarr, arr, l1, l2);
delete[] larr;
delete[] rarr;
}
}
int main() {
int arr[] = { 1, 10, 2, 7, 5 };
int length = sizeof arr / sizeof *arr;
mergeSort(arr, length);
for (int i = 0; i < length; i++) {
cout << arr[i] << " ";
}
return 0;
}
Freeing temporary arrays does not influence on space complexity because we must consider maximum memory consumption - it is about size of initial array.
From the performance point of view, it seems reasonable to allocate temporary storage once in the beginning of sorting, reuse it at every stage, and free it after all the work is done.

a program to sum 2 arrays and display output in third array. It's showing Runtime error. Why?

I edited the code. But now it's showing runtime error. Can anyone tell why ? This is a program to sum 2 arrays and display output in third array.
I also wanted to know if this code could be optimized ?
void sumOfTwoArrays(int arr[], int size1, int brr[], int size2, int crr[])
{
int k;
if(size1>size2){
k = size1;
}
else
k = size2;
int c = k;
int r = 0;
int i = size1-1;
int j = size2-1;
for(;i>=0&&j>=0;i--,j--){
int n = arr[i] + brr[j] + r;
if(n<=9){
crr[c] = n;
}
else
{
int r = n/10;
n = n%10;
crr[c] = n;
}
c--;
}
while(arr[i]>=0){
crr[c] = arr[i] + r;
r = 0;
c--;
}
while(brr[j]>=0){
crr[c] = brr[j] + r;
r = 0;
c--;
}
if(r!=0){
crr[c] = r;
}
}
You declare variables in a block scope, i.e. inside { ... }, and these variables are visible only within this block:
if(size1>size2){
int crr[size1+1];
int c = size1;
}
else{
int crr[size2+1];
int c = size2;
}
...
crr[c] = ... // neither crr nor c are valid here any more
BTW: C++ does not support variable length arrays like int crr[size1+1] (when size is not a compile-time-constant).
To overcome this, write...
int *crr;
int c;
if(size1>size2){
crr = new int[size1+1];
c = size1;
}
else{
crr = new int[size2+1];
c = size2;
}
...
delete[] crr;
About scope issue: see Stephan's answer.
I also wanted to know if this code could be optimized
By use of std::vector. OK, the following is only a fine option if you can use vectors outside as well – copying the raw arrays into vectors wouldn't be efficient either... But if you can, then you might like this variant:
template <typename T> // optional: you're more flexible if you make a template of...
void sumOfTwoArrays(std::vector<T> const& va, std::vector<T> const& vb, std::vector<T>& vr)
{
vr.resize(std::max(va.size(), vb.size() + 1));
int carry = 0; // renamed r to something more meaningful
// these pairs will help to avoid code duplication later
std::pair pa(va, va.rbegin());
std::pair pb(vb, vb.rbegin());
auto ir = vr.rbegin();
while(pa.second != pa.first.rend() && pb.second != pb.first.rend())
{
// just skip the if/else:
// assume you have arbitrary number, the else case will be entered anyway
// in 50 % of the cases - in the other 50 %, the else branch calculates
// the correct result, too; and on most modern machines, the branch is
// rather expensive, so you result in easier code and have quite a good
// chance to even perform better...
carry += *pa.second + *pb.second;
*ir = carry % 10;
carry /= 10;
++ir, ++pa.second, ++pb.second;
}
// avoiding of two identical while loops: iterate over the two pairs...
for(auto p : { pa, pb })
{
// just loop over, if we are already at the end, won't enter...
while(p.second != p.first.rend())
{
// STILL need to continue calculating the carry!
// imagine we have set it and ciphers following are all 9!
carry += *p.second;
*ir = carry % 10;
carry /= 10;
++ir, ++p.second;
}
}
// assign either 0 or 1...
*ir = carry;
}
Variant: instead of assigning 0, you could erase first element at the very end:
if(carry == 0)
{
vr.erase(vr.begin());
}
else
{
*ir = carry;
}
Note that this will move all the elements one position to front. On the other hand, if you repeatedly add vectors already containing a leading zero, you might prepend another one again and again without need, if you don't drop it again.
You wouldn't experience any of these issues if you inverted the order of digits in the vector, having least significant one at position 0 (you'd exchange rbegin() and rend() with begin() and end(), but would use the former for printing data to display...). Erasure at the end would be an O(1) operation then:
if(carry == 0)
{
vr.erase(std::previous(vr.end())
}
// ...
All this above will only work as expected if you keep your vectors normalised (i. e. all digits in between 0 and 9 inclusive). You might consider packing the vector into a separate class such that the data is hidden away from the user and only can be modified in controlled manner (assume you have a fine vector, but a user does v[7] = -1012...).
A runtime error suggests that it is a memory issue i.e. you are writing to some memory which is not allocated to be used by your code. So, as mentioned by other contributors, you should allocate proper memory for your arrays.
Following is modified version of your code which is working fine. You can see it working here:
void sumOfTwoArrays(int arr1[], int size1, int arr2[], int size2, int sumArr[])
{
int maxLen;
int* tArry;
int l;
if(size1>size2) { maxLen = size1; tArry = arr1; l = size1 - size2; }
else { maxLen = size2; tArry = arr2; l = size2 - size1; }
int carry = 0;
while(size1 && size2){
carry += arr1[--size1] + arr2[--size2];
sumArr[maxLen--] = carry%10;
carry /= 10;
}
while(l){
carry += tArry[--l];
sumArr[maxLen--] = carry%10;
carry /= 10;
}
sumArr[maxLen] = carry;
}
Calling code looks something like this:
...
int a[] = {9,9,9,9,9};
int b[] = {1};
int l1 = sizeof(a) / sizeof(int), l2 = sizeof(b)/sizeof(int);
int l3 = ((l1 > l2) ? l1 : l2) + 1;
int *c = new int[l3];
sumOfTwoArrays(a, l1, b, l2, c);
...
delete [] c;
...

Main skipping over function?

I'm attempting to teach myself the basics of algorithms and data structures through a free online course, and as such, I though it'd give it a first shot at merge sort. This isn't really going to be used for anything so it's pretty sloppy, but I seem to be having a problem where main is not calling the MergeSort function.
The output is 00000000, (I assume because array is never assigned anything). When I run the program through gdb the program seems to get to that line, and then completely skip over the function and go directly to the loop that prints the array.
Any thoughts? Am I missing something stupid?
#include <iostream>
using namespace std;
int *MergeSort(int array[], int sizeOf);
int main(){
int numbers[8] = {5, 4, 1, 8, 7, 2, 6, 3};
int *array = MergeSort(numbers, 8);
for (int i = 0; i < 8; i++)
cout << array[i];
return 0;
}
int *MergeSort(int array[], int sizeOf){
int *leftArr = new int[sizeOf/2]; // Build arrays to split in half
int *rightArr = new int[sizeOf/2];
if (sizeOf < 2){ // Base case to end recursion
return array;
}
else{
for (int i = 0; i < (sizeOf/2); i++){ // Left gets first half
leftArr[i] = array[i];
}
int j = (sizeOf/2) - 1; // Set point to start building 2nd
for (int i = sizeOf; i >= (sizeOf/2); i--){
rightArr[j] = array[i]; // Build other half of array
j--;
}
leftArr = MergeSort(leftArr, sizeOf/2); // Call Recursive functions
rightArr = MergeSort(rightArr, sizeOf/2);
}
static int *newArray = new int[sizeOf]; // Sorted array to Build
int k = 0; // Iterators to build sorted func
int m = 0;
int p = 0;
while (p < sizeOf){
if (leftArr[k] < rightArr[m]){ // Left Arr's current value is less
newArray[p] = leftArr[k]; // right arr's current calue
k++;
}
else if (leftArr[k] >= rightArr[m]){
newArray[p] = rightArr[k];
m++;
}
p++;
}
//for (int i = 0; i < 8; i++)
// cout << newArray[i] << endl;
return newArray; // Return address to new array
}
There is a fundamental design issue in your MergeSort():
your algorithm is recursive (that's perfect)
unfortunately it returns newArraywhich is static. This means that all invocations use the same instance of the same static variable (and overwrite the one returned by the recursive call).
You need to solve this by making newArray non static. And at the end of the function, you need to delete[] the arrays returned by recursive calls in order to avoid memory leakage.

Am I implementing the "Heapify" Algorithm correctly?

I'm creating a heap implementation for a computer science class, and I was wondering if the following recursive function would create a heap out of an array object that was not already a heap.
the code is as follows:
void Heap::Heapify(int i)
{
int temp, l, r, heapify;
l = LeftChild(i);// get the left child
r = RightChild(i);// get the right child
//if one of the children is bigger than the index
if((Data[i] < Data[l]) || (Data[i]< Data[r]))
{
//if left is the bigger child
if(Data[l] > Data[r])
{
//swap parent with left child
temp = Data[i];
Data[i] = Data[l];
Data[l] = temp;
heapify = l; // index that was swapped
}
//if right is the bigger child
else
{
//swap parent with right child
temp = Data[i];
Data[i] = Data[r];
Data[r] = temp;
heapify = r; // index that was swapped
}
// do a recursive call with the index
//that was swapped
Heapify(heapify);
}
}
the idea is that you see if the data at the index given is bigger than all of it's children. If it is, the function ends no problem. Otherwise, it check to see which is biggest(left or right children), and then swaps that with the index. The heapify is then called at the index where the swapping happened.
by ildjarn's request, I'm including my full class definition and implementation files to aid in the answering of my question:
here's the header file:
#ifndef HEAP_H
#define HEAP_H
//Programmer: Christopher De Bow
//Date: november 15, 2011
class Heap
{
private:
int Data [100];
int Parent(int);
int RightChild(int);
int LeftChild(int);
void Heapify(int);
void BuildHeap();
public:
Heap();
void insert();
void HeapSort();
void ExtractMaximum();
int Maximum();
void PrintHeap();
int heapsize;
void SetData(int[]);
};
#endif
and the implementation file:
#include <iostream>
#include "Heap.h"
using namespace std;
//Programmer: Christopher De Bow
//Date: november 15, 2011
Heap::Heap()
{
int init [10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
heapsize = 10;
SetData(init);
}
int Heap::Parent(int index)
{
int Rval;
if(index%2 == 0)// if the index is even
{
Rval = ((index-1)/2);
}
else// if the index is odd
{
Rval = (index/2);
}
return Rval;
}
int Heap::RightChild(int arrplace)
{
int ret;
ret = ((2*arrplace)+2); //rightchild is index times 2 plus 2
return ret;
}
int Heap::LeftChild(int i)
{
int rval;
rval = ((2*i)+1); //leftchild is index times 2 plus 1
return rval;
}
void Heap::Heapify(int i)
{
int temp, l, r, heapify;
l = LeftChild(i); // get the left child
r = RightChild(i); // get the right child
if((l <= heapSize) && (data[l] > data[i]))
{
heapify = l;
{
else
{
heapfiy = i;
}
if((r <= heapSize) && (data[r] > data[heapify]))
{
heapify = r;
}
if(heapify != i) // one of the two child nodes has proved
{ // larger than Data[i], so interchange values
//swap parent with left child
temp = Data[i];
Data[i] = Data[heapify];
Data[heapify] = temp;
Heapify(heapify);
}
}
void Heap::BuildHeap()
{
// we do not have a heap
// we will make a heap
// by calling heapify starting at the lowest
// internal node in the heap
for(int i = heapsize; i >= 1; i--)
{
Heapify(i-1);
}
}
void Heap::insert()
{
int insert;
heapsize = (heapsize + 1);
//getting data from the user
cout<<"what data would you like to insert?"<<endl;
cin>>insert;
Data[heapsize] = insert;
BuildHeap(); //call BuildHeap on array
cout<<"done"<<endl;
}
void Heap::PrintHeap()
{
BuildHeap();
for(int count = 0; count < (heapsize-1); count++)
{
cout<<Data[count];// print out every element in heap
}
cout<<endl<<endl;
}
void Heap::HeapSort()
{
BuildHeap();
int temp;
// do this for every elem in heap:
for(int i = 0; i < heapsize; i++)
{
temp = Data[heapsize-1];
Data[heapsize-1] = Data[0];
Data[0] = temp;
heapsize--;
BuildHeap();
}
PrintHeap();
}
void Heap::ExtractMaximum()
{
BuildHeap();
//assign last thing in heap to first thing in heap
Data[0] = Data[heapsize];
heapsize --; // decrease heapsize by one
Heapify(0); // heapify from the top
}
int Heap::Maximum()
{
int Rval;
BuildHeap();// make sure we have a heap
Rval = Data[0];
return Rval; // return top thing
}
//initialize the elements in the "Data" array
void Heap::SetData(int x[])
{
for(int i = 0; i <= (heapsize); i++)
{
Data[i] = x[i];
}
}
Your algorithm works. The problem is in the translation of algorithm to code. Say you declared Data as :
int Data[7];
and you populate it with the initial values {0, 1, 2, 3, 4, 5, 6}. Presuming definitions of LeftChild(i) and RightChild(i) to be something like:
#define LeftChild(i) ((i << 1) + 1)
#define RightChild(i) ((i << 1) + 2)
then your function BuildHeap(), which should be something like:
void Heap::BuildHeap()
{
for(int i = (7 >> 1); i >= 1; i--) // in general, replace 7 with
// (sizeof(Data)/sizeof(int)), presuming
// you have an array of int's. if not,
// replace int with the relevant data type
Heapify(i-1);
}
will begin the Heapify process on the lower-right-most sub-tree root. In this case, this is array index 2, with a left child of 5 and a right child of 6. Heapify will correctly exchange 2 and 6 and recursively call Heapify(6).
Here the whole thing can run aground! At present your tree looks like :
0
1 2
3 4 5 6
u n d e f i n e d s p a c e
so the call Heapify(6) will dutifully compare the values of Data[6] with Data[13] and Data[14] (the perils of C++ and its lack of array boundaries enforcement, unlike Java). Obviously, the latter two values can be any junk left in RAM. One solution here, ugly but a working patch, is to add 8 elements in the declaration of Data and initialize them all to some value lower than any element of the array. The better solution is to add a heapSize variable to your class and set it equal to the length of your array:
heapSize = (sizeof(Data)/sizeof(int));
Then integrate logic to only compare child nodes if they are valid leaves of the tree. An efficient implementation of this is :
void Heap::Heapify(int i)
{
int temp, l, r, heapify;
l = LeftChild(i); // get the left child
r = RightChild(i); // get the right child
if((l <= heapSize) && (Data[l] > Data[i]))
heapify = l;
else heapfiy = i;
if((r <= heapSize) && (Data[r] > Data[heapify]))
heapify = r;
if(heapify != i) // one of the two child nodes has proved
// larger than Data[i], so interchange values
{
//swap parent with left child
temp = Data[i];
Data[i] = Data[heapify];
Data[heapify] = temp;
Heapify(heapify);
}
}
So to summarize, the solution is as straightforward as adding logic to make sure the child nodes are valid leaves of the tree, and your main function will have something like :
Heap heap;
// initialize Data here
heap.BuildHeap();
Hope that helps.
No. On the tree
1
/ \
/ \
/ \
2 3
/ \ / \
6 7 4 5
the output is going to be
3
/ \
/ \
/ \
2 5
/ \ / \
6 7 4 1
which has several heap violations. (I'm assuming that Data[l] and Data[r] are minus infinity if the corresponding children do not exist. You may need extra logic to ensure this.)
What your function does is fix a tree that may not be a heap but whose left and right subtrees are heaps. You need to call it on every node, in postorder (i.e., for i from n - 1 down to 0) so that the children of i are heaps when Heapify(i) is called.
Your code now successfully builds a heap. There was only one conceptual flaw : the rest were off-by-one indexing errors. The one fundamental error was in BuildHeap : you had
for(int i = heapSize; i >= 1; i--)
{
Heapify(i-1);
}
whereas this should be
for(int i = (heapSize / 2); i >= 1; i--)
{
Heapify(i-1);
}
This is really important, you must see that Heapify is always called on a tree root, and (this is really cool) you can easily find the last tree root in the array at the index ((heapSize/2) - 1) (this is for C++ and Java style where the first index == 0). The way it was written your code called Heapify on the last leaf of the tree, which is in error.
Other than that, I added comments to flag the off-by-one errors. I placed them flush left so you can easily find them. Hope you get a suberb understanding of algorithms and data structures! :-)
Your header file :
#ifndef HEAP_H
#define HEAP_H
//Programmer: Christopher De Bow
//Date: november 15, 2011
class Heap
{
private:
int Data [100];
int Parent(int);
int RightChild(int);
int LeftChild(int);
void Heapify(int);
void BuildHeap();
// SO added heapSize
int heapSize;
public:
Heap();
void insert();
void HeapSort();
void ExtractMaximum();
int Maximum();
void PrintHeap();
int heapsize;
void SetData(int[]);
};
#endif
Your cpp file :
#include <iostream>
#include "Heap.h"
using namespace std;
//Programmer: Christopher De Bow
//Date: november 15, 2011
Heap::Heap()
{
int init [10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
heapSize = 10;
SetData(init);
}
int Heap::Parent(int index)
{
int Rval;
if(index%2 == 0)// if the index is even
{
Rval = ((index-1)/2);
}
else// if the index is odd
{
Rval = (index/2);
}
return Rval;
}
int Heap::RightChild(int arrplace)
{
int ret;
ret = ((2*arrplace)+2); //rightchild is index times 2 plus 2
return ret;
}
int Heap::LeftChild(int i)
{
int rval;
rval = ((2*i)+1); //leftchild is index times 2 plus 1
return rval;
}
void Heap::Heapify(int i)
{
int temp, l, r, heapify;
l = LeftChild(i); // get the left child
r = RightChild(i); // get the right child
// you have to compare the index to (heapSize - 1) because we are working
// with C++ and the first array index is 0 : l and r are direct indices
// into the array, so the maximum possible index is the heapSize'th
// element, which is at heapSize-1. this was kind of nasty as it let the
// heapify index get too large and led to a swap with memory beyond the
// last element of the array (again, C++ doesn't enforce array boundaries
// as Java does).
if((l <= (heapSize-1)) && (Data[l] > Data[i]))
heapify = l;
else
heapify = i;
// you have to compare the index to (heapSize - 1) because we are working
// with C++ and the first array index is 0 : l and r are direct indices
// into the array, so the maximum possible index is the heapSize'th
// element, which is at heapSize-1. this was kind of nasty as it let the
// heapify index get too large and led to a swap with memory beyond the
// last element of the array (again, C++ doesn't enforce array boundaries
// as Java does).
if((r <= (heapSize-1)) && (Data[r] > Data[heapify]))
heapify = r;
if(heapify != i) // one of the two child nodes has proved
{ // larger than Data[i], so interchange values
//swap parent with left child
temp = Data[i];
Data[i] = Data[heapify];
Data[heapify] = temp;
Heapify(heapify);
}
}
void Heap::BuildHeap()
{
// we do not have a heap
// we will make a heap
// by calling heapify starting at the lowest
// internal node in the heap
// i must be initialized to (heapsize/2), please see my
// post for an explanation
for(int i = heapSize/2; i >= 1; i--)
{
Heapify(i-1);
}
}
void Heap::insert()
{
int insert;
heapSize = (heapSize + 1);
//getting data from the user
cout<<"what data would you like to insert?"<<endl;
cin>>insert;
Data[heapSize] = insert;
BuildHeap(); //call BuildHeap on array
cout<<"done"<<endl;
}
void Heap::PrintHeap()
{
BuildHeap();
// the array indices are from 0 through (heapSize-1), so
// count must be less than _or equal to_ (heapSize-1). another
// way of phrasing this (which i applied in this function)
// is (count < heapSize). you'll get better boundary conditions
// with practice.
for(int count = 0; count < heapSize; count++)
{
// added an endl to the output for clarity
cout << Data[count] << endl;// print out every element in heap
}
cout<<endl<<endl;
}
void Heap::HeapSort()
{
BuildHeap();
int temp;
// do this for every elem in heap:
for(int i = 0; i < heapSize; i++)
{
temp = Data[heapSize-1];
Data[heapSize-1] = Data[0];
Data[0] = temp;
heapSize--;
BuildHeap();
}
PrintHeap();
}
void Heap::ExtractMaximum()
{
BuildHeap();
//assign last thing in heap to first thing in heap
Data[0] = Data[heapSize];
heapSize--; // decrease heapSize by one
Heapify(0); // heapify from the top
}
int Heap::Maximum()
{
int Rval;
BuildHeap();// make sure we have a heap
Rval = Data[0];
return Rval; // return top thing
}
//initialize the elements in the "Data" array
void Heap::SetData(int x[])
{
// the array indices are from 0 through (heapSize-1), so
// count must be less than _or equal to_ (heapSize-1). another
// way of phrasing this (which i applied in this function)
// is (i < heapSize). you'll get better boundary conditions
// with practice.
for(int i = 0; i < heapSize; i++)
{
Data[i] = x[i];
}
}
// basic confirmation function
int main()
{
Heap heap;
heap.PrintHeap();
return 0;
}
Your code as written here sure feels right; but there's nothing quite like writing a few test cases to see how it performs. Be sure to test against a heap with 1, 2, 3, 4, and dozens of elements. (I expect the base case to be where this piece falls short -- how does it handle when i has no children?. Testing on small heaps ought to show in a hurry.)
Some small advice for this piece:
if(Data[l] > Data[r])
{
//swap parent with left child
temp = Data[i];
Data[i] = Data[l];
Data[l] = temp;
heapify = l; // index that was swapped
}
//if right is the bigger child
else
{ //swap parent with right child
temp = Data[i];
Data[i] = Data[r];
Data[r] = temp;
heapify = r; // index that was swapped
}
You could probably gain some legibility by setting only the index in the if blocks:
if(Data[l] > Data[r]) {
swapme = l;
} else {
swapme = r;
}
temp = Data[i];
Data[i] = Data[swapme];
Data[swapme] = temp;
heapify = swapme;