Index over opgaver i denne lektion   Alfabetisk indeks   Kursets hjemmeside   

Opgaver og løsninger
Datastrukturer og Dataabstraktion


12.1   Funktionen date_compare  

På den tilknyttede slide har vi programmeret funktionen date_before. Den returnerer en boolsk værdi: Hvorvidt datoen d1 kommer forud for d2.

Programer på basis af date_before funktionen date_compare, som sammenligner to datoer efter de gængse konventioner i C (som vi bl.a. har studeret i forbindelse med sammenligningen af to tekststrenge). Funktionen date_compare skal altså kunne returnere enten et negativt, positivt eller nul resultat.


12.2   Sortering af et array af bøger  

Programmer en funktion, sort_books_by_title, som kan kaldes på variablen shelf fra programmet på den tilhørende slide. Tag udgangspunkt i dette program. Overfør også indekset af den sidste bog som en parameter til sort_books_by_title, således at sorteringsfunktionen ved hvor mange bøger, der skal sorteres:

  void sort_books_by_title(book shelf[], int last){
    qsort(shelf, last+1, ..., ...);
  }

Som antydet af funktionsnavnet, skal funktionen ordne bøgerne således at titlerne kommer i alfabetisk orden. Efter sorteringen er den første bog således 'C by Dissection', og den sidste bog er 'The C Programming Language'. Sorteringen skal foregå ved at bytte om på bøgerne i arrayet (ikke ved at danne en ny, sorteret kopi af arrayet).

Ved løsningen af denne opgave skal du anvende funktionen qsort fra stdlib.h, som tidligere har været beskrevet i dette undervisningsmateriale.

Programmer dernæst en funktion sort_books_by_kind_and_year. Denne funktion skal primært sortere bøgerne således at alle skønliterære bøger kommer før lærebøgerne (university text books). Inden for begge disse grupper skal funktionen sekundært sortere bøgerne efter udgivelsesår. Relativt til eksemplet på den tilknytede side i undervisningsmaterialet skal det give følgende ordning på bøgerne:

Title: Pelle Erobreren
Year: 1910
University text book: no

Title: Ditte Menneskebarn
Year: 1917
University text book: no

Title: The C Programming Language
Year: 1988
University text book: yes

Title: C by Disssection
Year: 2001
University text book: yes

Title: Problem Solving and Program Design in C
Year: 2010
University text book: yes

Afprøv begge sorterings-funktionerne på de fem bøger i variablen shelf.

Løsning

Her er programmet med to to sorterings-funktioner:

#include <stdio.h>
#include <string.h>
#define MAX_BOOKS 10

struct book {
  char *title, *author, *publisher;
  int publishing_year;
  int university_text_book;
};

typedef struct book book;

book make_book(char *title, char *author, char *publisher, 
               int year, int text_book){
  book result;
  result.title = title; result.author = author;
  result.publisher = publisher; result.publishing_year = year;
  result.university_text_book = text_book;
 
  return result;
}

void prnt_book(book b){
  char *yes_or_no;

  yes_or_no = (b.university_text_book ? "yes" : "no"); 
  printf("Title: %s\n"
         "Author: %s\n"
         "Publisher: %s\n"
         "Year: %4i\n"
         "University text book: %s\n\n",
         b.title, b.author, b.publisher, 
         b.publishing_year, yes_or_no);
}

int book_compare_by_title(const void *book1, const void *book2){
  return strcmp( ((book *)book1)->title, ((book *)book2)->title);
}

// A compare function for booleans represented as integers, following the conventions in C.
// false (0) is less than true (non-zero).
int compare_boolean_integers(int b1, int b2){
  if (b1 == 0 && b2 != 0)
    return -1;       // b1 < b2
  else if (b1 != 0 && b2 == 0)
    return 1;        // b1 >  b2
  else 
    return 0;        // b1 and b2 are equal (in the boolean sense).
}

// A natural integer compare function:
int compare_integers(int i1, int i2){
  if (i1 < i2)
    return -1;
  else if (i1 == i2)
    return 0;
  else return 1;
}

int book_compare_by_kind_and_year(const void *book1, const void *book2){
  int uni_book_1 = ((book *)book1)->university_text_book,
      uni_book_2 = ((book *)book2)->university_text_book,
      y1 =         ((book *)book1)->publishing_year,
      y2 =         ((book *)book2)->publishing_year,

      kind_compare = compare_boolean_integers(uni_book_1, uni_book_2);

  if (kind_compare != 0)                // books of different kinds
     return kind_compare;               // boolean 0 (kind not text books) before boolean 1 (text books).
  else
     return compare_integers(y1, y2);   // order by publishing year
}

void sort_books_by_title(book shelf[], int last){
  qsort(shelf, last+1, sizeof(book), book_compare_by_title);
}

void sort_books_by_kind_and_year(book shelf[], int last){
  qsort(shelf, last+1, sizeof(book), book_compare_by_kind_and_year);
}

