12. Вектори

План:
Използване на вектори за съхраняване на данни
Индекси на вектори
Векторите като параметри и стойности на функции
Вектор като връщана стойност на функция
Прости алгоритми за вектори - вмъкване и изтриване
Успоредни вектори и вектор от обекти


** Използване на вектори за съхраняване на данни

* Векторът е множество от данни от един и същи тип и данните са номерирани с последователни неотрицателни цели числа.
Пример: Дефиниция на вектор с 10 елемента тип double (конструиране на обект salaries):
  vector<double> salaries(10);
Клас-шаблон (
vector) и параметър на шаблона (double).
 
Пример: Достъп до елементите на вектора - на елемента с индекс 4 се задава стойност 355.
  salaries[4] = 355;
Елементите на вектора се номерират, започвайки от 0 (както позициите в низа).
  salaries[0] - първи елемент
  salaries[1] - втори елемент
  salaries[2] - трети елемент
  salaries[3] - четвърти елемент
  ...
  salaries[9] - десети елемент

Индекс
0
1
2
3
4
5
6
7
8
9
Стойност
    ?
    ?
    ?
    ?
355
    ?
    ?
    ?
    ?
    ?


**  Индекси на вектори
 
* Достъп до елементите на вектора дава операция индекс (subscript).
* Граници на индексите са от 0 до размера на вектора минус 1.
* Член-функция за получаване на размера на вектора - size().
Пример: Типичен цикъл за обхождане на елементите на вектора (в случая за отпечатването им) с използване на тази член-функция и операция индекс.
  for (i=0; i < salaries.size(); i++) cout << salaries[i];
* Дефиниране на вектор с 0 елементи (конструктор по подразбиране).
  vector<double> salaries;
 
* Типът vector е реализиран като клас-шаблон в STL.
* В този клас са дефинирани член-функциите:
push_back добавяне на елемнт към вектора (след последния) и
- pop_back
за премахване (изтриване) на последния елемент (с индекс размера на вектора минус 1).
Пример:
  salaries.push_back(2000)- добавяне на елемент 2000 към вектора (след последния);
  salaries.pop_back()- премахване на  последния елемент на вектора.
 
Пример: Четат се заплати на служители, стойностите се записват във вектор и се печати отбелязка на най-високата заплата.
 // salvect.cpp
#include
<iostream>
#include <vector> 
using namespace std;
 
int main()
{  vector<double> salaries;
// въвеждане на стойности на елементите на вектора
    bool more = true;
    while (more)
    {  double s;
       cout << "Please enter a salary, 0 to quit: ";
       cin >> s;
       if (s == 0) more = false;
       else salaries.push_back(s);
    }
// намиране на най-големия елемент на вектора
    double highest = salaries[0];
    int i;
    for (i = 1; i < salaries.size(); i++)
       if (salaries[i] > highest)
          highest = salaries[i];
 // извеждане на стойностите на елементите на вектора с отбелязване на най-големия елемент
    for (i = 0; i < salaries.size(); i++)
    {  if (salaries[i] == highest) 
          cout << "highest value => ";
       cout << salaries[i] << "\n";
    } 
    return 0;
}
nkirov@cpp % ./a.out
Please enter a salary, 0 to quit: 2500
Please enter a salary, 0 to quit: 3200
Please enter a salary, 0 to quit: 1850
Please enter a salary, 0 to quit: 3000
Please enter a salary, 0 to quit: 2300
Please enter a salary, 0 to quit: 0
2500
highest value => 3200
1850
3000
2300


** Векторите като параметри и стойности на функции

Пример: Намиране на средно аритметично на елементите на вектор. (Използва се параметър-променлива или константен псевдоним!)

// average.cpp
#include <iostream>
#include <vector>
using namespace std; 

/* v е параметър-променлива */
double average(vector<double> v)
{  if (v.size() == 0) return 0;
   int i;
   double sum = 0;
   for (i = 0; i < v.size(); i++)  sum = sum + v[i];
   return sum / v.size();
}
int main()
{  vector<double> salaries(5);
   salaries[0] = 35000.0;
   salaries[1] = 63000.0;
   salaries[2] = 48000.0;
   salaries[3] = 78000.0;
   salaries[4] = 51500.0;

   double avgsal = average(salaries);
   cout << "The average salary is " << avgsal << "\n";
   return 0;
}

The average salary is 55100

Пример: Промяна на стойностите на елементите на вектор - заплатите се увеличават с 4.5 процента.  (Използва се параметър-псевдоним!)

// raisesal.cpp
#include <iostream>
#include <vector>
using namespace std;

