2.1. Линейный (последовательный) поиск
Линейный поиск – самый простой из известных. Суть его заключается в следующем. Множество элементов просматривается последовательно в некотором порядке, гарантирующем, что будут просмотрены все элементы множества (например, слева направо). Если в ходе просмотра множества будет найден искомый элемент, просмотр прекращается с положительным результатом; если же будет просмотрено все множество, а элемент не будет найден, алгоритм должен выдать отрицательный результат. Именно так поступает человек, когда ищет что-то в неупорядоченном множестве. Например, при поиске нужной визитки в некотором неупорядоченном множестве визиток человек просто перебирает все визитки в поисках нужной.
Оформим описанный алгоритм в виде кусочка программы на Паскале и C. Пусть множество хранится в первых n элементах массива A. При этом не важен тип элементов множества, важна лишь возможность проверки эквивалентности элемента множества искомому элементу Key:
Пример на языке Паскаль:
i:= 1;
while (i<=n)and(A[i]<>Key) do Inc(i);
if A[i]=Key then <элемент найден>
else<элемент не найден>;
Пример на языке С:
i=1;
while ((i<=n)&&(A[i]!=Key)) i++;
if (A[i]==Key) <элемент найден>;
else <элемент не найден>;
В среднем этот алгоритм требует n/2 итераций цикла. Это означает сложность алгоритма поиска T(n). Единственное требование к способу хранения множества – существование некоторого порядка на множестве: чтобы можно было указать, что один элемент предшествует другому. Здесь можно хранить множество как в массиве (рассмотренный выше кусок программы), так и в обычном однонаправленном списке:
Пример на языке Паскаль:
Type
TListItem=record
Item: TItem;
Next: ^ListItem;
end;
Пример на языке С:
struct TListItem
{
TItem Item;
TListItem *Next;
};
Рассмотрим добавление элемента Key во множество, хранимое в виде массива. Здесь достаточно просто дописать новый элемент в “конец” множества:
Пример на языке Паскаль:
Inc(n);
A[n] :=Key;
Пример на языке С:
n++;
A[n]=Key;
Удалению элемента всегда предшествует успешный поиск. Это значит, что сложность операции удаления не может быть меньше сложности операции поиска. В данном случае при удалении нам придется сначала найти положение удаляемого элемента, затем все элементы, следующие за ним, сдвинуть на одну позицию к началу массива и уменьшить n. То есть в любом случае надо пробежать весь массив от начала до конца (это означает сложность T(n)):
Пример на языке Паскаль:
i:= 1;
while (i<=n)and(A[i]<>Key) do Inc(i); {поиск}
if A[i]=Key then
begin
while i<n do
begin
A[i] := A[i+1]; {сдвигаем}
Inc(i);
end;
Dec(n);
end;
Пример на языке С:
i=0;
while ((i<=n)&&(A[i]!=Key)) i++; //Поиск
if (A[i]==Key)
{
while (i<n)
{
A[i]=A[i+1]; //Сдвигаем
i++;
}
n--;
}
Теперь положим, что множество хранится в виде списка. Добавление элемента сведется к добавлению элемента в начало списка (при добавлении в конец списка пришлось бы сначала искать конец, либо специально хранить указатель на последний элемент списка). Удаление элемента будет состоять из поиска и собственно удаления (никаких сдвигов последующих элементов производить не придется.).