int main(void) {
  book shelf[MAX_BOOKS];
  int i;

  shelf[0] =
    make_book("Problem Solving and Program Design in C", "Hanly and Koffman", 
              "Pearson", 2010, 1);   

  shelf[1] =
    make_book("C by Disssection", "Kelley and Pohl", 
              "Addison Wesley", 2001, 1);   

  shelf[2] =
    make_book("The C Programming Language", "Kernighan og Ritchie", 
              "Prentice Hall", 1988, 1);   

  shelf[3] =
    make_book("Pelle Erobreren", "Martin Andersen Nexoe",
              "Gyldendal", 1910, 0);

  shelf[4] = shelf[3];
  shelf[4].title = "Ditte Menneskebarn"; 
  shelf[4].publishing_year = 1917;

//  sort_books_by_title(shelf, 4);
  sort_books_by_kind_and_year(shelf, 4);

  for(i = 0; i <=4; i++)
    prnt_book(shelf[i]);
  
  return 0;
}

Bemærk hvordan funktionen book_compare_by_kind_and_year benytter to simple compare funktioner på hhv. boolean (ala C) og heltal (int). Alternativt kunne vi har programmeret hele sammenligningen i én omgang, men det giver en forholdvis uigennemskuelig funktion:

int book_compare_by_kind_and_year(const void *book1, const void *book2){
  int uni_book_1 = ((book *)book1)->university_text_book,
      uni_book_2 = ((book *)book2)->university_text_book,
      y1 =         ((book *)book1)->publishing_year,
      y2 =         ((book *)book2)->publishing_year;

  if (uni_book_1 == 0 && uni_book_2 == 1)
       return -1;        //  book1 is not a text book, book2 is a text book. book1 comes before book2. Therefore -1
  else if (uni_book_1 == 1 && uni_book_2 == 0)
       return 1;
  else if (y1 < y2)      // both book are of the same kind. Then order by publishing year:
       return -1;
  else if (y1 > y2)
       return 1;
  else return 0;
}

Morale: Når problemet er for stort, nedbrydes det i et antal mindre problemer, som løses separat. Løsningerne på delproblemerne kombineres til løsningen på det oprindelige (store) problem.


12.3   Funktionen tomorrow  

den omkringliggende slide er der vist et program, der genererer en kalender på et helt år.

Vi har på en tidligere slide programmeret struct date.

I denne opgave skal du programmere den manglende funktion tomorrow. Funktionen tomorrow tager en dato som parameter, og den returnerer datoen på den efterfølgende dag. Du kan taget udgangspunkt i dette program.

Hint: Lad i udgangspunktet resultatet være den overførte parameter, og gennemfør (pr. assignment til enkelte felter) de nødvendige ændringer.

Løsning

Her er det komplette program, herunder funktionen tomorrow:

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

#define DATE_MAX 1000

/* Date related types */
enum weekday {sunday, monday, tuesday, wednesday, thursday, 
           friday, saturday};
typedef enum weekday weekday;

struct date {
  weekday day_of_week;
  int day;
  int month;
  int year;
};

typedef struct date date;

/* Funktion prototypes */

int leapYear(int);
int date_before(date, date);
weekday next_day_of(weekday);
void prnt_date(date);
char *name_of_weekday(date);
date tomorrow(date);

int main(void) {
  date calendar[DATE_MAX];
  char ignore;

  date first_date = {tuesday, 17, 11, 2015},
       last_date  = {thursday, 17, 11, 2016},
       current_date;
  int i = 0, j = 0;

  current_date = first_date;
  while (date_before(current_date, last_date)){
    calendar[i] = current_date;
    current_date = tomorrow(current_date);
    i++;
  }

 for (j = 0; j < i; j++){
    if (j % 20 == 0) {
      printf("More? - touch the enter key: ");
      scanf("%c", &ignore);
    }
    prnt_date(calendar[j]);

  }
}  

/* Return the date after d */
date tomorrow(date d){
  date result;
  
  result = d;

  switch(d.month){
    case 1: case 3: case 5: case 7: case 8: case 10: 
      if (d.day < 31)
	++(result.day);
      else {result.day = 1; ++(result.month);}
      break;
    case 4: case 6: case 9: case 11: 
      if (d.day < 30)
	++(result.day);
      else {result.day = 1; ++(result.month);}
      break;
    case 2:
      if (d.day < (leapYear(d.year)? 29 : 28))
        ++(result.day);
      else {result.day = 1; result.month = 3;}
      break;
    case 12:
      if (d.day < 31)
	++(result.day);
      else {result.day = 1; result.month = 1; (++result.year);}
      break;
    default: exit(-1);  break;
  }

  result.day_of_week = next_day_of(d.day_of_week);
  return result;
}

/* Is y a leapyear */
int leapYear(int y){
  int result;

  if (y % 400 == 0) result = 1;
  else if (y % 100 == 0) result = 0;
  else if (y % 4 == 0) result = 1;
  else result = 0;

  return result;
}

/* Is date d1 less than date d2 */
int date_before(date d1, date d2){
 return
  (d1.year < d2.year) ||
  (d1.year == d2.year && d1.month < d2.month) ||
  (d1.year == d2.year && d1.month == d2.month && d1.day < d2.day);
}  

