algorithm - problems with implementation of quicksort - c

I am learning algorithms as a beginner.When learning quicksort,I found that there're several ways to implement the quick sort.So I choose this the way in this tutorial to implement quicksort.
It chooses the element in end of the array as pivot,and choose the first element as a wall separating the values which are less and greater than the pivot.when the sort is done, insert the pivot in the position of wall and make partition.
Here's my implementation in c:
void simpleqs(int* data,int start,int end)
{
int wall = start;
int pivot = data[end];
//Base case
if (start >= end)
{
return;
}
//Sort the array
for(int e = start;e < end-1; e++)
{
if(data[e] < pivot)
{
swap(&data[e],&data[wall]);
wall++;
}
}
//Partition
if(data[wall] >= pivot)
{
swap(&data[wall],&data[end]);
}
simpleqs(data,start,wall-1);
simpleqs(data,wall+1,end);
}
In main:
int main(void)
{
int dataSet[] = {2,4,1,5,6,9,8,3,7,10,20,13,11,17,15};
int size = sizeof(dataSet)/sizeof(int);
simpleqs(dataSet,0,size-1);
for(int e = 0;e < size ; e++)
{
printf("%d,", dataSet[e]);
}
printf("\n");
}
There will always be one value in the wrong position,I can't figure out why.
Like this:
1,2,3,4,5,6,8,9,7,10,11,13,15,17,20,
Please help me to revise my logic,thanks!

You should go to the end, not end - 1.
// Sort the array
for (int e = start; e < end; e++)
{
if (data[e] < pivot)
{
swap(&data[e], &data[wall]);
wall++;
}
}

You have implemented Lomuto partition, but missed treatment of (end-1)-th element:
for(int e = start;e < end; e++)

Related

