Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
kernigan_paik.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.91 Mб
Скачать

2.1. Поиск

Ничто не сравнится с массивом, если нам нужно хранить статические табличные данные. Инициализация во время компиляции делает задачу конструирования таких массивов простой и легкой. (В Java инициализа­ция происходит во время выполнения, но это можно считать незначи­тельной деталью реализации, пока массивы не слишком велики.) В про­грамме для распознания слов, слишком часто употребляемых в плохой прозе, мы можем написать:

char *flab[] = {

"actually",

"just",

"quite",

"really",

NULL };

Функция поиска должна знать, сколько в массиве элементов. Один из способов сообщить ей это — передать длину массива в виде аргумента; второй способ, использованный здесь, — поместить в конце массива эле­мент-маркер NULL:

/* lookup: последовательный поиск слова в массиве */

int lookup(char *word, char *array[])

{

int i;

for (i = 0; array[i] ! = NULL;

if (strcmp(word, array[i] ) == 0)

return i;

return -1;

}

В С и C++ для передачи в качестве параметра массив строк можно опи-'•сать как char *array[] или char **array. Эти две формы эквивалентны, но в первой сразу видно, как будет использоваться параметр.

Предлагаемый поисковый алгоритм называется последовательным поиском, потому что он просматривает по очереди все элементы, сравни-|вая их с искомым. Когда данных немного, последовательный поиск ра­ботает достаточно быстро. Есть стандартные библиотечные функции, которые выполняют последовательный поиск для определенных типов ' данных. Например, в языках С и C++ функции st reh г и st rst r ищут пер-; вое вхождение заданного символа или подстроки в строку, в Java у клас-[са String есть метод indexOf, а обобщенные функции поиска в C++ find применимы к большинству типов данных. Если такая функция суще-|ствует для нужного вам типа данных, то используйте ее.

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

Вот пример массива более реалистичного размера из программы, вы­полняющей синтаксический разбор текста, написанного на HTML, где определены имена более чем сотни отдельных символов:

typedef struct Nameval Nameval;

struct Nameval {

char *name;

int value;

};

/* символы HTML например AElig - лигатура А и Е. */

/* значения в кодировке Unicode/13010646 */

Nameval htmlchars[] = {

"AElig", 0х00с6, /*лигатура А и Е*/

"Aacute", 0x00c1, /*А с акцентом*/

"Acirc", 0x0c2, /*А с кружочком*/

/*.......*/

"zeta", 0х0Зb6, /* греческая дзета */

Для объемистого массива вроде этого более эффективно было бы использовать двоичный поиск. Алгоритм двоичного поиска является сис­тематизированной версией поиска слова в словаре. Проверяем сред­ний элемент. Если это значение больше, чем нужное, то ищем далее в первой части; в противном случае ищем во второй части. Повторяем до тех пор, пока не найдем нужный элемент или не убедимся, что его в массиве нет.

Для двоичного поиска таблица должна быть отсортирована, как в дан­ном случае (в любом случае это полезно; люди тоже быстрее находят требуемое в отсортированных таблицах), а также должно быть известно, сколько элементов в таблице. Здесь может помочь макрофункция NELEMS из первой главы:

printf("Таблица HTML содержит %d слов\n", NELEMS(htmlchars));

Функция двоичного поиска для этой таблицы могла бы выглядеть так:

/* lookup: двоичный поиск имени name в таблице tab;

возвращается индекс */

int lookup(char *name, Nameval tab[], int ntab)

{

int low, high, mid, cmp;

low = 0;

high = ntab - 1;

while (low <= high) {

mid = (low + high) / 2;

cmp = strcmp(name, tab[mid].name);

if (cmp < 0)

high = mid - 1;

else if (cmp > 0)

low = mid + 1;

else /* совпадение найдено */

return mid;

}

return -1; /* совпадений нет */

Объединяя все это вместе, мы можем написать:

half = Iookup("frac12", htmlchars, NELEMS(htmlchars));

для определения индекса, под которым символ '/2 (одна вторая) стоит, в массиве htmlchars.

Двоичный поиск отбрасывает за каждый шаг половину данных, по­этому количество шагов пропорционально тому, сколько раз мы можем поделить и на 2, пока у нас не останется один элемент. Без учета округле­ния это число равно Iog2 п. Если у нас в массиве 1000 элементов, то ли­нейный поиск займет до 1000 шагов, в то время как двоичный — только около 10; при миллионе элементов линейный поиск займет миллион ша­гов, а двоичный — 20. Очевидно, чем больше число элементов, тем боль­ше преимущество двоичного поиска. Начиная с некоторого зависящего от реализации размера данных, двоичный поиск работает быстрее, чем линейный.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]