weekday next_day_of(weekday d){
  return (weekday) (((int) d + 1) % 7);
}

/* Print date d */
void prnt_date(date d){
  printf("%9s %2i.%2i.%4i\n", name_of_weekday(d), d.day, d.month, d.year);
}

/* Return the name of the weekday of the date d */
char *name_of_weekday(date d){
  char *result;
  switch (d.day_of_week) {
    case sunday: result = "Sunday";
       break;
    case monday: result = "Monday";
       break;
    case tuesday: result = "Tuesday";
       break;
    case wednesday: result = "Wednesday";
       break;
    case thursday: result = "Thursday";
       break;
    case friday: result = "Friday";
       break;
    case saturday: result = "Saturday";
       break;
  }
  return result;
}   


12.4   Spillekort  

Et spillekort er karakteriseret af en kulør (klør, ruder, hjerter og spar) og en værdi (es, 2, .., 10, knægt, dame, og konge). Alternativt kan et spillekort være en joker (som hverken har kulør eller værdi).

Definer en struct i C (med felter kuloer og vaerdi) som repræsenterer et spillekort. Beslut selv typen af felterne. Enumerationtyper kan være attraktive. Overvej hvordan du ønsker at håndtere jokeren!

Lav et spil kort bestående af de 52 "normale" spillekort og 3 jokere, altså ialt 55 kort. Organiser disse i et array af structs.

Programmer en funktion sorter_kort som sorterer kortene således:

I programmeringen af sorter_kort forventes at du benytter qsort med en passende sammenligningsfunktion.

Da kortene typisk er ordnet per konstruktion kan det anbefales du også programmerer en funktion, som blander kortene tilfældigt. Med en sådan funktion kan du blande kortene inden de sorteres. Blanding af kortene kan gøres på mange forskellige måder. Det kan f.eks. ske ved at gennemløbe alle kort, og at bytte det aktuelle kort om med et tilfældigt valgt andet kort.

Målet med opgaven er at opnå viden og færdigheder i programmering med structs og array af structs. Det er endvidere målet at opnå viden og færdigheder om sortering af et array af structs.

Løsning

Herunder er min løsning på opgaven, som også involverer en funktion der på tilfældig måde blander et spil kort.

Bemærk anvendelsen af enumeration typer til symbolsk navngivning af (kun) nogle af kortene.

Jeg har valgt at repræsentere et kort som to heltalsværdier (værdier i de to enumeration typer). Dette er langt bedre en f.eks. to tekst-strenge, som er forholdsvis vanskelige at håndtere når kortene skal sorteres. Jokeren håndteres gennem en særlig kulør, og via en særlige "konstruktor" funktion. Jokeren er valgt til at have den største kulør. Ligeledes kommer et es efter de andre værdier. Dette gør sorteringen nemmere.

Udskrivning faciliteres af to arrays of tekststrenge, lokalt i print_card. Disse er statiske, så vi ikke igen og igen skal etablere dem. Denne datastyrede udskrivning er meget lettere at lave end udskrivning via store switch kontrolstrukturer.

#include <stdio.h>
#include <stdlib.h> 
#include <time.h>
/* NUMBER_OF_CARDS controls the number of jokers */
#define NUMBER_OF_CARDS 55

enum card_type {club, diamond, heart, spade, joker};
typedef enum card_type card_type;

/* Card values range from 2 to ace. Card values above 10 have symbolic names: */
enum card_value {jack = 11, queen = 12, king = 13, ace = 14};  
typedef enum card_value card_value;

struct card{
  card_type type;
  card_value value;
};
typedef struct card card;

card make_card(card_type t, card_value v);
card make_joker(void);
void put_cards_in_deck(card deck[]);
void print_deck(card deck[]);
void print_card(card c);
int card_compare(const void *ep1, const void *ep2);
void sort_cards(card deck[]);
void swap_cards(card deck[], int i, int j);
void shuffle_cards(card deck[]);

int main(void) {
  card deck[NUMBER_OF_CARDS];

  srand(time(NULL));   /* Seed random number generator */

  put_cards_in_deck(deck);
  printf("INITIAL:\n");
  print_deck(deck);

  shuffle_cards(deck);
  printf("\nAFTER SHUFFLE:\n");
  print_deck(deck);

  sort_cards(deck);
  printf("\nAFTER SORTING:\n");
  print_deck(deck);
  
  return 0;
}

void put_cards_in_deck(card deck[]){
  int ct, cv, i = 0, j;
  card c;

  /* Make 4 x 13 normal cards: */
  for(ct = club; ct <= spade; ct++){
    for(cv = 2; cv <= ace; cv++){
      c = make_card(ct, cv);
      deck[i] = c;
      i++;
    }
  }

  /* Make sufficient jokers, to a total of NUMBER_OF_CARS cards. */
  for(j = 1; j <= NUMBER_OF_CARDS - (4 * (int)king); j++)
    deck[i++] = make_joker();
}

