using binary search to find the first capital letter in a sorted string

Viewed 379

I wrote the following code to find the first capital letter in a string using binary search:

char first_capital(const char str[], int n)
{
    int begin = 0;
    int end = n - 1;
    int mid;
    while (begin <= end)
    {
        mid = (begin + end) / 2;
        if (mid == 0 && isupper(str[mid]))
        {
            return mid;
        }
        else if (mid > 0 && isupper(str[mid]) && islower(str[mid - 1]))
        {
            return mid;
        }
        if (islower(str[mid]))
        {
            begin = mid + 1;
        }
        else
        {
            end = mid - 1;
        }
    }
    return 0;
}

Currently my code isn't working as expected while testing it. If anyone can mention where I went wrong it would help a lot.

NOTE: The input string will be already sorted (all lower case letters appear before upper case letters). const char str[] is the string and int n is the length of the string.

EDIT: for example: first_capital("abcBC", 5) should return 'B'.

3 Answers

Your logic is completely right, but you returned the wrong value

char first_capital(const char str[], int n)
{
    int begin = 0;
    int end = n - 1;
    int mid;
    while (begin <= end)
    {
        mid = (begin + end) / 2;
        if(mid == 0 && isupper(str[mid]))
        {
            return mid;    // Here the index is returned not the character
        }
        else if (mid > 0 && isupper(str[mid]) && islower(str[mid-1]))
        {
            return mid;    // Same goes here
        }
        if(islower(str[mid]))
        {
            begin = mid+1;
        }
        else
        {
            end = mid - 1;
        }
    }
    return 0;
}

The driver code

int main(){
    
    printf("%d\n", first_capital("abcabcabcabcabcZ", 16));
}

will be giving 15 as an answer which is the index of the character Z.

if u want the character to be returned replace return mid with return str[mid] and 'Z' will be returned.

#include <stdio.h>

/* This will find and return the first UPPERCASE character in txt
 * provided that txt is zero-or-more lowercase letters,
 * followed by zero-or-more uppercase letters.
 * If it is all lower-case letters, it will return \0 (end of string)
 * If it is all upper-case letters, it will return the first letter (txt[0])
 * If there are non-alpha characters in the string, all bets are off.
 */
char findFirstUpper(const char* txt)
{
    size_t lo = 0;
    size_t hi = strlen(txt);
    
    while(hi-lo > 1)
    {
        size_t mid = lo + (hi-lo)/2;
        *(isupper(txt[mid])? &hi : &lo) = mid;
    }
    
    return isupper(txt[lo])? txt[lo] : txt[hi];
}

int main(void)
{
    char answer = findFirstUpper("abcBC");
    printf("Found char %c\n", answer);
    return 0;
}

If the function deals with strings then the second parameter should be removed.

The function should return a pointer to the first upper case letter or a null pointer if such a letter is not present in the string. That is the function declaration and behavior should be similar to the declaration and behavior of the standard string function strchr. The only difference is that your function does not require a second parameter of the type char because the searched character is implicitly defined by the condition to be an upper case character.

On the other hand, though your function has the return type char it returns an integer that specifies the position of the found character. Also your function does not make a difference between the situations when an upper case character is not found and when a string contains an upper case character in its first position.

Also your function has too many if-else statements.

The function can be declared and defined the following way as it is shown in the demonstrative program below.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

char * first_capital( const char s[] )
{
    const char *first = s;
    const char *last = s + strlen( s );
    
    while ( first < last )
    {
        const char *middle = first + ( last - first ) / 2;
        
        if ( islower( ( unsigned char )*middle ) )
        {
            first = middle + 1;
        }
        else
        {
            last = middle;
        }
    }
    
    return ( char * )( isupper( ( unsigned char )*first ) ? first : NULL );
}

int main(void) 
{
    const char *s = "";
    
    char *result = first_capital( s );
    
    if ( result )
    {
        printf( "%c at %zu\n", *result, ( size_t )( result - s ) );
    }
    else
    {
        printf( "The string \"%s\" does not contain an upper case letter.\n", s );
    }
    
    s = "a";
    
    result = first_capital( s );

    if ( result )
    {
        printf( "%c at %zu\n", *result, ( size_t )( result - s ) );
    }
    else
    {
        printf( "The string \"%s\" does not contain an upper case letter.\n", s );
    }
    
    s = "A";
    
    result = first_capital( s );

    if ( result )
    {
        printf( "%c at %zu\n", *result, ( size_t )( result - s ) );
    }
    else
    {
        printf( "The string \"%s\" does not contain an upper case letter.\n", s );
    }
    
    s = "abcdefA";
    
    result = first_capital( s );

    if ( result )
    {
        printf( "%c at %zu\n", *result, ( size_t )( result - s ) );
    }
    else
    {
        printf( "The string \"%s\" does not contain an upper case letter.\n", s );
    }
    
    s = "abAB";
    
    result = first_capital( s );

    if ( result )
    {
        printf( "%c at %zu\n", *result, ( size_t )( result - s ) );
    }
    else
    {
        printf( "The string \"%s\" does not contain an upper case letter.\n", s );
    }
    
    return 0;
}

The program output is

The string "" does not contain an upper case letter.
The string "a" does not contain an upper case letter.
A at 0
A at 6
A at 2
Related