Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Динамические структуры данных1 - копия.doc
Скачиваний:
2
Добавлен:
31.07.2019
Размер:
79.87 Кб
Скачать

Указатели.

Указатель – переменная, предназначенная для хранения адреса некоторого объекта.

Имеется две взаимно обратных операции:

& - взятие адреса.

- снятие адреса.

&x – это адрес переменной x.

*p – это значение переменной, лежащей по адресу p.

Имя массива – это адрес его нулевого элемента. Имя функции – это адрес точки входа в функцию.

К указателю можно прибавлять целое число. Если указатель указывает на переменную типа T, то прибавление к нему числа m перемещает его на m * sizeof (T) байтов. Это позволяет строить индексные выражения для массивов ( см. лекцию про массивы ).

Указатели на переменные одного типа можно вычитать. При этом, если указатели p1 и p2 указывают на объекты типа Т, расположенные в памяти через n байтов, то p2 - p1 = n / sizeof (T).

Указатели объявляют точно так же, как и любую другую переменную.

Пример.

int *p;

Здесь написано, что *p является переменной типа int. Тогда p является указателем на переменную типа int.

Можно инициализировать указатели при объявлении.

int x=5;

int *p = &x;

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

Приведем несколько примеров.

int *p; p – указатель на переменную типа int.

int *p[5]; p – массив из пяти указателей на тип int

( p[0], p[1], … p[4] - – указатели на переменную типа int.)

int (*p) [5]; p – указатель на массив из пяти целых элементов.

struct c{

float x;

float y;

} *p; p – указатель на структуру.

(доступ к отдельным полям – p->x, p->y.

См. лекцию про структуры)

float (*p)(char, int); p – указатель на функцию, зависящую от двух

аргументов типа char и int и возвращающую

значение типа float.

float *p(char, int); p – функция, зависящая от двух аргументов

типа char и int и возвращающая указатель на float.

float (*p[5])(int); p – массив из пяти указателей на функции,

зависящие от аргумента типа int и возвращающие

значение типа float.

Мы уже использовали указатели для доступа к элементам одномерных и двумерных массивов, для передачи в функцию адреса переменной, что позволяет изменять значение самой переменной (см. лекцию про функции), а также приводили пример функции, зависящей от указателя на функцию.

Указатели можно использовать для организации достаточно сложных динамических типов данных.

В качестве примеров реализуем список и бинарное дерево.

Списки.

Если у нас есть множество элементов, каждый из которых расположен в своей области памяти, то возникает проблема доступа к произвольному элементу нашего множества. Один из вариантов ее решения – сделать так, чтобы каждый элемент содержал указатель на следующий элемент. Тогда для доступа ко всем элементам множества нам необходимо знать только указатель на первый элемент. Такая конструкция называется однонаправленным линейным списком. Каждый отдельный элемент вместе с указателем на следующий называется узлом списка.

Узел списка удобно хранить в виде структуры. Пусть, например мы создаем однонаправленный список из целых чисел. Тогда соответствующая структура должна выглядеть примерно так:

struct Node {

int d;

Node * next;

};

Здесь d – это наше данное (иногда его называют ключ), next – указатель на следующий узел списка.

Указатель next последнего узла списка может указывать на NULL, тогда список называется линейным. Можно сделать кольцевой список, заставив указатель последнего узла указывать на первый.

Понадобится еще указатель на начало списка pbeg. Он тоже должен иметь тип Node *.

Можно в каждый узел добавить еще и указатель на предыдущий элемент. Тогда по списку можно будет двигаться в обоих направлениях. В этом случае желательно иметь также и указатель pend на конец списка.

Над списками можно выполнять следующие операции:

  1. Начальное формирование списка.

  2. Добавление элемента в конец списка.

  3. Поиск элемента с заданным ключом.

  4. Вставка элемента в список после элемента с заданным ключом.

  5. Удаление элемента с заданным ключом.

В качестве примера реализуем двунаправленный линейный список целых чисел на языке С++.

#include<iostream.h>

struct Node{

int d;

Node *next;

Node * prev;

};

Node *first(int d);

void add(Node **pend, int d);

Node *find(Node * const pbeg,int i);

int remove(Node **pbeg,Node **pend,int key);

Node *insert(Node * const pbeg,Node **pend,int key,int d);

main(){

Node *pbeg=first(1);

Node *pend=pbeg;

for(int i=2;i<6;i++){

add(&pend,i);

}

insert (pbeg, &pend,2,200);

remove(&pbeg, &pend,5);

Node *pv=pbeg;

while(pv){

cout<<pv->d<<' ';

pv=pv->next;

}

return(0);

}

Node *first(int a){

Node *pv=new Node;

pv->d=a;

pv->next=NULL;

pv->prev=NULL;

return(pv);

}

void add(Node **pend, int d){

Node *pv=new Node;

pv->d = d;

pv->next = NULL;

pv->prev = *pend;

(*pend) -> next =pv;

*pend = pv;

}

Node *find(Node * const pbeg, int d){

Node *pv = pbeg;

while(pv){

if(pv->d == d)

break;

pv=pv -> next;

}

return(pv);

}

int remove(Node **pbeg, Node **pend, int key){

Node *pkey = find(*pbeg,key);

if(pkey){

if(pkey == *pbeg){

*pbeg = (*pbeg) -> next;

(*pbeg) -> prev = NULL;

}

else if(pkey == *pend){

*pend = (*pend) -> prev;

(*pend) -> next = NULL;

}

else{

(pkey -> prev) -> next = pkey -> next;

(pkey -> next) -> prev = pkey -> prev;

}

delete pkey;

return (1);

}

return(0);

}

Node * insert(Node * const pbeg, Node **pend, int key, int d){

Node *pkey = find(pbeg, key);

if(pkey){

Node *pv = new Node;

pv -> d = d;

pv -> next = pkey -> next;

pv -> prev = pkey;

pkey -> next = pv;

if(pkey != *pend)

(pv -> next) -> prev = pv;

else

*pend = pv;

return (pv);

}

return(NULL);

}

Вставить в нужные места комментарии и пояснить программу. Нарисовать соответствующие картинки.