void print_deck(card deck[]){
  int i;
  for (i = 0; i < NUMBER_OF_CARDS; i++) {
    print_card(deck[i]);
    printf("\n");
  }
}


card make_card(card_type t, card_value v){
  card result;
  result.type = t;
  result.value = v;
  return result;
}

card make_joker(void){
  card result;
  result.type = joker;
  result.value = 0;    /* arbitrary, but well-defined */
  return result;
}

  
void print_card(card c){
  static char *card_type_name[] = {"club", "diamond", "heart", "spade", "joker"};
  static char *card_value_name[] = {"two", "three", "four", "five", "six",
                                    "seven", "eight", "nine", "ten", 
                                    "jack", "queen", "king", "ace"};
  if (c.type == joker)
    printf("joker");
  else 
    printf("%s %s", card_type_name[c.type], card_value_name[c.value-2]);
}

void sort_cards(card deck[]){
  qsort(deck, NUMBER_OF_CARDS, sizeof(card), card_compare);
}


int card_compare(const void *ep1, const void *ep2){
  card *cp1 = (card *)ep1,
       *cp2 = (card *)ep2;

  if (cp1->type == cp2->type)
    return cp1->value - cp2->value;
  else 
    return cp1->type - cp2->type;
}


void swap_cards(card deck[], int i, int j){
  card temp_card;
  temp_card = deck[i];
  deck[i] = deck[j];
  deck[j] = temp_card;
}

void shuffle_cards(card deck[]){
  int i, r;
  for(i = 0; i < NUMBER_OF_CARDS; i++){
    r = rand() % NUMBER_OF_CARDS;
    swap_cards(deck, i, r);
  }
}


12.5   Brøker og structs  

Lav en struct, som repræsenterer en brøk (engelsk: fraction). Den nye type skal naturligvis have to felter, for hhv. tæller og nævner (engelsk: numerator og denominator). I denne opgave ønsker vi at disse skal være af typen unsigned int. Vi arbejder således udelukkende med positive brøker.

Programmer nu følgende funktioner på brøker:

Det kan også anbefales at lave en funktion der laver en brøk ud fra en tæller og en nævner. Altså en funktion der returnerer en brøk konstrueret ud fra tæller og nævner input.

Vi har tidligere i kurset arbejdet med en funktion, der finder den største fælles divisor af to positive heltal. For ikke at bruge for meget tid på dette aspekt, er der en mulig implementation af denne funktion her (Euclids algoritme):

  unsigned int gcd(unsigned int large, unsigned int small){
    unsigned int remainder; 
    while (small > 0){
      remainder = large % small;
      large = small;
      small = remainder;
    }
    return large;
  }

Når man skal addere to brøkker skal man først finde en fælles nævner. Dette kan gøres ved at multiplicere begge brøker med den anden brøks nævner.

Hvis du har brug for mere matematisk viden om brøker henviser vi til Wikipedias side om brøker.

Skriv også en main funktion som giver eksempler på hvordan givne brøker kan konstrueres, adderes og multipliceres.

Løsning

Her er min løsning til brøk-opgaven:

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

struct fraction{
  unsigned int numerator;
  unsigned int denominator;
}; 
typedef struct fraction fraction;

void print_fraction(const fraction f);
fraction shorten(const fraction f);
fraction add_fractions(fraction f, fraction g);
fraction multiply_fractions(fraction f, fraction g);
fraction multiply_fraction_with_int(fraction f, unsigned int i);

unsigned int gcd(unsigned int large, unsigned int small);

int main(void) {
  fraction f1 = {50, 30},
           f2 = {12, 32},
           f3 = {1, 2};

  print_fraction(shorten(f1));
  print_fraction(add_fractions(f2, f3));
  print_fraction(add_fractions(f3, f3));
  print_fraction(multiply_fractions(f2, f3));
  print_fraction(multiply_fractions(f3, f3));
  print_fraction(multiply_fraction_with_int(f3, 4));
  
  return EXIT_SUCCESS;
}

/* Make and return a fraction with numerator and denominator */
fraction make_fraction(unsigned int numerator, unsigned int denominator){
  fraction result;
  result.numerator = numerator; result.denominator = denominator;
  return result;
}

/* Add two the fractions f and g, and return the sum */
fraction add_fractions(fraction f, fraction g){
  return shorten(make_fraction(f.numerator * g.denominator + g.numerator * f.denominator, f.denominator * g.denominator));
}

/* Multiply the two fractions f and g, and return the sum */
fraction multiply_fractions(fraction f, fraction g){
  return shorten(make_fraction(f.numerator * g.numerator, f.denominator * g.denominator));
}

/* Multiply the fraction f and i and return the product. */
fraction multiply_fraction_with_int(fraction f, unsigned int i){
  return multiply_fractions(f, make_fraction(i, 1));
}

/* Return the greatest common divisor of large and small. Assume that large is greater than small. */
unsigned int gcd(unsigned int large, unsigned int small){
  unsigned int remainder; 
  while (small > 0){
    remainder = large % small;
    large = small;
    small = remainder;
  }
  return large;
}