/* s е параметър-псевдоним */
void raise_salaries(vector<double>& s, double p)
{  int i;
   for (i=0; i<s.size(); i++) s[i] = s[i]*(1 + p/100);
}
int main()
{  vector<double> salaries(5);
   salaries[0] = 35000.0;
   salaries[1] = 63000.0;
   salaries[2] = 48000.0;
   salaries[3] = 78000.0;
   salaries[4] = 51500.0;
   raise_salaries(salaries, 4.5);
   int i;
   for (i = 0; i < salaries.size(); i++)
            cout << salaries[i] << "\n";
   return 0;
}

36575
65835
50160
81510
53817.5

* Вектор като връщана стойност на функция

Пример: Намиране на всички стойности, които попадат в определен интервал.
Функцията връща стойност от тип vector<double>.

// between.cpp
#include <iostream>
#include <vector>
using namespace std;

/* връщаната стойност на функцията е от тип vector */
vector<double>
        between(vector<double> v, double low, double high)
{ vector<double> result;
  int i;
  for (i = 0; i < v.size(); i++)
  if (low <= v[i] and v[i] <= high) result.push_back(v[i]);
  return result;
}
int main()
{  vector<double> salaries(5);
   salaries[0] = 35000.0;
   salaries[1] = 63000.0;
   salaries[2] = 48000.0;
   salaries[3] = 78000.0;
   salaries[4] = 51500.0;
   vector<double> midrange_salaries
                  = between(salaries, 45000.0, 65000.0);
   int i;
   for (i = 0; i < midrange_salaries.size(); i++)
                   cout << midrange_salaries[i] << "\n";
   return 0;
}

63000
48000
51500

Пример
: Намиране на всички съответствия.
Функцията връща
vector<int>, съдържащ индексите на всички елементи, които удовлетворяват дадено условие.

// matches.cpp
#include <iostream>
#include <vector>
using namespace std;

vector<int> find_all_between(vector<double> v, 
                        double low, double high)

/* ЦЕЛ:      намира елементите на вектора a
             със стойност по-голяма от low и по-малка от high
   ПОЛУЧАВА: вектор a и число t
   ВРЪЩА:    вектор с индексите на намерените елементи,
             по-големи от t
*/

{  vector<int> pos;
    for (int i = 0; i < v.size(); i++)
      if (low <= v[i] && v[i] <= high)
          pos.push_back(i);
    
    return pos;
}

int main()
{  vector<double> salaries(5);
    salaries[0] = 35000.0;
    salaries[1] = 63000.0;
    salaries[2] = 48000.0;
    salaries[3] = 78000.0;
    salaries[4] = 51500.0;
 
    vector<int> matches
       = find_all_between(salaries, 45000.0, 65000.0);
 
    for (int j = 0; j <
matches.size(); j++)
       cout << salaries[
matches[j]] << "\n";
    return 0;
}

63000
48000

51500

** Прости алгоритми за вектори

*
За връзката: език за програмиране, алгоритми и структури от данни

Пример: Изтриване на елемент от вектор.  
Целта е да се изтрие един елемент на вектора и всички елементи след него да се преместят с една позиция назад - т.е. индексите на елементите след него да се намалят с 1.

 erase
// erase2.cpp

#include <iostream>

#include <string>

#include <vector>

using namespace std;

void erase(vector<string>& a, int pos)
/* ЦЕЛ: отстранява елемент с индекс pos от вектор a
   ПОЛУЧАВА: вектор a и индекс pos
   ЗАБЕЛЕЖКА: редът на оставащите елементи се запазва
*/
{  int i;
   for (i = pos; i < a.size()-1; i++) a[i] = a[i+1];
   a.pop_back();
}
void print(vector<string> a)
{  int i;
   for (i = 0; i < a.size(); i++)
      cout << "[" << i << "] " << a[i] << "\n";
}
int main()
{  vector<string> staff(5);
   staff[0] = "Cracker, Carl";
   staff[1] = "Hacker, Harry";
   staff[2] = "Lam, Larry";
   staff[3] = "Reindeer, Rudolf";
   staff[4] = "Sandman, Susan";
   print(staff);
   int pos;
   cout << "Remove which element? ";
   cin >> pos;
   erase(staff, pos);
   print(staff);
   return 0;
}

[0] Cracker, Carl
[1] Hacker, Harry
[2] Lam, Larry
[3] Reindeer, Rudolf
[4] Sandman, Susan
Remove which element? 2
[0] Cracker, Carl
[1] Hacker, Harry
[2] Reindeer, Rudolf
[3] Sandman, Susan

Пример: Вмъкване на елемент във вектор.
Целта е да се вмъкне нов елемент на вектора на зададена позиция и всички елементи след него да се преместят с една позиция напред - т.е. индексите на елементите след новия елемент да се увеличат с 1.

insert

// insert.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;

