Алгоритми

  • STL предлага много алгоритми
  • ще разгледаме само част от тях

Видове алгоритми

  • не-променящи последователни
  • променящи последователни
  • сортиращи и търсещи
  • множествени
  • пирамидални
  • числени
  • общи

Не-променящи последователни

  • for_each()
  • find()
  • find_end()
  • find_first_of()
  • adjascent_find()
  • count()
  • mismatch()
  • equal()
  • search()

Променящи последователни

  • copy()
  • swap()
  • transform()
  • replace()
  • fill()
  • generate()
  • remove()
  • unique()
  • reverse()
  • rotate()
  • random_shuffle()
  • partition()

Алгоритми за сортиране и търсене

  • sort()
  • stable_sort()
  • partial_sort()
  • nth_element()
  • lower_bound()
  • upper_bound()
  • equal_range()
  • binary_search()
  • merge()

Алгоритми за множества

  • includes()
  • set_union()
  • set_intersection()
  • set_difference()
  • set_symmetric_difference()

Тези алгоритми работят само върху сортирани последователности!!!

Алгоритми за пирамиди

  • push_heap()
  • pop_heap()
  • make_heap()
  • sort_heap()

numeric алгоритми

  • accumulate()
  • inner_product()
  • partial_sum()
  • adjascent_difference()

Общи алгоритми

  • min(),max()
  • min_element(),max_element()
  • lexicographical_compare()
  • next_permutation()
  • prev_permutation()

for_each

  • for_each(c.begin(),c.end(),g);
  • Стандартът казва, чеfor_eachе алгоритъм, който не променя редицата, върху която се изпълнява.
  • Функцията, която подадете наfor_eachможе да промени стойността на аргумента си.
  • Но това е лош стил - ако искате да променяте редицата използвайтеtransform
  • for_eachвръща функцията, с която е била извикана

Пример за for_each

struct Concat : unary_function<string, void>
{
        void operator()(const string& s)
        {
                result += s;
        }
        string result;
} ;
// ...
// v is container of strings
cout << for_each(v.begin(), v.end(), Concat()).result << endl;
cout << accumulate(v.begin(), v.end(), "") << endl;

transform

прилага функцията, подадена му като аргумент, върху всички елементи на една редица и запазва резултата в друга редици

Използване на transform

vector<int> v;
transform(v.begin(), v.end(), v.begin(), negate<int>());
// сменя знака на числата във v

vector<int> r;
transform(v.begin(), v.end(), back_inserter(r),
    bind1st(plus<int>(), 42));
    
vector<int> a, b;
transform(a.begin(), a.end(), b.begin(), b.end(),
    a.begin(), plus<int>());
// добава b към a почленно

Сортиране и търсене

  • Много задачи изискват сортиране
  • Не по-малко задачи изискватпочтисортиране
  • STL предлага алгоритми за всички основни типове задачи

Пълно сортиране

template <typename Iterator>
void sort(Iterator begin, Iterator end);
// сортира използвайки operator< в нарастващ ред

template <typename Iterator, typename Ordering>
void sort(Iterator begin, Iterator end, Ordering c);

// sort използва introsort алгоритъма и изисква random
// access iterators
// Average O(N*logN), Worst case О(N^2)

// за сортиране на list
list<int> l;
l.sort();
l.sort(greater<int>());

Стабилно сортиране

template <typename Iterator>
void stable_sort(Iterator begin, Iterator end);

template <typename Iterator, typename Ordering>
void stable_sort(Iterator begin, Iterator end, Ordering c);

// не по-бавно от O(N*logN*logN)
// O(N*logN) ако има достатъчно голям свободен буфер

Частично сортиране

10-те студента с най-висок успех?

vector<int> v;
// ...
vector<int>::iterator n = v.begin() + 10;
partial_sort(v.begin(), n, v.end());
// сортира [v.begin(), n) - поставя най-малките 10
// елемента сортирани в началото на v
// O(N*logN)

N-ти елемент

10-тия по успех студент?

