Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.47 Mб
Скачать

Поделиться в соц. Сетях Структуры данных. Стек

Опубликовано 16.05.2013 автором Dauren

Что такое структура данных?

Это способ хранения данных по особым правилам, использующий функции для доступа к ним. Одним из структур данных является стек (англ. stack - стопка).

Стек немного схож с ханойской башней. Вкратце вся суть этой игры такова:

  • Имеется доска с тремя стержнями;

  • На первом находятся восемь колец упорядоченных по размеру, т.е. получается своеобразная пирамида;

  • Нужно перенести кольца с первого стержня на третий, притом кольцо может лежать лишь на доске или же на кольце большего размера.

Теперь более формально.

Стек это — структура данных, использующая следующие функции:

  • push (x): добавить элемент x на самый верх стека;

  • top (): возвратить значение самого верхнего элемента;

  • pop (): удалить самый верхний элемент;

Получается некий шампур (т.е. стек), на который мы нанизываем еду (т.е информацию).

Cтек уже реализован в С++, в качестве контейнера. Перечислим все его функции:

  • stack < a > b - создать стек типа b с именем a;

  • b.push (x) — добавить в стек b элемент x (x должен быть того же типа, что и сам стек);

  • b.pop () - удалить самый верхний элемент стека;

  • b.top () — возвратить значение самого верхнего элемента стека;

  • b.size () - возвратить количество элементов в стеке;

  • b.empty () - true, если стек пуст, иначе — false;

Пример работы со стеком:

1

2

3

4

5

6

7

8

9

10

11

stack < int > st;             // создание стека типа integer в c++

st.push (1);                  // [1]

st.push (2);                  // [1][2]

st.push (3);                  // [1][2][3]

cout << st.top () << "\n";    // [1][2]->[3]<-, выводим "3"

st.push (4);                  // [1][2][3][4]

cout << st.top () << "\n";    // [1][2][3]->[4]<-, выводим "4"

st.pop ();                    // [1][2][3]

st.pop ();                    // [1][2]

st.pop ();                    // [1]

cout << st.top () << "\n";    // ->[1]<-, выводим "1"

Но ведь так не интересно, давайте напишем свой стек, хотя бы просто для int-ов. Сразу выкладываю код, объяснения как обычно там же:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

# include <iostream>

# include <cstdlib>

# include <cstdio>

 

using namespace std;

 

int st[1000]; // наш будущий стек

int st_size; // размер стека, изначально положим равным нулю

 

void push (int x)

{

    st[++st_size] = x; // увеличиваем размер стека и сразу записываем в него наш элемент

}

 

void pop ()

{

    st[st_size--] = 0; // обнуляем верхний элемент и уменьшаем размер стека

}

 

int top ()

{

    return st[st_size]; // возвращаем значение самого верхнего элемента

}

 

int size ()

{

    return st_size; // возвращаем размер стека

}

 

bool empty ()

{

    if (st_size >= 1) // проверяем, есть ли в стеке хоть 1 элемент

        return true;

 

    return false; // иначе возвращаем false

}

 

int main ()

{

    push (1);                  // [1]

    push (2);                  // [1][2]

    push (3);                  // [1][2][3]

 

    cout << top () << "\n";    // [1][2]->[3]<-, выводим "3"

 

    push (4);                  // [1][2][3][4]

 

    cout << top () << "\n";    // [1][2][3]->[4]<-, выводим "4"

 

    pop ();                    // [1][2][3]

    pop ();                    // [1][2]

    pop ();                    // [1]

 

    cout << top () << "\n";    // ->[1]<-, выводим "1"

 

    system ("pause");

 

    return 0;

}

Теперь, разобравшись с теорией, поговорим об использовании стека.

Вы когда-нибудь слышали о обратной польской нотации (далее ОПН)? Если да, то наверняка слышали и остековой машине, используемой для решения ОПН.

