C++ QuickSort Algorithm keeps crashing - c++

Trying to implement a Quick Sort algorithm.I think the problem is with the recursion, but I do not know what I should do to fix it. The program continues to crash every time I run it, and I cannot understand why. Here is the code:
#include<iostream>
using namespace std;
int pIndex;
int Partition(int *A,int start,int end){
int pivot;
int pIndex;
if(start<end){
int pivot=A[end];
int pIndex=start;
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
//cout<<pIndex<<endl;
swap(A[pIndex],A[end]);
return pIndex;
};
void QuickSort(int *A, int start,int end){
if(start<end)
{
pIndex=Partition(A,start,end);
QuickSort(A,pIndex+1,end);
QuickSort(A,start,pIndex-1);
}
};
int main(){
int A[10]{4,23,1,43,2,10};
//Partition(A,0,9);
QuickSort(A,0,5);
for(int x=0;x<10;x++){
cout<< A[x]<<" ";
}
}

Your partition algorithm is about twice as much code as it needs to be. You seem to be always choosing the last element in the sequence for your pivot, and although not advisable, it will work for academic demonstration.
Your Crash
You're defining two pIndex values, only one of which is actually deterministic. You also declare two pivot variables, but that does NOT cause your crash (the first one is never used). It should be cleaned up none-the-less, but the death knell in your code is duplicate pIndex
int pivot;
int pIndex; // HERE
if(start<end){
int pivot=A[end];
int pIndex=start; // HERE AGAIN
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
swap(A[pIndex],A[end]); // uses non-determined pIndex
return pIndex; // returns non-determined pIndex
Changing int pIndex=start; to simply be pIndex=start; will solve your crash, but your partition method still needs... help.
The "Sweep" Partition Method
The "sweep" method of partitioning is generally done like this for a pivot value that is assumed to be right-tailed, and you would be hard pressed to get this simpler (invoking std::partition not withstanding):
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<end; ++i)
{
if (A[i] < a[len-1])
std::swap(A[i], A[pvt++])
}
std::swap(A[pvt], a[len-1]);
return pvt;
};
The above algorithm includes only the necessities needed for a partition: the sequence iterator (a pointer in your case), and the sequence length. Everything else is deterministic based on those two items. A quick sample program of how this works follows, with a purposely placed 5 for the pivot value:
#include <iostream>
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
std::swap(A[i], A[pvt++]);
}
std::swap(A[pvt], A[len-1]);
return pvt;
};
int main()
{
int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
size_t n = Partition(arr, sizeof(arr)/sizeof(*arr));
std::cout << "Partition : " << n << '\n';
for (auto x : arr)
std::cout << x << ' ';
std::cout << '\n';
}
Output
Partition : 4
4 3 2 1 5 7 8 6 9
How To Invoke From QuickSort
Invoking a partition in quicksort sets up the pivot location, which becomes the "end" iteration point of the bottom segment, and the one-BEFORE iteration point of the top segment. It is critical the pivot location returned from an invoke of Partition() should NOT be included in either subsequence when recursing.
void QuickSort(int *A, size_t len)
{
if (len < 2)
return;
size_t pvt = Partition(A, len);
QuickSort(A, pvt++); // NOTE: post increment...
QuickSort(A+pvt, len-pvt); // ...which makes this skip the pivot
}
Yeah, pointer arithmetic rocks, don't you think?
Putting It All Together
The program below incorporates both the Partition and QuickSort :
#include <iostream>
size_t Partition(int *A, size_t len)
{
if (len < 2)
return 0;
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
std::swap(A[i], A[pvt++]);
}
std::swap(A[pvt], A[len-1]);
return pvt;
};
void QuickSort(int *A, size_t len)
{
if (len < 2)
return;
size_t pvt = Partition(A, len);
QuickSort(A, pvt++); // NOTE: post increment
QuickSort(A+pvt, len-pvt);
}
int main()
{
int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
QuickSort(arr, sizeof(arr)/sizeof(*arr));
for (auto x : arr)
std::cout << x << ' ';
std::cout << '\n';
}
Output
1 2 3 4 5 6 7 8 9
I hope it helps.

