C homework help: Searching through arrays [closed] - c

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I'm working on a problem where I'm given a sorted array of integers, and I am supposed to take an input from the user, search for the input, and return the first instance of that input (if it exists) and the number of times it appears.
I wrote a program with the following approach: I take an input from the user. I then use binary search to find the value. If it exists, then I store the index as m. After that, I write two while loops. The first loop checks for the number of occurrences of the value to the left and the second does the same, but to the right. For example, the binary search might be looking for 5, and it finds it. However, it lands on the 3rd one, ie. {.....5,5,**5**,5....}. The first while loop will count the two the the left and the second while loop will count the one to the right. Then I'll sum them all up and return the total number of instances. If the the input value does not exist, then I skip the afore-mentioned code and simply return -1.
In the body of the main function, I then check the return value. If it is -1, I tell the user that the value has not been found. If the return value is >=0, then I print the required info.
Anyways, I have written up the C code for the program, but I cannot get it to work properly. I get a seg. fault error, I don't know what I'm doing wrong though. Anyways, any help would be appreciated. I've been pounding my head on this problem for awhile. It's been interesting and hard, and I think I have the right logic; but I cannot get it to work properly. Anyways, here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
/* function prototype */
int get_num_of_ints( const int* arr, size_t r, int N, size_t* f, size_t* count );
int main()
{
int i;
int N; /* input variable */
int arr[]={1,1,2,3,4,4,4,4,5,5,6,7,7,7,7,8,9,9}; /* array of sorted integers */
size_t r = sizeof(arr[i])/sizeof(int); /* right bound */
size_t f; /* first match index */
size_t *fPtr;
fPtr = &f;
size_t count; /* total number of matches */
size_t *countPtr;
countPtr = &count;
printf( "\nPlease input the integer you would like to find.\n" );
scanf( "%d", &N );
int a = get_num_of_ints( arr, r, N, fPtr, countPtr );
if( a == -1)
printf( "%d has not been found.\n", N );
else if(a >= 0){
printf( "The first index is %d.\n", f );
printf( "The total number of values is %d.\n", count );
}
return 0;
}
/* function definition */
int get_num_of_ints( const int* arr, size_t r, int N, size_t* f, size_t* count )
{
int l = 0;
int m;
int w=r;
size_t *fPtr;
size_t *countPtr;
while(l <= r){
m = l +(r - l)/2;
if(arr[m] < N)
l = m+1;
else if(arr[m] > N)
r = m-1;
else if(arr[m]==N)
m=m;
break;
}
if( l > r)
m = -1;
if( m >= 0 ){
int j = m-1;
int L = 0;
while(arr[j] == arr[m] && j >= 0){
L++;
j--;
}
if( j>= 0 && L > 0 )
*fPtr=j;
else
*fPtr=m;
int h = m + 1;
int R = 0;
while( arr[h]==arr[m] && h <= w ){
R++;
h++;
}
*countPtr = (R + L + 1);
return *fPtr;
}
else if( m==-1)
return -1;
}

while(arr[j] == arr[m] && j >= 0)
You should switch the order of the two conditions here, or you'll try to read arr[-1]. Same thing for the second while loop.
Another problem is that r should start at 1 less than the array size, since arr[array_size] is past the end.
Edit:
A serious problem is that you are writing to the uninitialized pointers countPtr and fPtr when you should be writing to *count and *f. This is probably what's causing the segfault. This would have been easy to spot in a debugger.