/* Print the fraction f on standard output */
void print_fraction(const fraction f){
  if (f.denominator == 1)
    printf("%u\n", f. numerator);
  else 
    printf("%u/%u\n", f. numerator, f.denominator); 
}

/* Shorten f as much as possible, and return the shortened fraction */
fraction shorten(const fraction f){
  unsigned int factor = gcd(f.numerator, f.denominator);
  fraction result;
  
  result.numerator = f.numerator /factor;
  result.denominator = f.denominator /factor;

  return result;
}


12.6   Funktioner på cirkulære lister  

I denne opgave skal du tage udgangspunkt i diskussionen af cirkulære lister i videoen, som hører til denne lektion. Jeg foreslår at du laver en cirkulær liste med punkter.

Start med at programmere en funktion der beregner listens længde: length_of_circular_list

Programmer dernæst de fire funktioner der hhv. indsætter og sletter det første og det sidste element i en liste:

Det forventes at alle pånær én af disse kan køres i konstant tid (altså uafhængig af listens længde). Hvilken funktion er markant mindre effektiv end i de andre?

Her et udgangspunkt, som gør det lidt lettere at komme i gang:

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

struct point {int x; int y;};
typedef struct point point;

struct list_node {
  void             *data;
  struct list_node *next;
};
typedef struct list_node list_node;

void print_point(point *p);
void print_circular_point_list(list_node *cl);
list_node *insert_head(list_node *cl, void *el);
list_node *insert_tail(list_node *cl, void *el);
list_node *delete_head(list_node *cl);
list_node *delete_tail(list_node *cl);
int length_of_circular_list(list_node *cl);
list_node *find_previous_node(list_node *cl);

int main(void) {
  point p1 = {1,2}, p2 = {3,4}, p3 = {5,6}, p4 = {7,8};

  list_node *circular_list = NULL;
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));

  return EXIT_SUCCESS;
}

void print_point(point *p){
  printf("(%1d, %1d)\n", p->x, p->y);
}

void print_circular_point_list(list_node *cl){
  list_node *cur, *prev;

  if (cl != NULL){
   cur = cl->next;
   do{
     prev = cur;
     print_point(cur->data);
     cur = cur->next;
   }
   while(prev != cl);
  }
}

/* An exercise */
int length_of_circular_list(list_node *cl){
  return 0; 
}

Løsning

Her er mit samlede program der løse opgaven:

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

struct point {int x; int y;};
typedef struct point point;

struct list_node {
  void             *data;
  struct list_node *next;
};
typedef struct list_node list_node;

void print_point(point *p);
void print_circular_point_list(list_node *cl);
list_node *insert_head(list_node *cl, void *el);
list_node *insert_tail(list_node *cl, void *el);
list_node *delete_head(list_node *cl);
list_node *delete_tail(list_node *cl);
int length_of_circular_list(list_node *cl);
list_node *find_previous_node(list_node *cl);
void assert_allocation_success(list_node *ln);

int main(void) {
  point p1 = {1,2}, p2 = {3,4}, p3 = {5,6};

  list_node *circular_list = NULL;
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));

  circular_list =
    insert_tail(
      insert_tail(
        insert_tail(circular_list, &p1),
        &p2),
      &p3);
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));
  print_circular_point_list(circular_list);

  circular_list = delete_tail(circular_list);
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));
  print_circular_point_list(circular_list);

  circular_list = delete_head(circular_list);
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));
  print_circular_point_list(circular_list);

  circular_list = delete_tail(circular_list);
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));
  print_circular_point_list(circular_list);

  circular_list = delete_tail(circular_list);
  printf("Length of circular list: %d\n", length_of_circular_list(circular_list));
  print_circular_point_list(circular_list);

  return EXIT_SUCCESS;
}

void print_point(point *p){
  printf("(%1d, %1d)\n", p->x, p->y);
}

void print_circular_point_list(list_node *cl){
  list_node *cur, *prev;

  if (cl != NULL){
   cur = cl->next;
   do{
     prev = cur;
     print_point(cur->data);
     cur = cur->next;
   }
   while(prev != cl);
  }
}

/* Insert el as a new head. Return the circular list */
list_node *insert_head(list_node *cl, void *el){
  list_node *new_node = malloc(sizeof(list_node));
  assert_allocation_success(new_node);
  new_node->data = el;

  if (cl == NULL){
    new_node->next = new_node;
    return new_node;
  }
  else{    
    list_node *old_head = cl->next;
    new_node->next = old_head;
    cl->next = new_node;
    return cl;  
  }
}

/* Insert el as a new tail. Return the circular list */
list_node *insert_tail(list_node *cl, void *el){
  list_node *new_node = malloc(sizeof(list_node));
  assert_allocation_success(new_node);
  new_node->data = el;

  if (cl == NULL){
    new_node->next = new_node;
    return new_node;
  }
  else{    
    list_node *old_head = cl->next;
    cl->next = new_node;
    new_node->next = old_head;
    return new_node;
  }
}