В простых словах, ОПН это — вид записи арифметических выражений, не использующий скобок для указания порядка действий. Вместо скобок, для указания порядка действий, используется стек. Также в нем знаки стоят после самих чисел (т.е «2 + 2″ в простой записи это — «2 2 +» в ОПН).

Для полного понимания пример:

X3 * (2xy — 36) тоже самое, что и X X X * * 2 X Y * * 36 — *

Любой число (в нашем случае X или Y) значит добавление в стек этого числа

Любой знак (арифметический или бинарный, о которых подробнее писалось тут) значит произвести операцию, связанную с этим знаком, над двумя верхними числами в стеке, и заменить эти два числа результатом. Для лучшего понимания разбор нашего примера (X X X * * 2 X Y * * 36 — *):

  • [стек до операции] -> [после]

  • 1) [] -> [X]

  • 2) [X] -> [X,X]

  • 3) [X,X] -> [X,X,X]

  • 4) [X,X,X] -> [X,X2]

  • 5) [X,X2] -> [X3]

  • 6) [X3] -> [X3,2]

  • 7) [X3,2] -> [X3,2,X]

  • 8) [X3,2,X] -> [X3,2,X,Y]

  • 9) [X3,2,X,Y] -> [X3,2,XY]

  • 10) [X3,2,XY] -> [X3,2XY]

  • 11) [X3,2XY] -> [X3,2XY,36]

  • 12) [X3,2XY,36] -> [X3,2XY-36]

  • 13) [X3,2XY-36] -> [X3*(2XY-36)]

В качестве закрепления темы, советую написать стековую машину реализующую решение ОПН. Спасибо за внимание, удачи в начинаниях.

Z-функция строки. Число вхождений подстроки в строку.

Опубликовано 31.01.2013 автором admin

Доброго времени суток всем   Тема сегодняшней статьи будет о том, как найти число вхождений подстроки в строку. Ну, а в частности, делать мы это будем с помощью нахождения z-функции строки. 

Конечно существует очень много алгоритмов, которые могут помочь нам в этом, но сегодня мы рассмотрим именно этот.

Итак, для начала определимся что такое z-функция. 

Допустим, мы имеет строку и массив z. В каждой i-той ячейке массива мы будем хранить максимальное количество символов (число), которые полностью совпадают с символами находящимися вначале строки. Для более понятного объяснения, вот Вам наглядное пособие).

 

ОБЪЯСНЕНИЕ

 

Как Вы видите, здесь мы имеем строку abacaba. Теперь разберем массив. В первой ячейке мы пишем 0, потому что для нее не нужно находить какое-либо значение, так как мы уже знаем что оно будет максимально.

Далее, мы имеем символ ‘b’, который не совпадает даже с первой буквой строки, и поэтому пишем ’0′. Третьей буквой (соответственно в массиве 2 индекс), мы видим букву ‘a’, и так как она совпадает с первой буквой строки увеличиваем значение на 1. Дальше идет ‘c’, и в 3 ячейке массива мы пишем ноль.

Следующая буква снова ‘a’, и мы увеличиваем значение на 1. Сразу за ней мы видим ‘b’, и соответственно УЖЕ ДВА СИМВОЛА совпадают с начальными двумя. Увеличиваем 4 ячейку еще на 1. За ней идет снова ‘a’, и мы снова видим что она совпадает со следующей буквой. Также увеличиваем на 1. Вообщем выходит 3. И так далее.

КОД АЛГОРИТМА

Теперь рассмотрим саму функцию, реализованную на С++, которая работает за асимптотику О(n*n), то есть квадрат от длины строки.

Это очень простой код:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

# include <iostream>

 

using namespace std;

 

int zf[100];

 

int main()

{

    string s;

    cin>>s;

 

    for(int i=1; i<s.size(); ++i)

    {

            while(s[zf[i]]==s[zf[i]+i])

            zf[i]++;

    }

 

    for(int i=0; i<s.size(); ++i)

    cout<<zf[i];

 

    system("pause");

}