In this part:
int pivot;
int pIndex;
if(start<end){
int pivot=A[end];
int pIndex=start;
You are defining two pivots, and two pIndex's. You are not using the pivot at all, and with the last swap you are using the uninitialized pIndex. This should work:
int Partition(int *A,int start,int end){
int pIndex = start;
if(start<end){
for(int x=0;x<end;x++){
if(A[x]<A[end]){
swap(A[x],A[pIndex]);
pIndex=pIndex+1;
}
}
swap(A[pIndex],A[end]);
}
swap(A[pIndex],A[end]);
return pIndex;
}

I'm also something of a C++ newbie, but I find myself curious about what happens when start >= end. It looks as though your Partition function will still return a pIndex value, but I don't see where you define it anywhere. If (as I suspect) it returns whatever value happens to reside in memory, then you'll very likely end up referencing some undefined memory locations when you use A[pIndex]

Related

Randomly Shuffle an array and using quick sort algorithm

I have been trying to write a code to randomly shuffle the array elements, and then use the quick sort algorithm on the array elements. This is the code I wrote:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void randomise(int arr[], int s, int e)
{
int j;
srand(time(NULL));
for (int i = e; i >= s; i--)
{
int j = rand() % (i + 1);
swap(&arr[i], &arr[j]);
}
}
int Partition(int arr[], int s, int e)
{
int pivot = arr[e];
int i = s - 1;
int j;
for (j = s; j <= e; j++)
{
if (arr[j] <= pivot)
{
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[e]);
return i + 1;
}
void QuickSort(int arr[], int s, int e)
{
if (s >= e)
return;
int x = Partition(arr, s, e);
QuickSort(arr, s, x - 1);
QuickSort(arr, x + 1, e);
}
int main()
{
int b[] = {1, 2, 3, 4, 5};
randomise(b, 0, 4);
cout << "Elements of randomised array are:";
for (int i = 0; i <= 4; i++)
{
cout << b[i] << endl;
}
QuickSort(b, 0, 4);
cout << "Elements after quick sort are:";
for (int i = 0; i <= 4; i++)
{
cout << b[i] << endl;
}
return 0;
}
However, on debugging on GDB, I found out that this program gives segmentation fault. On execution, this program gives the output as:
Elements of randomised array are:4
5
2
3
1
Can someone tell me what is the bug in this code (I have tried debugging it on GDB, but I am still clueless).
Basically, when the error is segmentation fault, you should be looking for a bug which you will feel like crashing your head into wall, after finding it. On line 26. change <=, to < . It's in your partition function. for (j = s; j < e; j++)
A little explanation about quick sort; After each time quickSort function runs on a partiotion, the last element of the partition, called pivot, will reach its' real place in array. The partition function, returns the real place of the pivot in the array. Then the main array will be split into two more partitions, before the pivot place, and after that. Your bug is returning real-pivot-place + 1, as the output of partition function. So you will run quickSort on wrong partition; the partition that is already sorted but the program will keep trying to sort it over and over because of wrong partitioning. As you may know, each time you run a function, its' variables will be saved into a stack in computer. Since your calling a recursive function over and over(that isn't supposed to stop), this stack will get full and will overflow. After that, computer will represent some undefined behavior and maybe throw an exception that can not describe the problem correctly. This is why your getting segmentation fault. But why you return real-pivot-place + 1? Because in your for loop in partition function, you will visit the pivot too, which you shouldn't. Because pivot isn't supposed to be compared with itself. So you will increase i variable unnecessarily. https://en.wikipedia.org/wiki/Call_stack Check this link for additional information about stack and how a function runs in computer.

I am getting garbage values in my Merge Sort code

I am trying to implement merge sort algorithm in C++. This is my code.Logic seems to be fine.
But the output I'm getting is garbage values.I'm not able to get where the problem is in the code.
I think my logic is correct but I'm not sure.
#include <iostream>
using namespace std;
void Merge(int A[],int L[],int nL,int R[],int nR);
void MergeSort(int A[]);
//Function to Merge Arrays L and R into A.
//nL = number of elements in L
//nR = number of elements in R.
void Merge(int A[],int L[],int nL,int R[],int nR)
{
// i - to mark the index of left subarray (L)
// j - to mark the index of right sub-raay (R)
// k - to mark the index of merged subarray (A)
int i=0;
int j=0;
int k=0;
while(i<nL && j<nR)
{
if(L[i]<=R[i])
{ A[k]=L[i];
i=i+1;
}
else
{ A[k]=R[j];
j=j+1;
}
k=k+1;
}
while(i<nL)
{ A[k]=L[i];
i=i+1;
k=k+1;
}
while(j<nR)
{ A[k]=R[j];
j=j+1;
k=k+1;
}
}
// Recursive function to sort an array of integers.
void MergeSort(int A[],int n)
{
if (n<2) return;//base condition.If the array has less than two
//elements, do nothing
int mid=n/2;
// create left and right subarrays
// mid elements (from index 0 till mid-1) should be part of left sub-
//array
// and (n-mid) elements (from mid to n-1) will be part of right sub-
//array
int left[mid];
int right[n-mid];
for(int i=0;i<mid-1;i++) left[i]=A[i];// create left subarray
for(int i=mid;i<n-1;i++) right[i-mid]=A[i];// create right subarray
MergeSort(left,mid);
MergeSort(right,n-mid);
Merge(A,left,mid,right,n-mid);
}
int main()
{ int A[]={2,4,7,1,5,3};
int n=sizeof(A)/sizeof(A[0]);
MergeSort(A,n);
for(int i=0;i<n;i++) cout<<A[i]<<" ";
return 0;
}
Expected output is 1 2 3 4 5 7
But actual is 0 -785903160 1 0(every time it's different)
This has been answered at, how-to-implement-classic-sorting-algorithms-in-modern-c. But I thought I'd post an answer that beginners my find valuable as I've seen this asked many times in the past. It must be a popular homework question.
In my opinion, if this is a modern C++ assignment, there should be a lot less of using indexing.
So in this implementation, I have not used std::merge and wrote the merge so some method could be seen.
Avoid the idiom: using namespace std; Why it is taught is beyond me. Typedef your types, it is much clearer.
using data_vect = std::vector<int>;
Hopefully this is clear enough and completely done with iterators. It is not as efficient as possible, the push_back in Merge could be avoided among other things. I DuckDucked this sort and the first few hits were not that great.
#include <iostream>
#include <vector>
using data_vect = std::vector<int>;
using dv_iter = data_vect::iterator;
data_vect Merge(data_vect& first, data_vect& second)
{
data_vect result;
dv_iter fval = first.begin();
dv_iter sval = second.begin();
for (;fval != first.end() || sval != second.end();)
{
if (fval == first.end())
result.push_back(*sval++);
else if (sval == second.end())
result.push_back(*fval++);
else if (*fval < *sval)
result.push_back(*fval++);
else
result.push_back(*sval++);
}
return result;
}
void MergeSort(data_vect& input)
{
int half = input.size() / 2;
if (! half)
return;
data_vect left(input.begin(), input.begin() + half );
data_vect right(input.begin() + half, input.end());
MergeSort(left);
MergeSort(right);
input = Merge(left, right);
}
int main()
{
data_vect A = { 6,2,7,4,1,5,3 };
MergeSort(A);
for ( auto& val : A )
std::cout << val << " ";
return 0;
}
Although understandable,
it is not possibile in C++ to declare an array with a variable size, e.g. int[mSize].
All arrays must have a constant size, e.g. int[10] or
const int mSize = 10;
int[mSize] mArray...
You want a storage container which has a variable size. as #PaulMcKenzie suggested,
you might want to use a Vector object. Your code would look something like this:
#include <iostream>
#include <vector>
using namespace std;
void Merge(vector<int>& A, vector<int>& L, vector<int>& R);
void MergeSort(vector<int>& A);
//Function to Merge Arrays L and R into A.
void Merge(vector<int>& A, vector<int>& L, vector<int>& R)
{
// i - to mark the index of left subarray (L)
// j - to mark the index of right sub-raay (R)
// k - to mark the index of merged subarray (A)
unsigned int i=0;
unsigned int j=0;
unsigned int k=0;
while(i<L.size() && j<R.size())
{
if(L[i]<=R[i])
{ A[k]=L[i];
i=i+1;
}
else
{ A[k]=R[j];
j=j+1;
}
k=k+1;
}
while(i<L.size())
{ A[k]=L[i];
i=i+1;
k=k+1;
}
while(j<R.size())
{ A[k]=R[j];
j=j+1;
k=k+1;
}
}
// Recursive function to sort an array of integers.
void MergeSort(vector<int>& A)
{
int n = A.size();
if (n<2) return;//base condition.If the array has less than two
//elements, do nothing
int mid=n/2;
// create left and right subarrays
// mid elements (from index 0 till mid-1) should be part of left sub-
//array
// and (n-mid) elements (from mid to n-1) will be part of right sub-
//array
vector<int> left(mid);
vector<int> right(n-mid);
for(int i=0;i<mid;i++) left[i]=A[i];// create left subarray
for(int i=mid;i<n;i++) right[i-mid]=A[i];// create right subarray
MergeSort(left);
MergeSort(right);
Merge(A,left,right);
}
int main()
{ vector<int> A={2,4,7,1,5,3};
MergeSort(A);
for(unsigned int i=0;i<A.size();i++) cout<<A[i]<<" ";
return 0;
}
[Edit]
I noticed I accidentally used comma's instead of dots in vector.size() calls. Also, I noticed 2 arrays stopping one item too early in copying left and right vectors.
Your code did not work. Above code compiles fine, but produces 1 3 5 2 4 7 as output. Also, have you thought of a vector of uneven length, such as 5 4 3 2 1?
At this point, the code will not split properly

Quicksort Algorithm,Incorrect answer and segmentation fault with some specific input sequences

I am learning c++ and I am relatively new to programming. I wrote a C++ program that implements the quick sort algorithm using the last element as the pivot. Whenever I try to execute it, the answer is always wrong and for some specific input sequences I get a segmentation fault error.
I have tried playing around with the while loop and changing it to "if" statements to see if anything happens. The results change but they are incorrect.
// Example program
#include <iostream>
using namespace std;
int partition(int a[],int l,int r)
{
//int l=0,r=p-1;
int p=r+1;
while(r>l)
{
while (a[l]<a[p])
{
l=l+1;
}
while(a[r]>a[p])
{
r=r-1;
}
//if(a[l]>a[r]){
int f=a[r];
a[r]=a[l];
a[l]=f;
//}
}
int k=a[l];
a[l]=a[p];
a[p]=a[l];
p=l;
return p;
}
void quicksort(int a[],int l,int r)
{
int p;
if (l<r){
p=partition(a,l,r);
quicksort(a,0,p-2);
quicksort(a,p+1,r);
}
}
int main(){
int k;
cout<<"enter the number of elements in array";
cin>>k;
int a[k];
for (int i=0;i<k;i++)
{
cin>>a[i];
}
//int p=k-1;
int l=0;
int r=k-2;
quicksort(a,l,r);
for (int i=0;i<k;i++)
{
cout<<a[i];
}
return 0;
}
actual results:
enter the number of elements in array
4
3
0
1
2
sorted result
1322
expected results:
0123
If the posted code is compiled with warnings enabled, the following diagnostic is produced:
prog.cc:25:9: warning: unused variable 'k' [-Wunused-variable]
int k=a[l];
^
prog.cc:47:10: warning: variable length arrays are a C99 feature [-Wvla-extension]
int a[k];
^
The first one is generated by what seems to be a typo in function partition:
int k=a[l];
a[l]=a[p];
a[p]=a[l]; // <-- That should be 'a[p] = k;' to swap the values
Of course, the proper way of swapping those two values should be
std::swap(a[l], a[p]);
The second warning is easily fixed by using the proper data structure, which in C++ is a std::vector and passing a reference to it to the other functions, instead of a int *.
Those aren't the only issues in OP's code, which seems to implement a variant of the Quicksort algorithm using the Lomuto partition scheme.
In OP's code the first call is something like
quicksort(a, 0, k - 2); // k beeing the size of the VLA, it skips the last element
While, using a vector and following the convention of denoting a range by its first element and the one past the end, we could write the entry point as
// Note that std::vector::size() returns an unsigned type
quicksort(a, 0, a.size());
So that the quicksort function could be implemented as
void quicksort(std::vector<int> &a, size_t low, size_t high)
{
if ( low < high) {
size_t p = partition(a, low, high);
quicksort(a, low, p); // <- Note that OP's code uses '0' instead of 'low'
quicksort(a, p + 1, high);
}
}
If I correctly guessed the variant which the OP is trying to implement, the partition function could be simplified (and fixed) to
size_t partition(std::vector<int> &a, size_t low, size_t high)
{
size_t p = high - 1; // <- Assumes high > 0
size_t i = low;
for( size_t j = low; j < p; ++j )
{
if(a[j] < a[p]) {
std::swap(a[i], a[j]);
++i;
}
}
std::swap(a[i], a[p]);
return i;
}

Mergesort incorrect output c++

Input : {10,9,8,7,6,5,4,3,2,1}
Output : {8,7,6,9,10,5,4,3,2,1}
I'm not sure what the issue is. I think it has something to do with the recursion in mergesort. I'm new to recursion so my understanding is not too good. Any hints?
#include <iostream>
void mergeSort(int a[], int w[], int n);
void merge(int a[], int w[], int n);
using namespace std;
void mergeSort(int a[], int t[], int n) {
if (n > 1) {
for (int i = 0; i < n/2; i++) {
t[i] = a[i];
}
mergeSort(a, t, n/2);
for (int i = n/2; i < n; i++) {
t[i] = a[i];
}
mergeSort(a, t, n/2);
merge(a, t, n/2);
}
}
void merge(int a[], int t[], int n) {
int leftIndex = 0, leftEnd = n/2;
int rightIndex = n/2, rightEnd = n;
int targetIndex = 0;
while (leftIndex < leftEnd && rightIndex < rightEnd) {
if (t[leftIndex] < t[rightIndex])
a[targetIndex++] = t[leftIndex++];
else
a[targetIndex++] = t[rightIndex++];
}
while (leftIndex < leftEnd) {
a[targetIndex++] = t[leftIndex++];
}
while (rightIndex < rightEnd) {
a[targetIndex++] = t[rightIndex++];
}
}
int main() {
const int SIZE = 10;
int a[] = {10,9,8,7,6,5,4,3,2,1};
int w[SIZE];
mergeSort(a,w,SIZE);
for (int i = 0; i < SIZE; i++) {
cout << a[i] << " ";
}
cout << endl;
}
The general problem is pointer confusion. One of the C language quirks that is not immediately obvious is that in
void mergeSort(int a[], int t[], int n);
both a and t are not arrays, but pointers. There is a special rule for this in the language standard. What this means is that in all instantiations of mergeSort on the call stack, t and a refer to the same areas of memory, and this means that every time you do something like
for (int i = 0; i < n/2; i++) {
t[i] = a[i];
}
you're changing the same region of memory. After you've done so and returned to the previous call frame, this region no longer contains the data you expect it to contain.
The way to solve this is to define a temporary local buffer where you need it, which is in merge. For example:
const int SIZE = 10;
// mergeSort is much simpler now:
void mergeSort(int a[], int n) {
if (n > 1) {
// sort the left side, then the right side
mergeSort(a , n / 2);
mergeSort(a + n / 2, n - n / 2);
// then merge them.
merge(a, n);
}
}
// Buffer work done in merge:
void merge(int a[], int n) {
// temporary buffer t, big enough to hold the left side
int t[SIZE];
int leftIndex = 0 , leftEnd = n / 2;
int rightIndex = n / 2, rightEnd = n ;
int targetIndex = 0;
// copy the left side of the target array into the temporary
// buffer so we can overwrite that left side without worrying
// about overwriting data we haven't yet merged
for(int i = leftIndex; i < leftEnd; ++i) {
t[i] = a[i];
}
// then merge the right side and the temporary buffer to
// the left side. By the time we start overwriting stuff on
// the right side, the values we're overwriting will have been
// merged somewhere into the left side, so this is okay.
while (leftIndex < leftEnd && rightIndex < rightEnd) {
if (t[leftIndex] < a[rightIndex]) {
a[targetIndex++] = t[leftIndex++];
} else {
a[targetIndex++] = a[rightIndex++];
}
}
// If there's stuff in the temporary buffer left over,
// copy it to the end of the target array. If stuff on the
// right is left over, it's already in the right place.
while (leftIndex < leftEnd) {
a[targetIndex++] = t[leftIndex++];
}
}
Before explaining the errors, let me first emphasize that a function argument like int a[] is nothing more than a pointer passed to the function. It points to a region of memory.
Now, mergesort requires some temporary memory and works by
copying the data to the temporary memory;
sorting each half of the data in temporary memory;
merging the two halves, whereby writing into the original array.
In step 2, the original array is not needed and can serve as temporary memory for the recursion.
In view of these facts, your code contains two errors:
You don't use the arrays t[] and a[] appropriately. The idea is that a[] is both input and output and t[] a temporary array. Internally, data are first copied to the temporary array, each half of which is sorted, before merging them fills the original array a[].
You don't sort the second half of the temporary array, but the first half twice.
A correct implementation is, for example,
void mergeSort(int*a, int*t, int n) {
if (n > 1) {
for (int i = 0; i < n; i++)
t[i] = a[i]; // copy to temporary
mergeSort(t , a , n/2); // sort 1st half of temporary
mergeSort(t+n/2, a+n/2, n-n/2); // sort 2nd half of temporary
merge(a, t, n);
}
}
Note that since t[] and a[] are pointers, the operation t+n/2 simply obtains the pointer to the second half of the array. The result of your code with this alteration is 1 2 3 4 5 6 7 8 9 10.

Programming Quicksort Algorithm Q

I'm trying to write quicksort and understand the algorithm. I understand the 3 basic principles of quick sort
The element a[i] is in its final place in the array for some i.
None of the elements in a[l], a[i1] is greater than a[i].
None of the elemtns in a[i+1],..., a[r] is less than a[i].
I think I'm missing something about this algorithm. Any guidance much appreciated.
My first question is l and r, those are the min and max of the array? or is it any place within the left and right side of the array.
#include <iostream>
using namespace std;
void quicksort(int a[], int l , int r);
int partition(int a[], int l, int r);
void exchange(int a[], int i, int j);
int main()
{
const int MAX_ARRAY = 9;
// Quicksort
// Array of integers
int numArray[MAX_ARRAY] = {25,10,25,34,38,7,6,43,56};
for ( int i = 0 ; i < MAX_ARRAY ; i++)
{
cout << numArray[i] << endl;
}
quicksort(numArray, 4, 7);
// Call quicksort
for ( int i = 0 ; i < MAX_ARRAY ; i++)
{
cout << numArray[i]<< endl;
}
system("pause");
return 0;
}
void quicksort(int a[], int l , int r)
{
//
if (r <= l) {return;} // The max position and least position are now overlapping
int i = partition(a, l, r); // send the array and the two positions to partition
// i gives me the next position
quicksort(a,l,i-1); // sort left side
quicksort(a,i+1,r); // sort right side
}
int partition(int a[], int l, int r)
{
//Declarations
int i = l-1, j = r; int v = a[r];
for(;;) // Infinite ForLoop
{
// go through till you find a value in the array that is less than v = our pivot
while(a[++i] < v) ;
while (v < a[--j]) if (j == 1) break; // THis condition is to go thorugh and check for a number that is larger than v then if j is not at 1 then we break
if ( i >= j) break; // Overlap array
exchange(a, i , j); // swap the values
}
exchange(a,i,j); // swap the values
return i;
}
void exchange(int a[], int i, int j )
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
My first question is l and r, those are the min and max of the array? or is it any place within the left and right side of the array.
No, those are the left and right borders of the current sub-array being sorted. I have no idea why you invoke method with parameters 4 and 7: it means none of the elements before 4-th or after 7-th will be sorted.
first, quicksort(numArray, 4, 7); should be quicksort(numArray, 0, 8);
second, to make it a little bit faster, you can set a cutoff number like 10 on which you switch to insertion sort. Insertion sort is faster than quick sort for small input.