Use variable names that mean something. You might find the problem immediately.
Run the program in a debugger and step through the code; you should see where things are going wrong pretty quickly. (Hint, what's fPtr used for in get_num_of_ints? There are other bugs too as others have pointed out).

Since you need the number of occurrences, you need to search through each element, right?
Why not simplify things and just do a linear scan? Here's some pseudocode:
function get_num_of_ints(arr, n){
first_index = -1
count = 0
for(i = 0; i < length(arr); i++)
if(x == n){
count++
if(first_index == -1)
first_index = i
}
return count, first_index
}

I don't have a C compiler in the computer I'm sitting now, so I can't test it, but I see that in the first while-loop of your function you're saying:
else if(arr[m]==N)
m=m;
break;
The break statement is outside the if in this case, so the while loop will execute only once each time.
I don't know if this causes the error though.

The segmentation fault comes from get_num_of_ints() in lines 74, 77, and 87.
if( j>= 0 && L > 0 )
*fPtr=j;
else
*fPtr=m;
...
*countPtr = (R + L + 1);
You have not assigned a memory address to the pointers, and thus you are using an arbitrary memory location in these lines.
It doesn't seem like there's any real reason to be using a pointer for these variables anyway. Try changing them from pointers to size_t to just variables of type size_t.
size_t fPtr;
size_t countPtr;

In get_num_of_ints() function you are using pointer fptr without allocating memory to it.

Thanks to everybody's comments, I was able to find all the bugs in my program, and use the gdb debugger too. Anyways, I no longer have my seg. fault errors when I run the program; however, I have some sort of logic problem becauase when I prompt the user for an input,and say the user types 4, then the outputs for # of occurence and location of first occurrence are garbage values.
I get:
Please input the integer you would like to find.
4
The first index is -1075300456.
The total number of values is 12169204.
This revolves around the issue I had earlier with the last two parameters in my function. At the bottom, in the function definition, I want count to be the total number of occurrences found in the list.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
/* function prototype */
int get_num_of_ints( const int* arr, size_t r, int N, size_t f, size_t count );
int main()
{
int i;
int N; /* input variable */
int arr[]={1,1,2,3,4,4,4,4,5,5,6,7,7,7,7,8,9,9}; /* array of sorted integers */
size_t r = sizeof(arr)/sizeof(arr[0]) - 1; /* right bound */
size_t f; /* first match index */
size_t count; /* total number of matches */
printf( "\nPlease input the integer you would like to find.\n" );
scanf( "%d", &N );
int a = get_num_of_ints( arr, r, N, f, count );
if( a == -1)
printf( "%d has not been found.\n", N );
else if(a >= 0){
printf( "The first index is %d.\n", f );
printf( "The total number of values is %d.\n", count );
}
return 0;
}
/* function definition */
int get_num_of_ints( const int* arr, size_t r, int N, size_t f, size_t count )
{
int l = 0;
int m;
int w=r;
while(l <= r){
m = l +(r - l)/2;
if(arr[m] < N)
l = m+1;
else if(arr[m] > N)
r = m-1;
else if(arr[m]==N){
m=m;
break;
}
}
if( l > r)
m = -1;
if( m >= 0 ){
int j = m-1;
int L = 0;
while( j >= 0 && arr[j] == arr[m] ){
L++;
j--;
}
if( j>= 0 && L > 0 )
f=j;
else
f=m;
int h = m + 1;
int R = 0;
while( arr[h]==arr[m] && h <= w ){
R++;
h++;
}
count = (R + L + 1);
return f;
}
else if( m==-1)
return -1;
}

As everyone pointed out countPtr and fPtr needs to be allocated with memory
size_t *fPtr = malloc(sizeof(size_t));
Make sure you intialize "i" with some value before using it
Compile the program (if you are in Linux) with # gcc -g -Wall <your c file name> -o arraysort
Start the debugger, step through the code to find out valeues, I found several funny values for r, arr[m],
#gdb ./arraysort
b
run
[inspect variables here]
HTH

Related

I have troubles printing arrays of characters in C

the problem is that the following code prints nothing. And I tried very hard, using different methods, I used fixed sized arrays, I tried to print the array from a void function, I tried printf and sprintf, I tried with static s variable, I tried to loop the array and print charcacter the result is always the same, 0 errors, 0 warnings and never print the result. After about 30 seconds, the program automatically terminate with the following output:
Convert 56 to ascii:
Process returned -1073741819 (0xC0000005) execution time : 4.763 s
Press any key to continue.
Here's the code (I maybe used too many includes, but this is because I tried everything):
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void reverse(char s[])
{
int c, i, j;
for(i = 0, j = strlen(s)-1; i < j; i++,j++){
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
char * itoascii(int n)
{ char *s = malloc(10);
/*if(s == NULL)
return NULL;*/
int i, sign;
if((sign = n) < 0)
n = -n; // if n is negative, make it positive. And store the sign into sign
i = 0;
do {
s[i++] = n % 10 + '0'; // turn a digit into a string and then increment i
}while(( n /= 10) > 0);
if(sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return s;
}
int main()
{ int n;
n = 56;
printf("Convert %d to ascii:\n", n);
char *buf = itoascii(n);
sprintf(buf, "%s\n");
return 0;
}
Yes, the problem was the y++ stuff. The fact is that I copied this code from a K&R edition with errata. In the book I found y++ and I blindy trusted the function, I never consider it in my debugging assuming the problem was due to improper pointer usage or other things.
Sure the code may be improved. printf es better than sprintf and I also must free the allocated memory with malloc. I also have to remove the extra unused include.
Thanks for your comments!
Code has at least these problems:
10 insufficient for large int. Suggest at least 12. Maybe sizeof(int)*CHAR_BIT/3 + 3 for an approximate generalization.
n = -n; is UB when n == INT_MIN.
Wrong increment
//for(i = 0, j = strlen(s)-1; i < j; i++,j++){
for(i = 0, j = strlen(s)-1; i < j; i++,j--){

Coast length, kattis

I'm trying to solve one problem, which I found on website https://open.kattis.com/problems/coast. Tl;dr version of problem is, that for given map of landscape, I should print out length of coastline (without inner islands).
I receive 0/26 mark, but I have no idea why, I've tested, and as far as i checked, it worked. I assume it doesn't compile, but if that is the case, why is that? It compiles for me perfectly fine.
#include <stdio.h>
int edgeCount(int, int, char*);
int topToBottomCount(int, int, char*);
int leftToRightCount(int, int, char*);
int removingInsides(int, int, char*);
int main()
{
int n = 0; // number of strings
int m = 0; // strings lenghts
//printf("Enter N(number of strings) x M(strings lenght): ");
scanf("%d", &n);
scanf("%d", &m);
char coast[1024];
for(int i = 0; i < n; i++){
scanf("%s", coast+i*m); // adding strings to char coast[1024], making array of ones and zeroes // e.g we are adding 3x4 strings - 111100001111
} // it can also be looked as 1111
// 0000 - matrix
int coasts = edgeCount(n, m, coast); // 1111
coasts += topToBottomCount(n, m, coast);
coasts += leftToRightCount(n, m, coast);
coasts -= removingInsides(n, m, coast);
printf("%d - coasts\n", coasts);
return 0;
}
int edgeCount(int n, int m, char *coast){ // if 1 is placed at the edge of the "map", it is 1 coast (2 if it is at corner)
int edgeCoast = 0;
for(int i = 0; i < m; i++){ // top edges
if(coast[i] == '1')
edgeCoast++;
}
for(int i = m*n - m; i < m*n; i++){ // bottom edges (m*n - m = first char in the last string, it can be also looked as the last row in matrix)
if(coast[i] == '1')
edgeCoast++;
}
for(int i = 0; i <m*n; i+=m){ // left side edges (first column in matrix)
if(coast[i] == '1')
edgeCoast++;
}
for(int i = m-1; i < m*n; i+=m){ // right side edges (last column in matrix)
if(coast[i] == '1')
edgeCoast++;
}
return edgeCoast;
}
int topToBottomCount(int n, int m, char *coast){
int coasts = 0;
for(int i = 0; i < m*n - m; i++){ // we start from first char in "matrix", and move to the (m*n - m = 2nd last "row")
if(coast[i] ^ coast[i+m]) // we are checking if zero is placed above one or via versa
coasts++;
}
return coasts;
}
int leftToRightCount(int n, int m, char* coast){
int coasts = 0;
int p = m-1;
for(int i = 0; i < n*m; i++){ // we start from the first charr, and we are going trough whole matrix, but the last column
if(i == p){ // p = m - 1 (last char in first row)
p+=m; // p+=m (last char in next column, and so on)
continue; // we move to next iteration
}
if(i == m*n - 1) //if we are at last char in matrix, we break out from loop
break;
if(coast[i] ^ coast[i+1])
coasts++;
}
return coasts;
}
int removingInsides(int n, int m, char* coast){ // Lakes and islands in lakes are not contributing to the sea coast. we are checking if they exist.
int innerCoasts = 0;
for(int i = m + 1; i < n*m - m - 1; i ++){
if( coast[i] == '0' && coast[i] ^ coast[i-1] && coast[i] ^ coast[i+1] && coast[i] ^ coast[i-m] && coast[i] ^ coast[i+m]) // char has to be 0, and to hist left, right, above and under there has to be 1
innerCoasts++;
}
return innerCoasts * 4; // *4 because we added 4 coasts before for each island.
}
I tried compiling your code using the GCC C++ compiler (4.9.2). It compiled fine and I tested it using the sample problem in the link you provided. It spit out the right answer.
However, when I tried compiling using the GCC C compiler (also v 4.9.2), it fails with 'for' loop initial declarations are only allowed in C99 or C11 mode, which is explained by this SO question. I think your assignment was graded using a C compiler and the compilation of your program failed due to this error.

How to resort an array with file input?

I am writing a program that will ask the user for a 'n' value, they will then enter 'n' values that will be stored into an array and sorted. I have this part done easy enough.
I will compare this array with input from a number read from a text file. If the number is bigger than any of the current array values it will replace them and slide the rest down. This creates an array of the largest 'n' values
Example: n = 4 n values are : 999 972 954 462 937
a[4] = {999, 972, 954, 462, 937};
Sorted :
a[4] = {999, 972, 954, 937, 462};
if the file input is say 968 the result is.
Resorted :
a[4] = {999, 972, 968, 937, 937};
This is my current code.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
if (argc<3) //error checking
return -1;
int size = atoi(argv[2]);
int a[size];
int i, j, temp=0;
printf("Enter %d numbers\n", size); //user array input for size and n values
for(i = 0; i < size; i++)
scanf("%d", &a[i]);
for(i=0; i < size; i++){ //sorting array
for(j = i+1; j <size; j++){
if( a[i] < a[j]){
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
FILE *input;
input = fopen(argv[1], "r");
if(input ==NULL) //error checking
return -1;
if(fscanf(input, "%d", &temp) != 1)
return -1;
while(fscanf(input, "%d", &temp) ==1){ //loop while there is file input
for(i =1 < size; i++){ //check if temp is larger than array values
if(temp > a[i] && temp < a[i-1]){
for(j = size-1; j >= i; j--) //slide down the rest of the array
a[j] = a[j-1];
a[i] = temp;
}
}
}
for(i=0; i <size; i++){ //print out array
printf("%d ", a[i]);
}
return (0);
}
I have tried this on a smaller simpler skill were I have already created the array and the values instead of using user input. I also just passed the array check sequence through a loop that increases a number value instead of reading from a file. This seemed to work fine with something like
a[5] = {10, 8, 6, 4, 2};
number = 5; // number++ number = 6 number = 7... until 10
result: a[5] = {10, 9, 8, 7, 6};
I am sad to say that even if the program was not printing the right array at the beginning I could see there were numbers from the file. The loop is still going trough the file, but at one point the output just started being the sorted user array. I can't seem to get the array values right. Any ideas?
Continuing from my comments yesterday, I apologize if the errors were due to the retyping of your code, but that is the whole reason you want to try to cut and paste as Jonathan indicated -- eliminate human error in the transcription process.
I think I understand what your primary problem is. If your goal is to read some number of user input values from stdin, sort them in descending order, and then open a file and read a single additional value into the array in sort-order, then you must provide space for the final value in your array at the time it is declared (if using VLA's). Otherwise, you either need to create a second VLA large enough to store the values from the use and the file, and copy the user provided values to the new array or dynamically allocate the array originally (with malloc or calloc) and then realloc as needed to add space for additional values as required.
In this case, it's not that difficult since you know you are reading one value from the file. Just read the size from the command line argument and then create your array as int a[size + 1];
The rest of your task can be handled in a couple of ways. After you read (and validate) the user input, you can sort your values in descending order, read the value from the file, and create an insert & shuffle routine to insert the value in the correct order and move the remaining array elements down, or (probably a less error prone method) is simply to add the element from the file to the end of the array, and call your sort routine again.
(note: you should get used to using qsort rather than attempting to reinvent the bubble-sort, etc.. It is orders of magnitudes more efficient and much less error prone)
You need limit (or eliminate) your use of atoi as it provides zero error checking. A better approach is to use strtol and then check errno and check the end-pointer against the original to determine if there were any digits read. Below a simple helper function incorporates error-checking for strtol to insure you have an actual value for size.
Further, be careful. While you may expect the user will enter size integer values, there is no guarantee they will. It is better to track the number of values actually entered and use that value in subsequent iterations over the array rather than blindly iterating for (i = 0; i < size; i++) throughout the remainder of your code.
Whether you attempt an insert-in-place of the value read from the file, or just add it to the end of the array and call your sort routine again is up to you. I would encourage you to move your sort code into a function to provide that flexibility without having to duplicate the code in main. Look the following over and let me know if you have any questions. Since I presume this was a homework assignment, the insert-in-place case is shown below (but the simple add the file value to the end and call sort again code is included commented out)
#include <stdio.h>
#include <stdlib.h> /* for strtol */
#include <limits.h> /* for LONG_MAX/MIN */
#include <errno.h> /* for ERANGE,errno */
void sort_int_array_dec (int *a, size_t size);
long xstrtol (char *p, char **ep, int base);
int main (int argc, char **argv) {
/* read size as first argument, or 5 if none given */
int size = argc > 2 ? (int)xstrtol (argv[2], NULL, 10) : 5,
a[size + 1], /* variable length array for user + file values */
n = 0, /* number of values from user */
fval, /* value read from file */
temp, /* temporary value for array */
i = 0;
FILE *fp = NULL;
if (size < 1) return 1;
printf ("enter %d integers\n", size);
while (n < size) { /* read up to size values */
int result, c;
printf (" integer[%2d] : ", n + 1);
/* validate read of each value using scanf return */
if ((result = scanf ("%d", &temp)) != 1) {
if (result == EOF) { /* always check for EOF */
fprintf (stderr, "user canceled input.\n");
break;
}
fprintf (stderr, "error: invalid conversion.\n");
/* empty input buffer of invalid entry */
while ((c = getchar()) != '\n' && c != EOF) {}
}
else /* good value read, save, increment n */
a[n++] = temp;
}
sort_int_array_dec (a, n); /* sort a */
printf ("\nsorted array before inserting value from file:\n\n");
for (int i = 0; i < n; i++)
printf ("a[%2d]: %d\n", i, a[i]);
if (!(fp = fopen (argv[1], "r"))) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
return 1;
}
if (fscanf (fp, "%d", &fval) != 1) { /* read value from file */
fprintf (stderr, "error: read of file value failed.\n");
return 1;
}
printf ("\n value from file: %d\n\n", fval);
/* add fval into array in descending sort order
* (you can add it anywhere and simply call sort again, e.g.)
*/
// a[n] = fval; /* add it to the end of the array */
// sort_int_array_dec (a, n + 1); /* sort a again */
for (i = 1; i < n + 1; i++) {
if (fval > a[i-1]) {
temp = a[i-1];
a[i-1] = fval;
break; /* temp now holds value to insert at i */
}
}
if (i == n + 1) /* if already at last element just set it */
a[n] = fval;
else /* otherwise, insert and shuffle remaining elements down */
for (int j = i; j < n + 1; j++) {
int mov = a[j];
a[j] = temp;
temp = mov;
}
printf ("sorted array after inserting value from file:\n\n");
for (int i = 0; i < n + 1; i++)
printf (" a[%2d]: %d\n", i, a[i]);
return 0;
}
/** sort integer array descending (your code) */
void sort_int_array_dec (int *a, size_t size)
{
size_t i, j;
int temp;
if (size < 2) return; /* nothing to sort */
for (i = 0; i < size; i++) {
for (j = i + 1; j < size; j++) {
if (a[i] < a[j]) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
/** a simple strtol implementation with error checking.
* any failed conversion will cause program exit. Adjust
* response to failed conversion as required.
*/
long xstrtol (char *p, char **ep, int base)
{
errno = 0;
char *endpt = ep ? *ep : NULL;
long tmp = strtol (p, &endpt, base);
/* Check for various possible errors */
if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
(errno != 0 && tmp == 0)) {
perror ("strtol");
exit (EXIT_FAILURE);
}
if (endpt == p) {
fprintf (stderr, "No digits were found\n");
exit (EXIT_FAILURE);
}
if (ep) *ep = endpt;
return tmp;
}
Example Use/Output
$ cat dat/file.txt
523
$ ./bin/addintoarray dat/file.txt 4
enter 4 integers
integer[ 1] : 400
integer[ 2] : 500
integer[ 3] : 600
integer[ 4] : 700
sorted array before inserting value from file:
a[ 0]: 700
a[ 1]: 600
a[ 2]: 500
a[ 3]: 400
value from file: 523
sorted array after inserting value from file:
a[ 0]: 700
a[ 1]: 600
a[ 2]: 523
a[ 3]: 500
a[ 4]: 400
/*I'm a beginner C programmer so I don't know much of the syntax.
But I think I can help you with that problem.
I created a simple code and I hope I can really help
the integers from the file must be already sorted.
So the only integer that we will sort is the recent integer that the user inputed.
*/
/*So here's my code of sorting array of integers coming from file.
Please give it a try.
It's not the same syntax as your code but I know you can see my point*/
#include <stdio.h>
#include <stdlib.h>
//my style here is I'm declaring the max Num that I want to put in array.
//But you can do this with different style.
#define MAX_ARRAY 10
//I created separate functions
void readFile(int num_arr[]);
void sortArray(int num_arr[]);
void writeFile(int num_arr[]);
int main()
{
int num_arr[MAX_ARRAY + 1]; // take note that I added 1 (one). And you will see later why I did that
readFile(num_arr);
sortArray(num_arr);
writeFile(num_arr);
//Now we can sort them. Use a temp_num holder.
return 0;
}
void readFile(int num_arr[])
{
int x = 0;
int y = 0;
int temp_num;
FILE *sample_file_pointer = fopen("sample_file.txt", "r");
//first I read the integers from the file and put them in int array.
while(fscanf(sample_file_pointer, " %d\n", &num_arr[x]) == 1)
{
x++;
}//after reading the integers, the last element of the array we declared is still unused.. Now we will use it.
fclose(sample_file_pointer);
//now we will use the unused element by entering the 'n'. Then we will sort the array later.
printf("Enter value of n: ");
scanf(" %d", &num_arr[MAX_ARRAY]);//We put the n value in the last element of the array
}
void sortArray(int num_arr[])
{
int x = MAX_ARRAY;//We will use this to point the last element of the array.
int temp_num;
/*because the array from
the file is already
sorted, (I know you can
do the sorting of that.
That's why I didn't include
it here to make this short)
we can just test the most recent
integer that is added by the user*/
//We do that with this loop
for(int i = MAX_ARRAY; i > 0; i--)
{
if(num_arr[x] >= num_arr[i - 1])
{
temp_num = num_arr[x];
num_arr[x] = num_arr[i - 1];
num_arr[i - 1] = temp_num;
//now set the x to the swapped element to follow the recent number all through. Till the element test becomes 1.
x = i - 1;
}
}
//now we're ready to write this sorted array to a file again
}
void writeFile(int num_arr[])
{
FILE *sample_file_pointer = fopen("sample_file.txt", "w");
for(int i = 0; i < MAX_ARRAY; i++)
{
fprintf(sample_file_pointer, "%d\n", num_arr[i]);
}
//We can ignore the last element of the array. She's out of the group now. It's her fault for being the lowest.. LOL..
fclose(sample_file_pointer);
}

Recursive finding the indexes of 2 biggest nums in massive (C)

I have broken my head trying to solve this question on an exam. I'm stil shocked by it.
Can anybody help me write a function:
void get2(int a[], int n, *i1, *i2)
that receives the massive a[] of integers and his length n and saves to pointers i1 and i2 the indexes of the two biggest numbers in the massive (i1 - the biggest, i2 - the second biggest).
The solution must be recursive and can not contain loops. It has be done by the complextiy of O(n). The numbers in the massive are all diffrent. We cant use any helper functions.
There aren't any additional conditions. I tried some solutions, but they werent good enough.
I dont know how to save the indexes without losing there values when I recurse.
There was additional hint, that I should pay attention to how I use the *i1, *i2 to pass information between recursive steps, but i tried to use it some how and it didn't work for me anything I tried. Help somebody ?
There is only one trick to play on this exercise: Recognize when we are in the outermost recursive call and when we are not, or simpler terms when the current call of get2 is the first one and when it is not.
Using the pointers and the n parameter to do some basic Divide et Impera strategy is straightforward and require no further explanation.
We need to tell the first and subsequent calls because in the first, and only there, call we need to initialize the pointers.
We cannot rely on i1 or i2 values as they are uninitialized.
Only n has enough structure to carry some information: When we are called from the extern, i.e. not by recurring, then n >= 0.
We can then perform the one-time initialization and then negate n. This way if n<=0 we know that we are in the recursive calls.
However we still have a problem: if n >= 0 then it's opposite is n <= 0. The two condition overlaps for n == 0.
This will make the initialization happen two time and further worst the second one at the end of the recursion stack thereby voiding the state calculated.
In order to avoid them, the n is not simply negated but negated and decremented by 1, so the 5 becomes -6.
We can then recover the usable value of n by again negating and decrementing by 1. In the code below this is done and stored in m.
The else part is straightforward as pointed above. We only additionally check that the second maximal element is not equal to the maximal element in the second if.
If one index is -1 it means that it is not pointing to any element and can be assigned unconditionally.
When the function return, i1 cannot point to -1 unless the array is empty.
i2 can point to -1 if all elements are the same.
#include <stdio.h>
void get2(int a[], int n, int* i1, int* i2)
{
/* Transform from encoded n to actual length */
int m = -n-1;
/* Is this the first call? */
if (n >= 0)
{
/* Initialize the pointer to -1, i.e. no elements */
*i1 = *i2 = -1;
/* Start the recursion, encode the length */
get2(a, -n-1, i1, i2);
}
/* Here we are in the subsequent calls, we use m and not n */
else if (m-- > 0)
{
/* Assign i1 */
if (*i1 == -1 || a[m] > a[*i1])
{
*i2 = *i1; /* Don't forget to update i2 too! */
*i1 = m;
}
/* Opportunity to assign i2, check that the second max != max */
else if ((*i2 == -1 || a[m] > a[*i2]) && a[m] != a[*i1])
{
*i2 = m;
}
/* Tail recursion, do proper encoding of the n parameter */
get2(a, -m-1, i1, i2);
}
}
This function can be tested with this code
void test(int a[], int n)
{
int max, smax, i;
get2(a, n, &max, &smax);
printf("------------------------------\n");
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\nMax is ");
if (max >= 0)
printf("%d", a[max]);
else
printf("NOT FOUND");
printf("\nSecond Max is ");
if (smax >= 0)
printf("%d\n", a[smax]);
else
printf("NOT FOUND\n");
}
int main()
{
int v1[] = {};
int v2[] = {1};
int v3[] = {1,2};
int v4[] = {2, 1};
int v5[] = {2, 2};
int v6[] = {1,2,3,4};
int v7[] = {4,3,2,1};
int v8[] = {2,1,4,3};
int v9[] = {2,2,2,2};
int v10[] = {1,2,3,4,3,2,1};
int v11[] = {1,1,2,3,4,4};
test(v1, 0);
test(v2, 1);
test(v3, 2);
test(v4, 2);
test(v5, 2);
test(v6, 4);
test(v7, 4);
test(v8, 4);
test(v9, 4);
test(v10, 7);
test(v11, 6);
return 0;
}
With the below output
------------------------------
Max is NOT FOUND
Second Max is NOT FOUND
------------------------------
1
Max is 1
Second Max is NOT FOUND
------------------------------
1 2
Max is 2
Second Max is 1
------------------------------
2 1
Max is 2
Second Max is 1
------------------------------
2 2
Max is 2
Second Max is NOT FOUND
------------------------------
1 2 3 4
Max is 4
Second Max is 3
------------------------------
4 3 2 1
Max is 4
Second Max is 3
------------------------------
2 1 4 3
Max is 4
Second Max is 3
------------------------------
2 2 2 2
Max is 2
Second Max is NOT FOUND
------------------------------
1 2 3 4 3 2 1
Max is 4
Second Max is 3
------------------------------
1 1 2 3 4 4
Max is 4
Second Max is 3
I will give you another hint instead of writing the actual code for you:
You have 4 arguments, use all of them to pass the relevant data to the recursive calls. What I mean by that is - the first 2 arguments can describe the part of the array that is interesting for us, the pointers store the largest integers we have found, you modify them each time you find a bigger one.
And here lies the trap that most likely caught you - those are int *, that is a pointer to an integer. What the function receives is simply an address in memory. If you set that pointer to point to anything, the caller will not receive that data. You can simply write directly to that address instead of setting the pointer, like so:
int myInteger;
int *ptr = &myInteger;
*ptr = 123;
Here is a non-threadsafe way to do it: by keeping track of the original start of the array using a static variable. The addresses of the largest elements are stored in the pointers, which are used as pointers to pointers, and right at the end they are replaced by the indexes of the elements by using some pointer arithmetic aided by the static variable.
void get2(int a[], int n, int *one, int *two) {
static int *arr_start = 0;
if (n > 0) {
if (arr_start == 0) {
arr_start = a;
}
if (a[0] > **((int **)one)) {
*one = a;
} else if (a[0] > **((int **)two)) {
*two = a;
}
get2(a + 1, n - 1, one, two);
} else {
*one = *((int **)one) - arr_start;
*two = *((int **)two) - arr_start;
}
}
EDIT
Without using static variables: Use the second pointer as an indicator of whether or not we have already allocated space to keep track of the the start of the array and the indexes. Use the allocated space to keep track of the initial array size and the two largest indexes, then assign them to the correct pointers at the end:
#include <stdlib.h>
void get2(int a[], int n, int *one, int *two) {
int **pp_one = (int **)one;
if (*two != NULL) {
*pp_one = malloc(sizeof(int) * 3);
(*pp_one)[0] = n;
(*pp_one)[1] = 0;
(*pp_one)[2] = 0;
}
if (n > 0) {
if (a[0] > (*pp_one)[1]) {
(*pp_one)[1] = a;
} else if (a[0] > (*pp_one)[2]) {
(*pp_one)[2] = a;
}
get2(a + 1, n - 1, one, two);
} else {
int *t = *one;
*two = (*pp_one)[1] - (*pp_one)[0];
*one = (*pp_one)[2] - (*pp_one)[0];
free(t);
}
}
Thanks for the brain teaser. It sounded like an interesting problem. Even though it's homework, I decided to solve it.
This gets a bit annoying to do without having the indices pre-initialized somehow, but it's doable. Sounds like an artificial restriction to create artificial pain.
void
get2(int a[], int n, int *i1, int *i2) {
if (n == 0) {
*i1 = *i2 = -1;
return;
}
get2(&a[1], n - 1, i1, i2);
if (*i1 != -1)
(*i1)++;
if (*i2 != -1)
(*i2)++;
if (*i1 == -1) {
*i1 = 0;
} else if (*i2 == -1 || a[*i2] < a[0]) {
*i2 = 0;
if (a[*i1] < a[0]) {
*i2 = *i1;
*i1 = 0;
}
}
}
Here we just recurse until we have no array left to look at, then initialize the indices and on the way back we figure out the indices we're interested in. On the way back through all the recursions we update the indices i1 and i2 if they are initialized to properly point into the array we have in this iteration, then if i1 isn't initialized yet, then we know that n == 0 and must be the biggest element. On iterations after that we know that is the only interesting element of the array is element 0, if that's not bigger than a[*i2], then it's not bigger than a[*i1] either. If it is (or we're just initializing i2), we compare to see if we need to swap i1 and i2.
"But wait!", you might yell. "You're not doing tail recursion, if the array is massive you will run out of stack.". Glad you mentioned it, in fact, C doesn't say anything about the compiler having to do optimizations for tail recursion so I'd like to argue that any solution has to account for this problem. Here's a solution that works on the same principles (run out of array before we initialize the indices) that solves this in O(log n) stack space:
void
get2(int a[], int n, int *i1, int *i2) {
/*
* Reset the indices at the end of the recursion.
*/
switch (n) {
case 0:
*i1 = -1;
*i2 = -1;
return;
case 1:
*i1 = 0;
*i2 = -1;
return;
}
/*
* Number of elements in the lower and upper halves of the array.
* Notice the '+ (n & 1)' which is there to add one element to the
* upper half of the array if n is odd.
*
* The asserts document the invariants.
*/
int lower_half = n / 2, upper_half = (n / 2) + (n & 1);
int li1, li2, ui1, ui2;
assert(lower_half >= 1);
assert(upper_half >= 1);
assert(lower_half + upper_half == n);
get2(&a[0], n - upper_half, &li1, &li2);
get2(&a[lower_half], n - lower_half, &ui1, &ui2);
ui1 += lower_half;
if (ui2 != -1)
ui2 += lower_half;
assert(li1 != -1);
assert(ui1 != -1);
assert(li2 == -1 || a[li2] < a[li1]);
assert(ui2 == -1 || a[ui2] < a[ui1]);
if (a[li1] < a[ui1]) {
*i1 = ui1;
if (ui2 == -1 || a[li1] > a[ui2])
*i2 = li1;
else
*i2 = ui2;
} else if (a[li1] > a[ui1]) {
*i1 = li1;
if (li2 == -1 || a[ui1] > a[li2])
*i2 = ui1;
else
*i2 = li2;
} else {
*i1 = li1;
if (li2 == -1 || a[ui2] > a[li2])
*i2 = ui2;
else
*i2 = li2;
}
}
I'm not going to explain it, the comments and asserts should be enough to show what's going on. I'm pretty sure that the big block of ifs at the end can be written much more efficiently, but I didn't bother.
It might not be perfectly correct, it worked on the first try with a few test cases taken from the previous answers and I didn't do more testing. There might be some edge cases I haven't considered, but the general idea works.
Here is a nice answer I got. The next few strokes are just to get enough characters to publish my answer with this image. this it requieres 30 characters, so i wiil write anything.
It's brilliant as simple as it is. Isnt it ))?
* This code need a little correction, with one more condition, if one of the pointers is NULL, not to loop.
Here's my take on it. This implementation is pretty concise.
void get2(int a[], int n, int *i1, int *i2)
{
if (n == 0) return;
if (*i1 == -1 || a[n-1] > a[*i1]) {
*i2 = *i1;
*i1 = n-1;
} else if (*i2 == -1 || a[n-1] > a[*i2]) {
*i2 = n-1;
}
get2(a, n-1, i1, i2);
}
To test:
#define NUM 10
int main()
{
int a[NUM];
int i, i1, i2;
srand(time(NULL));
for (i=0;i<NUM;i++) {
a[i] = rand() & 0xFFFF;
fprintf(stderr,"%d ",a[i]);
}
fprintf(stderr,"\n");
i1 = i2 = -1;
get2(a,NUM,&i1,&i2);
fprintf(stderr,"i1=%d, i2=%d\n", i1, i2);
fprintf(stderr,"a[i1]=%d, a[i2]=%d\n", a[i1], a[i2]);
}
Just another try on your function prototype,
void get2(int a[], int n, int *i1, int *i2)
{
if(n <= 0)
return;
if(*i1 == -1 && n < 2){//initial a[] is null or with only one element
*i1 = n-1;
return;
}
if(*i1 == -1 && n >=2){
if(a[n-1] > a[n-2]){
*i1 = n-1;
*i2 = n-2;
}else{
*i1 = n-2;
*i2 = n-1;
}
return get2(a, n-2, i1, i2);
}
int max= a[*i1], less = a[*i2];
if(a[n-1] > max){
*i2 = *i1;
*i1 = n-1;
}else if (a[n-1] > less){
*i2 = n-1;
}
get2(a, n-1, i1, i2);
}
Test as follows,
int main()
{
int a[] = { 1, 2, 3, 4, 7, 8, 6, 5, 0};
int maxIndex = -1, lessIndex= -1;
get2(a,sizeof(a)/sizeof(int),&maxIndex,&lessIndex);
printf("maxIndex=%d(value:%d), lessIndex=%d(value:%d)\n",
maxIndex,a[maxIndex],lessIndex, a[lessIndex]);
}
Output,
maxIndex=5(value:8), lessIndex=4(value:7)

function to perform bubble sort in C providing unstable results

I am participating in Harvard's opencourse ware and attempting the homework questions. I wrote (or tried to) write a program in C to sort an array using bubble sort implementation. After I finished it, I tested it with an array of size 5, then 6 then 3 etc. All worked. then, I tried to test it with an array of size 11, and then that's when it started bugging out. The program was written to stop getting numbers for the array after it hits the array size entered by the user. But, when I tested it with array size 11 it would continuously try to get more values from the user, past the size declared. It did that to me consistently for a couple days, then the third day I tried to initialize the array size variable to 0, then all of a sudden it would continue to have the same issues with an array size of 4 or more. I un-did the initialization and it continues to do the same thing for an array size of over 4. I cant figure out why the program would work for some array sizes and not others. I used main to get the array size and values from the keyboard, then I passed it to a function I wrote called sort. Note that this is not homework or anything I need to get credit, It is solely for learning. Any comments will be very much appreciated. Thanks.
/****************************************************************************
* helpers.c
*
* Computer Science 50
* Problem Set 3
*
* Helper functions for Problem Set 3.
***************************************************************************/
#include <cs50.h>
#include <stdio.h>
#include "helpers.h"
void
sort(int values[], int n);
int main(){
printf("Please enter the size of the array \n");
int num = GetInt();
int mystack[num];
for (int z=0; z < num; z++){
mystack[z] = GetInt();
}
sort(mystack, num);
}
/*
* Sorts array of n values.
*/
void
sort(int values[], int n)
{
// this is a bubble sort implementation
bool swapped = false; // initialize variable to check if swap was made
for (int i=0; i < (n-1);){ // loops through all array values
if (values[i + 1] > values [i]){ // checks the neighbor to see if it's bigger
i++; // if bigger do nothing except to move to the next value in the array
}
else{ // if neighbor is not bigger then out of order and needs sorting
int temp = values[i]; // store current array value in temp variable for swapping purposes
values[i] = values[i+1]; //swap with neighbor
values[i+1] = temp; // swap neighbor to current array value
swapped = true; // keep track that swap was made
i++;
}
// if we are at the end of array and swap was made then go back to beginning
// and start process again.
if((i == (n-1) && (swapped == true))){
i = 0;
swapped = false;
}
// if we are at the end and swap was not made then array must be in order so print it
if((i == (n-1) && (swapped == false))){
for (int y =0; y < n; y++){
printf("%d", values[y]);
}
// exit program
break;
}
} // end for
// return;
}
You can easily use 2 nested for loops :
int i, j, temp ;
for ( i = 0 ; i < n - 1 ; i++ )
{
for ( j = 0 ; j <= n - 2 - i ; j++ )
{
if ( arr[j] > arr[j + 1] )
{
temp = arr[j] ;
arr[j] = arr[j + 1] ;
arr[j + 1] = temp ;
}
}
}
also you should now it's a c++ code not a c, because c doesn't have something like :
int mystack[num];
and you should enter a number when you're creating an array and you can't use a variable (like "int num" in your code). This is in C, but in C++ you're doing right.
The first thing to do when debugging a problem like this is ensure that the computer is seeing the data you think it should be seeing. You do that by printing out the data as it is entered. You're having trouble with the inputs; print out what the computer is seeing:
static void dump_array(FILE *fp, const char *tag, const int *array, int size)
{
fprintf(fp, "Array %s (%d items)\n", tag, size);
for (int i = 0; i < size; i++)
fprintf(fp, " %d: %d\n", i, array[i]);
}
int main(void)
{
printf("Please enter the size of the array \n");
int num = GetInt();
printf("num = %d\n", num);
int mystack[num];
for (int z = 0; z < num; z++)
{
mystack[z] = GetInt();
printf("%d: %d\n", z, mystack[z]);
}
dump_array(stdout, "Before", mystack, num);
sort(mystack, num);
dump_array(stdout, "After", mystack, num);
}
This will give you direct indications of what is being entered as it is entered, which will probably help you recognize what is going wrong. Printing out inputs is a very basic debugging technique.
Also, stylistically, having a function that should be called sort_array_and_print() suggests that you do not have the correct division of labour; the sort code should sort, and a separate function (like the dump_array() function I showed) should be used for printing an array.
As it turns out the reason why it was doing this is because when comparing an array's neighbor to itself as in:
if (values[i + 1] > values [i])
The fact that I was just checking that it is greater than, without checking if it is '=' then it was causing it to behave undesirably. So if the array is for example [1, 1, 5, 2, 6, 8] then by 1 being next to a 1, my program did not account for this behavior and acted the way it did.

Resources