Здесь мы для каждой ячейки сравниваем символы и увеличиваем ответ для zf[i], пока не найдем НЕсовпадение или не дойдем до конца строки.

Этот алгоритм работает не так быстро, так что теперь рассмотрим наиболее быстрый метод решения этой задачи.

КОД ЭФФЕКТИВНОГО НАХОЖДЕНИЯ Z-ФУНКЦИИ

В этом алгоритме, мы будем максимально использовать информацию, которую уже нашли до того, как дошли до i-той ячейки.

Этот код уже немного побольше, но все заключается в том, что если мы ищем ответ для какой-то подстроки, которая меньше той, для которой мы искали ответ раньше, мы можем воспользоваться информацией, которую получили заранее.

Вот сам код:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

# include <iostream>

 

using namespace std;

 

int zf[100];

 

int main()

{

    string s;

    cin>>s;

 

    //Здесь мы будем хранить

    //координаты самого длинного

    //отрезка, который совпадает с

    //началом нашей строки

    int r=-1,l=-1;

 

    //Запускаем цикл для прохождения по строке

    for(int i=1; i<s.size(); ++i)

    {

            //Проверяем, если ячейка для

            //которой мы ищем ответ

            //находится в отрезке между

            //l и r

            if(i<=r)

            {

                    /*

                    Это означает, что у нас уже есть

                    такая же ячейка для которой мы искали ответ

                    */

                    zf[i]=min(r-i+1,zf[i-l]);

            }

            //Теперь пользуемся тривиальным алгоритмом

            while(s[zf[i]]==s[zf[i]+i])

            zf[i]++;

            //И обновляем границы нашей подстроки

            if(i+z[i]-1>r)

            {

                          l=i;

                          r=i+z[i]-1;

            }

    }

 

    //Выводим ответ на экран

    for(int i=0; i<s.size(); ++i)

    cout<<zf[i];

 

    system("pause");

}

Ну что ж, теперь подумаем, как можно использовать алгоритм нахождения z-функции для нахождения количества вхождения подстроки?

Для этого, нам стоит просто поместить подстроку которую мы ищем вперед, и разделить ее каким-либо знаком. Знаю что не понятно ;))) поэтому покажу как)))

Допустим, есть строка abcqweabcqwe и нам нужно найти в ней строку abc.

Для этого соединим все в одну строчку вот так: abc#abcqweabcqwe

Теперь мы можем просто запустить z-функцию, и она найдет нам такой ответ:

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

 

Ну в общем, в конце у меня вышел такой код: (может кому пригодится   )

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

# include <iostream>

 

using namespace std;

 

int zf[100];

 

int main()

{

    string h,f;

    cin>>h>>f;

    string s=f+"#"+h;

 

    int r=-1,l=-1;

 

    for(int i=1; i<s.size(); ++i)

    {

 

            if(i<=r)

            {

 

                    zf[i]=min(r-i+1,zf[i-l]);

            }

 

            while(s[zf[i]]==s[zf[i]+i])

            zf[i]++;

 

            if(i+zf[i]-1>r)

            {

                          l=i;

                          r=i+zf[i]-1;

            }

    }

 

    cout<<s<<endl;

 

    for(int i=0; i<s.size(); ++i)

    cout<<zf[i];

 

    cout<<endl;

    system("pause");

}

Спасибо за внимание, скоро напишу еще что-нибудь из алгоритмов на С++. Кстати, кому что надо, ну там какие статьи или информация пишите в комменты, как прочту так напишу статейку.

Быстрое возведение числа в степень на С++

Опубликовано 29.01.2013 автором admin

Всем привет! Очередная статья по алгоритмам языка С++, и сегодня мы будем учиться писать функцию, которая возводит число в степень за log(степень).

Если вы еще не читали что такое функция на С++ или как писать программы на С++, то советуем Вам посетить эти статьи(ну по возможности и прочитать   )

Функции в С++

Что такое С++