void insert(vector<string>& a, int pos, string s)
/* ЦЕЛ: вмъква елемент s с индекс pos във вектора a
   ПОЛУЧАВА: вектор a, индекс pos и елемент s
   ЗАБЕЛЕЖКА: редът на останалите елементи се запазва
*/
{  int last = a.size() - 1;
   a.push_back(a[last]);
   int i;
   for (i = last; i > pos; i--) a[i] = a[i - 1];
   a[pos] = s;
}
void print(vector<string> a)
{  int i;
   for (i = 0; i < a.size(); i++)
      cout << "[" << i << "] " << a[i] << "\n";
}
int main()
{  vector<string> staff(5);
   staff[0] = "Cracker, Carl";
   staff[1] = "Hacker, Harry";
   staff[2] = "Lam, Larry";
   staff[3] = "Reindeer, Rudolf";
   staff[4] = "Sandman, Susan";
   print(staff);
   int pos;
   cout << "Insert before which element? ";
   cin >> pos;
   insert(staff, pos, "New, Nina");
   print(staff);
   return 0;
}

[0] Cracker, Carl
[1] Hacker, Harry
[2] Lam, Larry
[3] Reindeer, Rudolf
[4] Sandman, Susan
Insert before which element? 1
[0] Cracker, Carl
[1] New, Nina
[2] Hacker, Harry
[3] Lam, Larry
[4] Reindeer, Rudolf
[5] Sandman, Susan

** Успоредни вектори и вектор от обекти
 
*  Два вектора са успоредни, ако съществува логическа (смислова) връзка между елементите им с еднакви индекси. 

Пример: Задачата за най-добро отношение цена-качество на компютри (6. Класове) с отбелязване на най-добрата оферта в списъка от входа - решение с успоредни вектори и без класове.
 // bestval1.cpp
#include
<iostream>
#include <string>
#include <vector> 
using namespace std;
 
int main()
{  vector<string> names;
    vector<double> prices;
    vector<int> scores;
 
    double best_price = 1;
    int best_score = 0;
    int best_index = -1;
 
    bool more = true;
    while (more)
    {  string next_name;
       cout << "Please enter the model name: ";
       getline(cin, next_name);
       names.push_back(next_name);
       double next_price;
       cout << "Please enter the price: ";
       cin >> next_price;
       prices.push_back(next_price);
       int next_score;
       cout << "Please enter the score: ";
       cin >> next_score;
       scores.push_back(next_score);
       string remainder; /* read remainder of line */
       getline(cin, remainder); 
 
       if (next_score / next_price > best_score / best_price)
       {  best_index = names.size() - 1;
          best_score = next_score;
          best_price = next_price;
       }      
       cout << "More data? (y/n) ";
       string answer;
       getline(cin, answer);
       if (answer != "y") more = false;
    } 
    for (int i = 0; i < names.size(); i++)
    {  if (i == best_index) cout << "best value => ";
       cout << names[i]
            << " Price: " << prices[i]
            << " Score: " << scores[i] << "\n";
    } 
    return 0;
}

nkirov@cpp % c++ bestval1.cpp
nkirov@cpp % ./a.out
Please enter the model name: abc
Please enter the price: 1000
Please enter the score: 75
More data? (y/n) y
Please enter the model name: xyz 23
Please enter the price: 1200
Please enter the score: 85
More data? (y/n) n
best value => abc Price: 1000 Score: 75
xyz 23 Price: 1200 Score: 85

* Успоредните вектори обикновено създават проблеми и трябва да се избягват.

Пример: Задачата за най-добро отношение цена-качество на компютри с отбелязване на най-добрата оферта в списъка от входа - решение без успоредни вектори и с класове (вектор от обекти).

 // bestval2.cpp
#include
<iostream>
#include <string>
#include <vector> 
using namespace std;
 
class Product {
public:
    Product();
    void read();
    bool is_better_than(Product b) const;
    void print() const;
private:
    string name;
    double price;
    int score;
};
 
Product::Product()
{  price = 0;
    score = 0;
}
 
void Product::read()
{  cout << "Please enter the model name: ";
    getline(cin, name);
    cout << "Please enter the price: ";
    cin >> price;
    cout << "Please enter the score: ";
    cin >> score;
    string remainder; /* read remainder of line */
    getline(cin, remainder);
}
 
bool Product::is_better_than(Product b) const
{  if (price == 0) return false;
    if (b.price == 0) return true;
    return score / price > b.score / b.price;
}
 
void Product::print() const
{  cout << name
         << " Price: " << price
         << " Score: " << score << "\n";
}
 
int main()
{  vector<Product> products;
    Product best_product;
    int best_index = -1;
 
    bool more = true;
    while (more)
    {  Product next_product;
       next_product.read();
       products.push_back(next_product);
 
       if (next_product.is_better_than(best_product))
       {  best_index = products.size() - 1;
          best_product = next_product;
       }     
 
       cout << "More data? (y/n) ";
       string answer;
       getline(cin, answer);
       if (answer != "y") more = false;
    }
 
    for (int i = 0; i < products.size(); i++)
    {  if (i == best_index) cout << "best value => ";
       products[i].print();
    } 
    return 0;
}