void assert_allocation_success(list_node *ln){
  if (ln == NULL){
    printf("Cannot allocate list node. Bye\n");
    exit(EXIT_FAILURE);
  }
}


/* Delete the head, if possible, and return the resulting (shorter) circular list */
list_node *delete_head(list_node *cl){
  if(cl == NULL){
    printf("Cannot delete from emtpy list");
    exit(EXIT_FAILURE);
  }
  else if (cl == cl->next) { /* circular list of length one */
    free(cl);
    return NULL;
  }
  else{
    list_node *node_to_delete = cl->next;
    cl->next = cl->next->next;
    free(node_to_delete);
    return cl;
  }
}

/* Delete the tail, if possible, and return the resulting (shorter) circular list */
list_node *delete_tail(list_node *cl){
  if(cl == NULL){
    printf("Cannot delete from emtpy list");
    exit(EXIT_FAILURE);
  }
  else if (cl == cl->next) { /* circular list of length one */
    free(cl);
    return NULL;
  }
  else{
    list_node *before_tail = find_previous_node(cl);
    before_tail->next = cl->next;
    free(cl);
    return before_tail;
  }
}

/* As a precondition, assume that cl is not empty and that cl is not singular. Thus the list has least two elements. */
list_node *find_previous_node(list_node *cl){
  list_node *cur = cl->next;
  
  while (cur->next != cl){
    cur = cur->next;
  }
  return cur;
}

int length_of_circular_list(list_node *cl){
  if (cl == NULL)
    return 0;
  else {
    int count = 0;
    list_node *cur = cl->next, *prev;
    do{
      prev = cur;
      ++count;
      cur = cur->next;
    }
    while(prev != cl);
    return count;
  }
}

Det er sletning fra bagenden, delete_tail, som kræver et gennemløb af hele listen for at finde det næst-bagereste element. Og derfor er den ikke effektiv. De tre andre indsæt/slet funktioner kører i konstant tid (altså uafhængig af længden af den kædede liste).

Bemærk venligst hvor svært det kan være at få alle detaljer på plads i dette program. Programmering af kædede datastrukturer er ganske krævende, og det kan være vanskeligt at finde de småfejl vi næsten altid laver undervejs i programmeringen.

Bemærk også at jeg har brugt malloc uden at check for succes. Det er ikke godt.


12.7   Højde-funktion af binært træ  

Højden af et binært træ defineres som højden af træets rod.

Højden af en knude K i træet er den længste sti fra K til et blad. Med dette bliver højden af et blad altså 0.

Skriv en funktion i C der beregner højden af et træ. Udgangspunktet (parameteren) skal være en pointer til en knude i træet. Afprøv funktionen på forskellige træer.

Her er et godt udgangspunkt for denne opgave - dog givet i kontekst af binære søgetræer - og svarende til de programmer der er diskuteret i lektionsvideoen.

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

struct tree_node{
  int key;
  struct tree_node *leftp, *rightp;
};

typedef struct tree_node tree_node;

tree_node *make_tree(int key, tree_node *left, tree_node *right);
void assert_allocation_success(void *alloc_pointer);
tree_node *insert_in_binary_search_tree(tree_node *tree_ptr, int e);
void print_tree(tree_node *tree_ptr);
void print_tree_helper(tree_node *tree_ptr, int level);
int tree_height(tree_node *tree_ptr);

int main(void) {

  tree_node *tree4 = NULL;
  tree4 = insert_in_binary_search_tree(tree4, 6);
  tree4 = insert_in_binary_search_tree(tree4, 2);
  tree4 = insert_in_binary_search_tree(tree4, 1);
  tree4 = insert_in_binary_search_tree(tree4, 4);
  tree4 = insert_in_binary_search_tree(tree4, 3);
  tree4 = insert_in_binary_search_tree(tree4, 5);
  tree4 = insert_in_binary_search_tree(tree4, 7);
  tree4 = insert_in_binary_search_tree(tree4, 9);
  tree4 = insert_in_binary_search_tree(tree4, 8);

  printf("tree4:\n");
  print_tree(tree4);

  exit(EXIT_FAILURE);
}


tree_node *make_tree(int key, tree_node *left, tree_node *right){
  tree_node *the_tree = malloc(sizeof(tree_node));
  assert_allocation_success(the_tree);
  the_tree->key = key; 
  the_tree->leftp = left; 
  the_tree->rightp = right; 
  return the_tree;
} 

void assert_allocation_success(void *alloc_pointer){
  if (alloc_pointer == NULL){
    printf("Allocation problems. Your program stops.");
    exit(EXIT_FAILURE);
  }
}

/* Insert the element e i the binary search tree, designated by tree_ptr. 
   Drop the insertion if e is already in the tree. 
   Return the tree  in terms of a pointer to its root node. */