Итак, начнем нашу лекцию). Для начала хочу рассказать о простых математических формулах, который будут использованы в нашей функции для быстрого возведения числа в степень.

Как Вы знаете, если у нас есть число A в степени X, то есть AX, мы можем написать это как (Az)y, где z*y=x.

Соответственно, если z будет просто 2, нам потребуется лишь возвести число в квадрат, а y поделить на 2.

Но также стоит учитывать такой случай, когда Х может быть нечетным числом. Тогда нам нужно просто возвести А в квадрат, и после чего продолжать наши действия деления X (Так как С++ при делении int`a будет оставлять только целую часть).

Теперь когда принцип работы алгоритма нам известен, осталось показать Вам код и все встанет на свои места 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# include <iostream>

 

using namespace std;

 

int bin_pow(int a, int b)

{

    int res = 1;

    while(b)

    {

              if(b % 2 == 1)

              {

                   res *=a;

              }

              b/=2;

              a*=a;

    }

    return res;

}

 

int main()

{

   int a,b;

   cout<<bin_pow(a,b);

 

   system("pause");

}

Теперь немного разберем код. У нас есть функция под названием bin_pow, которая принимает два параметра, переменные А и B.

После этого, мы проверяем условие того, что B нечетное, и если оно выполнено, возводим ответ в квадрат.

Теперь просто продолжаем наши действия с делением степени и возведением числа А в квадрат.

После того, как B, становится единицей, мы останавливаем цикл и возвращаем ответ.

Надеюсь, алгоритм работы был понятен, спасибо за внимание!

Изменение позиции курсора. С++

Опубликовано 05.12.2012 автором admin

Всем привет! С вами я, Руслан, администратор сообщества. Сегодня хотел рассказать вам о том, как когда-то писал маленькие «программы-приколы» на с++.

Итак, речь пойдет о так сказать бешеном курсоре, а именно о изменении позиции курсора на с++. Для начала хочу сказать, что для того чтобы переместить курсор на с++ нам потребуется библиотека windows.h, которая есть во всех современных компиляторах. Писать мы будем на Dev C++. Ее можно скачать с официального сайта или прямо у нас. Вот тут —> Скачать Bloodshed Dev C++.

Теперь когда вы установили себе эту прекраснейшую программу перейдем непосредственно к делу. Будем писать (ну может кто-то копировать) код 

Сразу скажу, что код программы совсем небольшой, а вот и он:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#include<iostream>

#include<windows.h>

using namespace std;

int main()

{

      int x;

      int y;

      for(int i=0; i<10; ++i)

      {

                 x=600+rand()%800;

                 y=600+rand()%800;

                 SetCursorPos(x,y);

                 cout<<"\a";

                 Sleep(600);

      }

}

Теперь объяснения. Сначала подключаем необходимые библиотеки, а именно iostream и windows.h. После чего создаем две переменные, которые будут отвечать за положение курсора на экране (то есть его координаты). Ну а потом самое интересное 

Пускаем цикл до 10 (но если вы хотите реально напугать юзера, то можно и бесконечный) и каждую итерацию присваиваем переменным координат рандомные значение от 600 до 800 (примерно посередине экрана) и теперь внимание, используем функцию SetCursorPos, для смены позиции курсора на экране.

Регистр букв (большие и маленькие) писать точно такой как тут. Иначе функция не будет работать.

Дальше еще немного пугаем юзера и делаем команду

1

cout<<"\a";

которая будет заставлять процессор (точнее материнскую плату) пикать 

Ну и в конце ставим небольшую паузу на полсекунды, чтобы пользователь (жертва) смог заметить куда делся курсор. И так цикл повторяет 10 раз).

Вот, на сегодня все, думаю вам понравится)

C++. Математические операции. Считывание данных.

Опубликовано 01.12.2012 автором admin

Здравствуйте! Скопилось много не рассказанной информации, но все же ее недостаточно для отдельного урока. Поэтому решил выделить ее просто в отдельный пост.

Предыдущие уроки вы можете найти на этой странице.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]