Опубликован: 05.11.2013 | Доступ: свободный | Студентов: 542 / 46 | Длительность: 11:51:00
Лекция 6:

Абстрактный тип данных

Основные операции над множеством - это добавление элемента, проверка наличия элемента, объединение, вычитание и пересечение множеств. Отсюда можно сформировать set.h-файл с определением типа и заголовками операций.

----- файл set.h -----
/*******************************************************
date: 10 January 2013
description: Set type
******************************************************/
/* Definition of the Set type */
typedef struct
{
  int elems[255];
} Set;
/*******************************************************
* Name : initSet
* Purpose : prepares the Set to work with
* (should be called before using Set variable)
* Input : s – new set
* Output : s – set prepared to work with
* Return : none
******************************************************/
void initSet(Set *s);
/*******************************************************
* Name : initFromStringSet
* Purpose : prepares the Set to work with
* (can be called instead of initSet(…) )
* Input : s – new set,
* str – string with elements to add to the set
* Output : s – set prepared to work with,
* containing elements from string str
* Return : none
******************************************************/
void initFromStringSet(Set *s, char *str)
/*******************************************************
* Name : addElemSet
* Purpose : adds the element to the set
* (if element already exists – does nothing)
* Input : s – set, elem – element to add
* Output : s – set with added element
* Return : none
******************************************************/
void addElemSet(Set *s, char elem);
/*******************************************************
* Name : removeElemSet
* Purpose : removes the element from the set
* (if element already exists – does nothing)
* Input : s – set, elem – element to delete
* Output : s – set with no element elem
* Return : none
******************************************************/
void removeElemSet(Set *s, char elem);
/*******************************************************
* Name : isInSet
* Purpose : check whether the element is in the set
* Input : s – set
* Output : none
* Return :
*   -1 is in set
*   0 is not in set
******************************************************/
int isInSet(Set s, char elem);
/*******************************************************
* Name : unionSet
* Purpose : unions two sets
* Input : s1 – set one, s2 – set two
* Output : s1 – union result
* Return : none
******************************************************/
void unionSet(Set *s1, Set s2);
// deducts set s2 from set s1
// input: s1 – set one, s2 – set two
// output: s1 – deduct result
void substrSet(Set *s1, Set s2);
/*******************************************************
* Name : intersectSet
* Purpose : intersects two sets
* Input : s1 – set one, s2 – set two
* Output : s1 – intersection result
* Return : none
******************************************************/
void intersectSet(Set *s1, Set s2);
/*******************************************************
* Name : isEqualSet
* Purpose : compares two sets
* Input : s1 – set one, s2 – set two
* Output : none
* Return :
*   1 sets are equal
*   0 not equal
******************************************************/
int isEqualSet(Set s1, Set s2);
/*******************************************************
* Name : copySet
* Purpose : copies set s1 to set s2
* Input : s1 – set one, s2 – set two
* Output : s1 – copy of s2
* Return : none
******************************************************/
void copySet(Set *s1, Set s2);
/*******************************************************
* Name : isEmptySet
* Purpose : check whether the set is empty
* Input : s1 – set one, s2 – set two
* Output : none
* Return :
*   1 set is empty
*   0 not empty
******************************************************/
int isEmptySet(Set s);
/*******************************************************
* Name : printSet
* Purpose : prints set
* Input : s1 – set to print
* Output : prints set on th screen (standard output)
* Return : none
******************************************************/
void printSet(Set s);
----- файл set.c -----
//
// author: O.
// date: 10 January 2013
// description: Set type
//
#include "set.h"
#include <stdio.h>
void initSet(Set *s)
{
  int i;
  for (i = 0; i < 255; i++)
  {
    (*s).elems[i] = 0;
  }
}
void initFromStringSet(Set *s, char *str)
{
  int i;
  while(*str != '\0')
  {
    i = (int)*str;
    (*s).elems[i] = 1;
    str++;
  }
}
void addElemSet(Set *s, char elem)
{
  int i;
  i = (int)elem;
  (*s).elems[i] = 1;
}
void removeElemSet(Set *s, char elem)
{
  int i;
  i = (int)elem;
  (*s).elems[i] = 0;
}
int isInSet(Set s, char elem)
{
  int i;
  i = (int)elem;
  if (s.elems[i] == 1)
  {
    return 1;
  } else
  {
    return 0;
  }
}
void unionSet(Set *s1, Set s2)
{
  int i;
  for (i=0; i < 255; i++)
  {
    (*s1).elems[i] = ((*s1).elems[i] | s2.elems[i]);
  }
}
void substrSet(Set *s1, Set s2)
{
  int i;
  for (i=0; i < 255; i++)
  {
    (*s1).elems[i] = ((*s1).elems[i] - s2.elems[i]);
    if ( (*s1).elems[i] < 0 )
    {
      (*s1).elems[i] = 0;
    }
  }
}
void intersectSet(Set *s1, Set s2)
{
  int i;
  for (i=0; i < 255; i++)
  {
    (*s1).elems[i] = ((*s1).elems[i] & s2.elems[i]);
  }
}
int isEqualSet(Set s1, Set s2)
{
  int i;
  for (i=0; i < 255; i++)
  {
    if (s1.elems[i] != s2.elems[i] )
    {
      return 0;
    }
  }
  return 1;
}
void copySet(Set *s1, Set s2)
{
  int i;
  for (i=0; i < 255; i++)
  {
    (*s1).elems[i] = s2.elems[i];
  }
}
int isEmptySet(Set s)
{
  int i;
  for (i=0; i < 255; i++)
  {
    if (s.elems[i] == 1 )
    {
      return 0;
    }
  }  
  return 1;
}
void printSet(Set s)
{
  int i;
  for (i=0; i < 255; i++)
  {
    if (s.elems[i] == 1 )
    {
      printf("%c",(char)i);
    }
  }
}  
    