CS50x pset3 (Why doesn't my program pass Check50?)

I completed my Problem Set 3 helpers.c program and it works perfectly up to a total of 10 of haystack but stops working when I press Control-D with less than 10 in haystack. Instead the program skips a line and I can freely write like in a not pad. Since, it can not pass 3 or 4 in haystacks, my program can't pass Check50. Does anybody have a solution to this problem?
In case you need my code, here it is:
bool search(int value, int values[], int n)
{
if(value < 0)
{
return false;
}
for(int i = 0; i < n; i++)
{
if (value == values[i])
{
return true;
}
}
return false;
}
/**
* Sorts array of n values.
*/
void sort(int values[], int n)
{
bool tf;
do
{
tf = false;
for(int i=0; i < n-1; i++)
{
if(values[i] > values[i+1])
{
int temp = values[i];
values[i] = values[i+1];
values[i+1] = temp;
tf = true;
}
}
}
while(tf == false);
return;
}
Your sorting algorithm is flawed. It just takes one round of the whole array and arranges it partially in order but not fully.
You can use bubble sort (refer to https://en.wikipedia.org/wiki/Bubble_sort or the lecture of cs50) of which I cannot provide code as it is against the honour code of the course.

Transform an array to another array by shifting value to adjacent element

I am given 2 arrays, Input and Output Array. The goal is to transform the input array to output array by performing shifting of 1 value in a given step to its adjacent element. Eg: Input array is [0,0,8,0,0] and Output array is [2,0,4,0,2]. Here 1st step would be [0,1,7,0,0] and 2nd step would be [0,1,6,1,0] and so on.
What can be the algorithm to do this efficiently? I was thinking of performing BFS but then we have to do BFS from each element and this can be exponential. Can anyone suggest solution for this problem?
I think you can do this simply by scanning in each direction tracking the cumulative value (in that direction) in the current array and the desired output array and pushing values along ahead of you as necessary:
scan from the left looking for first cell where
cumulative value > cumulative value in desired output
while that holds move 1 from that cell to the next cell to the right
scan from the right looking for first cell where
cumulative value > cumulative value in desired output
while that holds move 1 from that cell to the next cell to the left
For your example the steps would be:
FWD:
[0,0,8,0,0]
[0,0,7,1,0]
[0,0,6,2,0]
[0,0,6,1,1]
[0,0,6,0,2]
REV:
[0,1,5,0,2]
[0,2,4,0,2]
[1,1,4,0,2]
[2,0,4,0,2]
i think BFS could actually work.
notice that n*O(n+m) = O(n^2+nm) and therefore not exponential.
also you could use: Floyd-Warshall algorithm and Johnson’s algorithm, with a weight of 1 for a "flat" graph, or even connect the vertices in a new way by their actual distance and potentially save some iterations.
hope it helped :)
void transform(int[] in, int[] out, int size)
{
int[] state = in.clone();
report(state);
while (true)
{
int minPressure = 0;
int indexOfMinPressure = 0;
int maxPressure = 0;
int indexOfMaxPressure = 0;
int pressureSum = 0;
for (int index = 0; index < size - 1; ++index)
{
int lhsDiff = state[index] - out[index];
int rhsDiff = state[index + 1] - out[index + 1];
int pressure = lhsDiff - rhsDiff;
if (pressure < minPressure)
{
minPressure = pressure;
indexOfMinPressure = index;
}
if (pressure > maxPressure)
{
maxPressure = pressure;
indexOfMaxPressure = index;
}
pressureSum += pressure;
}
if (minPressure == 0 && maxPressure == 0)
{
break;
}
boolean shiftLeft;
if (Math.abs(minPressure) > Math.abs(maxPressure))
{
shiftLeft = true;
}
else if (Math.abs(minPressure) < Math.abs(maxPressure))
{
shiftLeft = false;
}
else
{
shiftLeft = (pressureSum < 0);
}
if (shiftLeft)
{
++state[indexOfMinPressure];
--state[indexOfMinPressure + 1];
}
else
{
--state[indexOfMaxPressure];
++state[indexOfMaxPressure + 1];
}
report(state);
}
}
A simple greedy algorithm will work and do the job in minimum number of steps. The function returns the total numbers of steps required for the task.
int shift(std::vector<int>& a,std::vector<int>& b){
int n = a.size();
int sum1=0,sum2=0;
for (int i = 0; i < n; ++i){
sum1+=a[i];
sum2+=b[i];
}
if (sum1!=sum2)
{
return -1;
}
int operations=0;
int j=0;
for (int i = 0; i < n;)
{
if (a[i]<b[i])
{
while(j<n and a[j]==0){
j++;
}
if(a[j]<b[i]-a[i]){
operations+=(j-i)*a[j];
a[i]+=a[j];
a[j]=0;
}else{
operations+=(j-i)*(b[i]-a[i]);
a[j]-=(b[i]-a[i]);
a[i]=b[i];
}
}else if (a[i]>b[i])
{
a[i+1]+=(a[i]-b[i]);
operations+=(a[i]-b[i]);
a[i]=b[i];
}else{
i++;
}
}
return operations;
}
Here -1 is a special value meaning that given array cannot be converted to desired one.
Time Complexity: O(n).

Searching array twice

This is a hackerrank problem.
Find out the maximum movies that can be watched. Assume the list to be distinct movies.
Input:
movieStart[] = {10,12,9,14,16,14}
movieEnd[] = {11,13,15,16,18,18}
Output : 4 (10-11,12-13,14-16,16-18)
Below code is giving me correct output. Would like to know the best solution possible for this problem.
Also what algorithm is used to solve this kind of problem?
static int getMaxMovies(int[] movie_start, int[] movie_end) {
int cnt = 0;
for (int i = 0; i < movie_start.length; i++) {
for (int j = 0; j < movie_start.length; j++) {
if (movie_start[j] == movie_start[i] && movie_end[j] == movie_end[i]) {
continue;
}
if (movie_start[j] >= movie_start[i] && movie_end[j] <= movie_end[i]) {
cnt += 1;
break;
}
}
}
return movie_start.length - cnt;
}
That's "Activity selection" problem - which could be easily solved with O(n) (in case of sorted input) or O(nlogn) (when input isn't sorted) greedy algorithm. If input is already sorted by finishing (end) time you should pick movies which doesn't conflict with previously selected movie.
Pseudocode:
Sort by finish times
int previouslySelectedIndex = 0;
int watchedMoviesCount = 1;
for (int i=1; i<movie_end.Length; i++)
{
if (movie_start[i] >= movie_end[previouslySelectedIndex)
{
watchedMoviesCount++;
previouslySelectedIndex=i;
}
}

searching within an array of structs in c

i see plenty of examples on how to search arrays to find a specific instance what i want to do is find all the instances and print them for example i have this struct
struct BookInfo
{
char title[50];
int numAuthors;
char authors[50][50];
int year;
int checkedout;
};
struct BookInfo library[500];
and i have a function to search within the years but it only gives me the first instance it finds how do i get it to give me bot instances???
heres the function
int yearsearch()
{
int target, i, l, r, mid;
l = 0;
r = 14-1;
printf("type a year to search for book");
scanf("%d", &target);
while(l <= r)
{
mid = (l+r)/2;
if(library[mid].year == target)
{
printf("\n%s",library[mid].title);
printf(" %d",library[mid].year);
printf("These are all the books with that year");
break;
}
else if (library[mid].year < target)
{
l = mid + 1;
}
else
{
r = mid - 1;
}
if(l > r)
printf("The target is not in the array.\n");
}
menu();
}
You're doing a kind of binary search over the array, which is not designed to find all instances without modification. Have you considered just doing a linear search (i.e., a for loop) over the length of the array and printing if the array element matches your search criteria? One might implement a naive linear search like this:
for (int i = 0; i<500; i++) {
if (library[i].year == target) {
// Do your printing here
}
// Otherwise do nothing!
}

Removing Duplicates in an array in C

The question is a little complex. The problem here is to get rid of duplicates and save the unique elements of array into another array with their original sequence.
For example :
If the input is entered b a c a d t
The result should be : b a c d t in the exact state that the input entered.
So, for sorting the array then checking couldn't work since I lost the original sequence. I was advised to use array of indices but I don't know how to do. So what is your advise to do that?
For those who are willing to answer the question I wanted to add some specific information.
char** finduni(char *words[100],int limit)
{
//
//Methods here
//
}
is the my function. The array whose duplicates should be removed and stored in a different array is words[100]. So, the process will be done on this. I firstly thought about getting all the elements of words into another array and sort that array but that doesn't work after some tests. Just a reminder for solvers :).
Well, here is a version for char types. Note it doesn't scale.
#include "stdio.h"
#include "string.h"
void removeDuplicates(unsigned char *string)
{
unsigned char allCharacters [256] = { 0 };
int lookAt;
int writeTo = 0;
for(lookAt = 0; lookAt < strlen(string); lookAt++)
{
if(allCharacters[ string[lookAt] ] == 0)
{
allCharacters[ string[lookAt] ] = 1; // mark it seen
string[writeTo++] = string[lookAt]; // copy it
}
}
string[writeTo] = '\0';
}
int main()
{
char word[] = "abbbcdefbbbghasdddaiouasdf";
removeDuplicates(word);
printf("Word is now [%s]\n", word);
return 0;
}
The following is the output:
Word is now [abcdefghsiou]
Is that something like what you want? You can modify the method if there are spaces between the letters, but if you use int, float, double or char * as the types, this method won't scale at all.
EDIT
I posted and then saw your clarification, where it's an array of char *. I'll update the method.
I hope this isn't too much code. I adapted this QuickSort algorithm and basically added index memory to it. The algorithm is O(n log n), as the 3 steps below are additive and that is the worst case complexity of 2 of them.
Sort the array of strings, but every swap should be reflected in the index array as well. After this stage, the i'th element of originalIndices holds the original index of the i'th element of the sorted array.
Remove duplicate elements in the sorted array by setting them to NULL, and setting the index value to elements, which is the highest any can be.
Sort the array of original indices, and make sure every swap is reflected in the array of strings. This gives us back the original array of strings, except the duplicates are at the end and they are all NULL.
For good measure, I return the new count of elements.
Code:
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
void sortArrayAndSetCriteria(char **arr, int elements, int *originalIndices)
{
#define MAX_LEVELS 1000
char *piv;
int beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R;
int idx, cidx;
for(idx = 0; idx < elements; idx++)
originalIndices[idx] = idx;
beg[0] = 0;
end[0] = elements;
while (i>=0)
{
L = beg[i];
R = end[i] - 1;
if (L<R)
{
piv = arr[L];
cidx = originalIndices[L];
if (i==MAX_LEVELS-1)
return;
while (L < R)
{
while (strcmp(arr[R], piv) >= 0 && L < R) R--;
if (L < R)
{
arr[L] = arr[R];
originalIndices[L++] = originalIndices[R];
}
while (strcmp(arr[L], piv) <= 0 && L < R) L++;
if (L < R)
{
arr[R] = arr[L];
originalIndices[R--] = originalIndices[L];
}
}
arr[L] = piv;
originalIndices[L] = cidx;
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
}
else
{
i--;
}
}
}
int removeDuplicatesFromBoth(char **arr, int elements, int *originalIndices)
{
// now remove duplicates
int i = 1, newLimit = 1;
char *curr = arr[0];
while (i < elements)
{
if(strcmp(curr, arr[i]) == 0)
{
arr[i] = NULL; // free this if it was malloc'd
originalIndices[i] = elements; // place it at the end
}
else
{
curr = arr[i];
newLimit++;
}
i++;
}
return newLimit;
}
void sortArrayBasedOnCriteria(char **arr, int elements, int *originalIndices)
{
#define MAX_LEVELS 1000
int piv;
int beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R;
int idx;
char *cidx;
beg[0] = 0;
end[0] = elements;
while (i>=0)
{
L = beg[i];
R = end[i] - 1;
if (L<R)
{
piv = originalIndices[L];
cidx = arr[L];
if (i==MAX_LEVELS-1)
return;
while (L < R)
{
while (originalIndices[R] >= piv && L < R) R--;
if (L < R)
{
arr[L] = arr[R];
originalIndices[L++] = originalIndices[R];
}
while (originalIndices[L] <= piv && L < R) L++;
if (L < R)
{
arr[R] = arr[L];
originalIndices[R--] = originalIndices[L];
}
}
arr[L] = cidx;
originalIndices[L] = piv;
beg[i + 1] = L + 1;
end[i + 1] = end[i];
end[i++] = L;
}
else
{
i--;
}
}
}
int removeDuplicateStrings(char *words[], int limit)
{
int *indices = (int *)malloc(limit * sizeof(int));
int newLimit;
sortArrayAndSetCriteria(words, limit, indices);
newLimit = removeDuplicatesFromBoth(words, limit, indices);
sortArrayBasedOnCriteria(words, limit, indices);
free(indices);
return newLimit;
}
int main()
{
char *words[] = { "abc", "def", "bad", "hello", "captain", "def", "abc", "goodbye" };
int newLimit = removeDuplicateStrings(words, 8);
int i = 0;
for(i = 0; i < newLimit; i++) printf(" Word # %d = %s\n", i, words[i]);
return 0;
}
Traverse through the items in the array - O(n) operation
For each item, add it to another sorted-array
Before adding it to the sorted array, check if the entry already exists - O(log n) operation
Finally, O(n log n) operation
i think that in C you can create a second array. then you copy the element from the original array only if this element is not already in the send array.
this also preserve the order of the element.
if you read the element one by one you can discard the element before insert in the original array, this could speedup the process.
As Thomas suggested in a comment, if each element of the array is guaranteed to be from a limited set of values (such as a char) you can achieve this in O(n) time.
Keep an array of 256 bool (or int if your compiler doesn't support bool) or however many different discrete values could possibly be in the array. Initialize all the values to false.
Scan the input array one-by-one.
For each element, if the corresponding value in the bool array is false, add it to the output array and set the bool array value to true. Otherwise, do nothing.
You know how to do it for char type, right?
You can do same thing with strings, but instead of using array of bools (which is technically an implementation of "set" object), you'll have to simulate the "set"(or array of bools) with a linear array of strings you already encountered. I.e. you have an array of strings you already saw, for each new string you check if it is in array of "seen" strings, if it is, then you ignore it (not unique), if it is not in array, you add it to both array of seen strings and output. If you have a small number of different strings (below 1000), you could ignore performance optimizations, and simply compare each new string with everything you already saw before.
With large number of strings (few thousands), however, you'll need to optimize things a bit:
1) Every time you add a new string to an array of strings you already saw, sort the array with insertion sort algorithm. Don't use quickSort, because insertion sort tends to be faster when data is almost sorted.
2) When checking if string is in array, use binary search.
If number of different strings is reasonable (i.e. you don't have billions of unique strings), this approach should be fast enough.

Resources