bsearch, qsort selbst bauen

Um nicht mit den Namen der Standardfunktionen zu kollidieren, werden in der folgenden Musterlösung andere Funktionsnamen verwendet, nämlich msearch() und msort().

// Time-stamp: "(31.10.01 19:28) msearchmsort.cpp [Klaus Wachtler (aw38)]"
//
// Lösung der Beispielaufgabe "Neuprogrammieren von bsearch() und qsort()".
//
// Diese Version läuft nur unter dem gcc, weil zum Allokieren von
// Speicherplatz die Funktion alloca() verwendet wird. Dies ist kein
// ANSI-Standard!


#include <stdlib.h>
#include <stdio.h>

// sucht in einem sortierten Feld (*base)[] nach einem
// Element, für das die Funktion (*compar)() Gleichheit mit dem
// Schlüssel (*key) verspricht, und liefert einen Zeiger auf
// das gefundene Element, oder NULL
// (entspricht dem Original-bsearch() nach ANSI-C)
void *msearch( const void *key,
               const void *base,
               size_t nmemb,
               size_t size,
               int (*compar)( const void *, const void * )
               )
{
  switch( nmemb )
    {
    case 0:
      return NULL;
      
    case 1:
      return ( (*compar)( base, key )==0 ? (void *)base : NULL );
      
    default :
      
      // Teilen, und suchen lassen:
      {
        size_t n1 = nmemb/2;
        const void  *p1 = (const char*)base+(n1*size);
        int    v1 = (*compar)( p1, key );
        if( v1==0 )
          {
            // Schlüssel genau getroffen
            return(void *) p1;
          }
        else if( v1<0 )
          {
            // base[n1] kleiner als Schlüssel;
            // also in der oberen Hälfte suchen.
            // base[n1] braucht schon nicht mehr betrachtet
            // zu werden.
            return msearch( key,
                            (const char*)p1+size,
                            nmemb - n1 - 1,
                            size,
                            compar
                            );
          }
        else
          {
            // base[n1] größer als Schlüssel;
            // also in der unteren Hälfte suchen.
            // base[n1] braucht schon nicht mehr betrachtet
            // zu werden.
            return msearch( key,
                            base,
                            n1,
                            size,
                            compar
                            );
          }
      }

    } // switch

} // msearch()



// Aus c't 3/92, S. 263ff. entnommen:
// {Bezugsdatenstruktur a : ARRAY [1 .. n] und temp}
// 
// PROCEDURE quicksort ( links, rechts : integer);
// VAR k, i, j : integer;
// BEGIN
//    IF rechts > links THEN BEGIN
//       i := links -1; j := rechts; k := a[rechts].key;
//       REPEAT
//          REPEAT i := i +1 UNTIL a[i].key > k;
//          REPEAT j := j -1 UNTIL a[j].key <= k;
//          IF i >= j THEN
//          BEGIN
//             temp := a[i]; a[i] := a[r]; a[r] := temp;
//          END
//          ELSE
//          BEGIN
//             temp := a[i]; a[i] := a[j]; a[j] := temp;
//          END;
//       UNTIL i >= j;
//    quicksort( links, i-1 );
//    quicksort( i+1, rechts);
//   END;
// END;


void msort( const void *base,
            size_t nmemb,
            size_t size,
            int (*compar)( const void *, const void * )
            )
{
  // VAR k, i, j : integer;
  int i, j;
  void *key_p = alloca( size );

  // IF rechts > links THEN BEGIN
  if( nmemb>1 )
    { // Feldlänge größer als 1
      
      // i := links -1; j := rechts; k := a[rechts].key;
      i = -1; j = nmemb - 1;
      memcpy( key_p, (char*)base + size*j, size );

      // REPEAT
      do
        {

          // REPEAT i := i +1 UNTIL a[i].key > k;
          while( compar( (char*)base + size*++i, key_p )>0 );

          // REPEAT j := j -1 UNTIL a[j].key <= k;
          while( compar( (char*)base + size*--j, key_p )<=0 );

          // IF i >= j THEN
          if( i>=j )
            {
              // temp := a[i]; a[i] := a[r]; a[r] := temp;
              // ?
              // 31.10.2001 Klaus Wachtler:
              // Was hier vertauscht werden soll, weiß ich nicht.
              // Die Variable r ist bisher nicht erwähnt; ich nehme
              // an, daß nichts vertauscht werden muß.
              // Mein Beispielfeld jedenfalls wird richtig
              // sortiert.
              // Was wirklich gemeint ist, muß ich noch klären.
            }
          // ELSE
          else
            {
              // temp := a[i]; a[i] := a[j]; a[j] := temp;
              void *temp_p = alloca( size );
              memcpy( temp_p, (char*)base + size*i, size );
              memcpy( (char*)base + size*i, (char*)base + size*j, size );
              memcpy( (char*)base + size*j, temp_p, size );
            }
        }
      while( i<j ); // UNTIL i >= j;

      // quicksort( links, i-1 );
      msort( base, i-1, size, compar );

      // quicksort( i+1, rechts);
      msort( (char*)base + size*(i+1), nmemb - i - 1, size, compar );

    } // Feldlänge größer als 1
  

} // msort( const void *base,...


typedef struct
{
  double d;
  char   s[100];
} s_t;


// vergleicht zwei Elemente vom Typ s_t (auf welche
// die beiden übergebenen Zeiger zeigen) anhand ihrer
// darin enthaltenen Gleitkommawerte d:
int vergl_f( const void *p1, const void *p2 )
{
  if( ((const s_t*)p1)->d > ((const s_t*)p2)->d )
    {
      return 1;
    }
  else if( ((const s_t*)p1)->d < ((const s_t*)p2)->d )
    {
      return -1;
    }
  else
    {
      return 0;
    }
}


// zu sortierendes Beispielfeld:
s_t f[] =
{
  { 3.141592654, "pi" },
  { 2.718281828, "e" },
  { 53.00000000, "w" },
  { 1.000000000, "eins" },
  { 2.000000000, "zwoo" },
  { 13.00000000, "drölf" },
};


// Anzahl der Feldelemente:
const int l = sizeof(f)/sizeof(f[0]);


// Testprogramm:
int main( int nargs, char **args )
{
  qsort( f,
         l,
         sizeof(f[0]),
         vergl_f
         );

  //for( int i=0; i<l; i++ )
  //  {
  //    printf( "f[%2d]=%g\n", i, f[i].d );
  //  }


  while( 1 )
    {
      double wert;
      printf( "Bitte einen Wert: " );
      if( scanf( "%lf", &wert )!=1 )
        {
          break;
        }

      s_t key = { wert, "" };
      s_t *p = (s_t *)msearch( &key,
                               f,
                               l,
                               sizeof(f[0]),
                               vergl_f
                               );
      
      if( p )
        {
          printf( "Gefunden: %f %s\n", p->d, p->s );
        }
      else
        {
          printf( "nichts gefunden!\n" );
        }

    }

  return 0;

} /* main( int nargs, char **args ) */



AnyWare@Wachtler.de