Пример использования множества показан далее. Он будет помещен в отдельный файл "setexample.c", в который будет импортироваться реализованный тип данных путем включения заголовочного файла "set.h".

----- файл setexample.c -----
/******************************************************
date: 10 January 2013
description: Set type
*******************************************************/
#include <stdio.h>
#include "set.h"
int main()
{
  Set s1, s2;
/* Do not forget to do it! */
  initFromStringSet(&s1, "abcde");
  initSet(&s2);
  addElemSet(&s2,'a');
  addElemSet(&s2,'b');
  printf("\n s1: ");
  printSet(s1);
  printf("\n s2: ");
  printSet(s2);
  removeElemSet(&s1,'a');
  printf("\n s1: ");
  printSet(s1);
  unionSet(&s1, s2);
  printf("\n s1: ");
  printSet(s1);
  substrSet(&s1, s2);
  printf("\n s1: ");
  printSet(s1);
  addElemSet(&s1,'a');
  intersectSet(&s1, s2);
  printf("\n s1: ");
  printSet(s1);
  return 0;
}  
    

Из реализации данного относительного несложного типа можно вывести ряд правил, которые необходимо учитывать при разработке АТД.

Во-первых, это необходимость в импортере всегда использовать операцию создания, которая в примере реализована функцией initSet и функцией initFromStringSet. Так как массив в Си не инициализируется по умолчанию, то если в основной программе перед работой со множеством не вызвать initSet (или initFromStringSet), во множестве могут оказаться "лишние" элементы. При этом компилятор сам не может отследить корректность использования функций создания. Это целиком задача программиста.

Во-вторых, необходимо самостоятельно реализовать функции копирования и сравнения.

При реализации функций объединения и пересечения множеств используются операции побитового ИЛИ и побитового И, что упрощает реализацию и одновременно отражает суть соответствующих операций над множествами.

Несмотря на то, что массивы эффективнее передавать по ссылке, чтобы не копировать большой объем информации в память стека при передаче параметров, в данном примере переменные типа Set передаются по ссылке только в те функции, где они должны быть изменены. Это уменьшает вероятность случайного изменения значения переменной типа Set в остальных функциях и позволяет отражать в самом заголовке функции, происходит ли внутри нее изменение множества или нет.

В качестве примера использования множества рассматривается задача проверки символов введенной строки на допустимость. Пред-положим, необходимо проверить, является ли введенная строка шест-надцатеричным числом. Тогда можно сказать, что допустимый алфавит (т.е. допустимое множество) - это символы

0 1 2 3 4 5 6 7 8 9 A B C D E F

и все символы строки должны принадлежать этому алфавиту (множеству).

/* sample code - how Set can be used */
Set s3;
char str[100];
int i;
initFromStringSet(&s3,"0123456789ABCDEF");
printf("\nEnter HEX number: ");
scanf("%s", str);
i = 0;
while(str[i] != '\0')
{
  if (isInSet(s3, str[i]) == 0)
  {
    printf("You entered not a HEX number!");
    break;
  }
  i++;
}