9. Въведение в структури от данни I


  Свързани списъци.
* В линейната структура свързан списък всяка стойност се съхранява в отделен блок от паметта, заедно с местоположението на съседните блокове в редицата. Този начин на съхранение позволява лесно да се вмъкне или отстрани елемент, без да се местят другите елементи. Както знаем, това е невъзможно, ако се използва вектор.
* Във всеки елемент на свързания списък се съхранява една данна и две връзки - съм следващия елемент от списъка и към предишнея елемент от списъка.  

* Вмъкването и изтриването на елемент от свързания списък става лесно - променят се стойностите на няколко връзки.

* Изтриването на елемент от свързан списък не изисква преместване на елементи на спистка.

За разлика от линейната структура вектора, където има пряк достъп до елементите на вектора (с операция индекс), свързаният списък не осигурява пряк достъп (не поддържа операция индекс).

В стандартната библиотека шаблони (STL) на С++ има реализация на линейната структура свързан списък - шаблонът list

// list1.cpp
#include <iostream>
#include <string>
#include <list>
using namespace std;

int main()
{  list<string> staff;     /* шаблон за списък */

   staff.push_back("Cracker, Carl");
   staff.push_back("Hacker, Harry");
   staff.push_back("Lam, Larry");
   staff.push_back("Sandman, Susan");

   list<string>::iterator pos;         /* итератор на списък */

/* добавя елемент на четвърто място */
   pos = staff.begin();
   pos++;
   pos++;
   pos++;
   staff.insert(pos, "Reindeer, Rudolf");

/* отстранява втория елемент */
   pos = staff.begin();
   pos++;
   staff.erase(pos);

/* добавя елемент на последно място */
   pos = staff.end();
   staff.insert(pos, "Zeider, Zeev");

/* обхождане на списък */
   for (pos = staff.begin(); pos != staff.end(); pos++)
      cout << *pos << "\n";      /* извежда съдържание на текущата позиция */
   return 0;
}

Cracker, Carl
Lam, Larry
Reindeer, Rudolf
Sandman, Susan
Zeider, Zeev
Стек и опашка
Стекът е линейна структура с добавяне и изтриване на елементи само от единия край, наречен връх на стека. Нарича се още LIFO от last in, first out. Две са основните операции: добавяне на елемент - push и изтриване на елемент pop.
Реализация в STL.
stack<string> s;
s.push("Tom");
s.push("Dick");
s.push("Harry");
while (s.size() > 0)
{ cout << s.top() << "\n";
s.pop()
}
Опашката е линейна структура от данни с добавяне на елементи от единия край и изтриване на елементи от другия край. Нарича се още FIFO от first in, first out. Реализация в STL:
queue<string> q;
q.push("Tom");
q.push("Dick");
q.push("Harry");
while (q.size() > 0)
{ cout << q.front() << "\n";
q.pop();
}
Пример:
// fifolifo.cpp
01: #include
<iostream> 02: #include <string>
03: #include <queue> 04: #include <stack> 06: using namespace std; 07: 08: int main() 09: { cout << "FIFO order:\n"; 11: 12: queue<string> q; 13: q.push("Tom"); 14: q.push("Dick"); 15: q.push("Harry"); 16: 17: stack<string> s; 18: while (q.size() > 0) 19: { string name = q.front(); 21: q.pop(); 22: cout << name << "\n"; 23: s.push(name); 24: } 26: cout << "LIFO order:\n"; 28: while (s.size() > 0) 29: { cout << s.top() << "\n"; 31: s.pop(); 32: } 34: return 0; 35: }

Други стандартни контейнери

Контейнерът множество (set) поддържа наредба на елементите, независимо в какъв ред са въведени.
// set.cpp
#include <iostream> #include <string> #include <set> using namespace std; int main() { set<string> s; s.insert("Tom"); s.insert("Dick"); s.insert("Tom"); // !!! s.insert("Harry"); cout << "set: " << endl; set<string>::iterator p; for (p = s.begin(); p!= s.end(); p++) cout << *p << "\n"; multiset<string> ms; ms.insert("Tom"); ms.insert("Dick"); ms.insert("Tom"); // !!! ms.insert("Harry"); cout << "multiset: " << endl; multiset<string>::iterator mp; for (mp = ms.begin(); mp!= ms.end(); mp++) cout << *mp << "\n"; cout << ms.size() << endl; cout << ms.count("Tom"); return 0; }
Контейнерът map прилича на вектор с различен тип на индексите.
map<string, double> scores;
scores["Tom"] = 90;
scores["Dick"] = 86;
scores["Harry"] = 100;
Осигурява пряк и ефективен достъп до елементите.
// map.cpp
#include <iostream> #include <string> #include <map> using namespace std; int main() { map<string, int> scores; scores["Tom"] = 90; scores["Dick"] = 86; scores["Harry"] = 100; map<string, int>::iterator p; for (p = scores.begin(); p!= scores.end(); p++) cout << p->first << " " << p->second << "\n"; cout << scores.size() << endl; multimap<string, int> mmap; mmap.insert(pair<string, int>("Tom", 90)); mmap.insert(pair<string, int>("Dick", 86)); mmap.insert(pair<string, int>("Harry", 100)); mmap.insert(pair<string, int>("Tom", 190)); multimap<string, int>::iterator q; for (q = mmap.begin(); q!= mmap.end(); q++) cout << q->first << " " << q->second << "\n"; cout << mmap.size() << endl; return 0;
}

Multimap е контайнер от двойки елементи - ключ, стойност.
multimap<string, double> mmap;
mmap.insert(pair("Tom", 90));
mmap.insert(pair("Dick", 86));
mmap.insert(pair("Harry", 100));
mmap.insert(pair("Tom", 190));

Алгоритми в STL
Причината за въвеждане и използване на итератори е отделянето на алгоритмите от структурите от данни. Алгоритмите в STL са реализирани от функции, които работят само с итератори и по такъв начин са независими от контейнерите, където се съхраняват данните.
Пример: Функция, която намира сумата на елементите на контейнер - вектор или списък.
vector<double> data;
/* do something with data */

double vsum = 0;
accumulate(data.begin(), data.end(), vsum);
/* now vsum contains the sum of the elements in the vector */
list<double> salaries;
/* do something with salaries */

double lsum = 0;
accumulate(salaries.begin(), salaries.end(), lsum);
/* now lsum contains the sum of the elements in the list */
Библиотеката поддържа няколко функции за търсене.
/* search for a certain name on the staff */
list<string>::iterator it =
find(staff.begin(), staff.end(), name);
cout << *it << endl;