tree_node *insert_in_binary_search_tree(tree_node *tree_ptr, int e){
  if (tree_ptr == NULL){
    /* Make a small tree rooted with e and empty subtrees */
    /* This branch is never reached via recursion */
    tree_node *the_tree = make_tree(e, NULL, NULL);
    return the_tree;
  }
  else if (e == tree_ptr->key){
    /* Do nothing */
    return tree_ptr;
  }
  else if (e < tree_ptr->key && tree_ptr->leftp == NULL){
    tree_node *the_tree = make_tree(e, NULL, NULL);
    tree_ptr->leftp = the_tree;
    return tree_ptr;         
  }
  else if (e < tree_ptr->key){
    insert_in_binary_search_tree(tree_ptr->leftp, e);
    return tree_ptr;
  }
  else if (e > tree_ptr->key && tree_ptr->rightp == NULL){
    tree_node *the_tree = make_tree(e, NULL, NULL);
    tree_ptr->rightp = the_tree;
    return tree_ptr;         
  }
  else { /* e > tree_ptr->key) */
    insert_in_binary_search_tree(tree_ptr->rightp, e);
    return tree_ptr;
  }
}

void print_tree(tree_node *tree_ptr){
  print_tree_helper(tree_ptr, 0);
}    

/* print the tree designated by tree_ptr as horizotally, as an indented text, on standard output.
   Empty subtrees are printed as "." if tree_ptr is passsed as NULL */
void print_tree_helper(tree_node *tree_ptr, int level){
  int i; 

  /* print indented root */
  for(i = 0; i < level; ++i) 
    printf("   ");
  if (tree_ptr == NULL)
    printf(".\n");  /* Missing part */
  else
    printf("%d\n", tree_ptr->key);

  if(tree_ptr != NULL){
    /* print branches */
    if (tree_ptr->leftp == NULL && tree_ptr->rightp == NULL){
      /* print nothing */
    }
    else if (tree_ptr->leftp != NULL && tree_ptr->rightp == NULL){
      print_tree_helper(tree_ptr->leftp, level+1);
      print_tree_helper(NULL, level+1);
    }   
    else if (tree_ptr->leftp == NULL && tree_ptr->rightp != NULL){
      print_tree_helper(NULL, level+1);
      print_tree_helper(tree_ptr->rightp, level+1);
    }
    else {  
      print_tree_helper(tree_ptr->leftp, level+1);
      print_tree_helper(tree_ptr->rightp, level+1);
    }
  }
}


/* Opgave */
int tree_height(tree_node *tree_ptr){
  return 0;
}

Løsning

Her er den efterspurgte funktion (og lidt mere).

int int_max(int a, int b){
  return a > b ? a : b;
}

int is_leaf(tree_node *tree_ptr){
  if (tree_ptr == NULL)
    return 0;
  else if (tree_ptr->leftp == NULL && tree_ptr->rightp == NULL)
    return 1;
  else 
    return 0;
}

int tree_height(tree_node *tree_ptr){
  if (tree_ptr == NULL)
    return 0;
  else if (is_leaf (tree_ptr))
    return 0;
  else return 1 + int_max(tree_height(tree_ptr->leftp), tree_height(tree_ptr->rightp));
}


12.8   En funktion der genkender et binært søgetræ  

Programmer en funktion, is_binary_search_tree, der tager et binært træ som input, og som returnerer hvorvidt dette træ er et (binært) søgetræ.

Afprøv funktionen på forskellige binære træer.

Herunder er der et godt udgangspunkt for denne opgave. Det vil også være en god ide at konsultere denne del af lektionsvidoen, hvor et binært søgetræ defineres.

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

struct tree_node{
  int key;
  struct tree_node *leftp, *rightp;
};

typedef struct tree_node tree_node;

tree_node *make_tree(int key, tree_node *left, tree_node *right);
void assert_allocation_success(void *alloc_pointer);
void print_tree(tree_node *tree_ptr);
void print_tree_helper(tree_node *tree_ptr, int level);
tree_node *insert_in_binary_search_tree(tree_node *tree_ptr, int e);
int is_binary_search_tree(tree_node *tree_ptr);

int main(void) {
  tree_node *tree4 = NULL;
  tree4 = insert_in_binary_search_tree(tree4, 6);
  tree4 = insert_in_binary_search_tree(tree4, 2);
  tree4 = insert_in_binary_search_tree(tree4, 1);
  tree4 = insert_in_binary_search_tree(tree4, 4);
  tree4 = insert_in_binary_search_tree(tree4, 3);
  tree4 = insert_in_binary_search_tree(tree4, 5);
  tree4 = insert_in_binary_search_tree(tree4, 7);
  tree4 = insert_in_binary_search_tree(tree4, 9);
  tree4 = insert_in_binary_search_tree(tree4, 8);

  /* Per construction, tree4 should be a binary search tree */
  /* You can use make_tree to construct other trees which are not binary search trees */

  printf("tree4:\n");
  print_tree(tree4);

  exit(EXIT_FAILURE);
}


tree_node *make_tree(int key, tree_node *left, tree_node *right){
  tree_node *the_tree = malloc(sizeof(tree_node));
  assert_allocation_success(the_tree);
  the_tree->key = key; 
  the_tree->leftp = left; 
  the_tree->rightp = right; 
  return the_tree;
} 