vector<int> v;
// ...
vector<int>::iterator n = v.begin() + 10;
nth_element(v.begin(), n, v.end());
// *n ще бъде 10-тото най-малко число
// всички числа в [v.begin(), n) са по-малки или равни на *n
// всички числа в [n + 1, v.end()) са по-големи или равни на *n

// Avarage: O(N)

lower_bound

Връща итератор към първата позиция, в която можем да вмъкнем елемента без да нарушим подредеността на редицата.

lower_bound ИЗИСКВА редицата да е била сортирана

lower_bound прави O(logN) сравнения

lower_bound прави O(logN) стъпки за random access iterator и O(N) за forward

vector<int> v;
// ...
sort(v.begin(), v.end());
int x = g(42);
vector<int>::iterator p = lower_bound(v.begin(), v.end(), x);
if (p != v.end() && !(*p < x || x < *p))
    cout << "there is " << x << " in v" << endl;
v.insert(p, x);

// O(logN)

upper_bound

Връща итератор към последната позиция, в която можем да вмъкнем елемента без да нарушим подредеността на редицата. Ако съществува еквивалентен на дадения елемент в редицата,upper_boundсочи точно след последния такъв.

upper_bound има същото поведение както lower_bound относно редицата и итераторите

equal_range

Връща std::pair отlower_boundиupper_bound

typedef vector<int> Vector;
typedef vector<int> VectorIt;
Vector v;
// ...
sort(v.begin(), v.end());
pair<VectorIt, VectorIt> r = equal_range(v.begin(), v.end(), 42);
if (r.first == r.second)
    cout << "42 is not there";
v.insert(r.second, 42);
// вмъква 42

// O(logN)

binary_search

Връщаtrue/falseрезултат дали даден елемент се среща вСОРТИРАНАредица.

vector<int> v;
// ...
sort(v.begin(), v.end());
if (binary_search(v.begin(), v.end(), 42))
    cout << "42 is the Answer" << endl;
    
// O(logN)

Как Ви се струва това?

class A { } ;

bool operator==(const A& lhs, const A& rhs);

bool mycmp(const A& x, const A& y) {
    return x.better_than(y);
}

vector<A> v;
// ...
sort(v.begin(), v.end(), mycmp);
A x = g(42);
vector<A>::iterator p = lower_bound(v.begin(), v.end(), mycmp);
if (p != v.end() && *p == x)
    cout << "there is " << x << " in v" << endl;
    

Бележки за алгоритмите за сортиране и търсене

  • всички алгоритми могат да използват частична наредба определена от потребителя вместоoperator<.
  • ако използвате Ваша наредба трябва да се изключително внимателни да използвате навсякъде една и съща наредба
  • Алгоритмите за търсене работят върху вечеПОДРЕДЕНИредици
  • Правете разлика между равенство (operator\==) и еквивалентност. Използвайте или само едното или само другото!
  • асоциативните контейнери имат методиfind,lower_bound,upper_bound,equal_range
  • list::sortестабилносортиране O(NlogN)

Числови алгоритми

  • намират се в
  • TR1 добава много повече функции, включително и генериране на произволни числа

accumulate

vector<int> v;
// ...
int sum = accumulate(v.begin(), v.end(), 0);
// uses operator+
int prod = accumulate(v.begin(), v.end(), 1, multiplies<int>());

partial_sum

vector<int> v;
// ...
int sum = partial_sum(v.begin(), v.end());
// uses operator+
int prod = partial_sum(v.begin(), v.end(), multiplies<int>());
int c = partial_sum(v.begin(), v.end(), Combiner());

adjacent_difference

Смята разликите между два съседни елемента и ги запазва в друга редица

vector<int> v, r;
// ...
adjacent_difference(v.begin(), v.end(), back_inserter(r));
// r[0] = v[0]
// r[1] = v[1] - v[0];
// r[n+1] = v[n+1] - v[n];

inner_product

Смята произведението на две редици:

a1*b1 + a2*b2 + ... + aN*bN
vector<double> a, b;
// a и b са вектори зададени с координати в ортогонален базис
double ab = inner_product(a.begin(), a.end(), b.begin(), 0);
// ab е скаларното произведение на двата вектора