void assert_allocation_success(void *alloc_pointer){
  if (alloc_pointer == NULL){
    printf("Allocation problems. Your program stops.");
    exit(EXIT_FAILURE);
  }
}

/* Insert the element e i the binary search tree, designated by tree_ptr. 
   Drop the insertion if e is already in the tree. 
   Return the tree  in terms of a pointer to its root node. */
tree_node *insert_in_binary_search_tree(tree_node *tree_ptr, int e){
  if (tree_ptr == NULL){
    /* Make a small tree rooted with e and empty subtrees */
    /* This branch is never reached via recursion */
    tree_node *the_tree = make_tree(e, NULL, NULL);
    return the_tree;
  }
  else if (e == tree_ptr->key){
    /* Do nothing */
    return tree_ptr;
  }
  else if (e < tree_ptr->key && tree_ptr->leftp == NULL){
    tree_node *the_tree = make_tree(e, NULL, NULL);
    tree_ptr->leftp = the_tree;
    return tree_ptr;         
  }
  else if (e < tree_ptr->key){
    insert_in_binary_search_tree(tree_ptr->leftp, e);
    return tree_ptr;
  }
  else if (e > tree_ptr->key && tree_ptr->rightp == NULL){
    tree_node *the_tree = make_tree(e, NULL, NULL);
    tree_ptr->rightp = the_tree;
    return tree_ptr;         
  }
  else { /* e > tree_ptr->key) */
    insert_in_binary_search_tree(tree_ptr->rightp, e);
    return tree_ptr;
  }
}
/* void delete_tree(tree_node *tree_ptr)
   Rather difficult to program because it requires access to parents */


void print_tree(tree_node *tree_ptr){
  print_tree_helper(tree_ptr, 0);
}    

/* print the tree designated by tree_ptr as horizotally, as an indented text, on standard output.
   Empty subtrees are printed as "." if tree_ptr is passsed as NULL */
void print_tree_helper(tree_node *tree_ptr, int level){
  int i; 

  /* print indented root */
  for(i = 0; i < level; ++i) 
    printf("   ");
  if (tree_ptr == NULL)
    printf(".\n");  /* Missing part */
  else
    printf("%d\n", tree_ptr->key);

  if(tree_ptr != NULL){
    /* print branches */
    if (tree_ptr->leftp == NULL && tree_ptr->rightp == NULL){
      /* print nothing */
    }
    else if (tree_ptr->leftp != NULL && tree_ptr->rightp == NULL){
      print_tree_helper(tree_ptr->leftp, level+1);
      print_tree_helper(NULL, level+1);
    }   
    else if (tree_ptr->leftp == NULL && tree_ptr->rightp != NULL){
      print_tree_helper(NULL, level+1);
      print_tree_helper(tree_ptr->rightp, level+1);
    }
    else {  
      print_tree_helper(tree_ptr->leftp, level+1);
      print_tree_helper(tree_ptr->rightp, level+1);
    }
  }
}


int int_max(int a, int b){
  return a > b ? a : b;
}

/* Opgaven */
int is_binary_search_tree(tree_node *tree_ptr){
  return 0;
}


    

Løsning

Her er den efterspurgte funktion og en hjælpefunktion:

int is_binary_search_tree(tree_node *tree_ptr){
  if (tree_ptr == NULL)
    return 1;  /* The empty tree is here considered as binary search tree */
  else if (tree_ptr->leftp != NULL && tree_ptr->rightp != NULL)
    return tree_ptr->key > max_value_in_binary_tree(tree_ptr->leftp) &&
           tree_ptr->key < max_value_in_binary_tree(tree_ptr->rightp) &&
           is_binary_search_tree(tree_ptr->leftp) &&
           is_binary_search_tree(tree_ptr->rightp);
  else if (tree_ptr->leftp != NULL && tree_ptr->rightp == NULL)
    return tree_ptr->key > max_value_in_binary_tree(tree_ptr->leftp) &&
           is_binary_search_tree(tree_ptr->leftp);
  else if (tree_ptr->leftp == NULL && tree_ptr->rightp != NULL)
    return tree_ptr->key < max_value_in_binary_tree(tree_ptr->rightp) &&
           is_binary_search_tree(tree_ptr->rightp);
  else if (tree_ptr->leftp == NULL && tree_ptr->rightp == NULL)
    return 1;
  else {
    printf("is_binary_search_tree: Should not happen\n");
    exit(EXIT_FAILURE);
  }
}

int max_value_in_binary_tree(tree_node *tree_ptr){
  if (tree_ptr == NULL)
    return INT_MIN; /* Convenient value for empty trees */
  else 
    return int_max(int_max(tree_ptr->key, max_value_in_binary_tree(tree_ptr->leftp)), 
                   max_value_in_binary_tree(tree_ptr->rightp));
}

int int_max(int a, int b){
  return a > b ? a : b;
}


Genereret: Mandag 29. november 2021, 12:05:47