# WPMII 27 XI
## plan zajęć:
---
## ZŁOŻONOŚĆ OBLICZENIOWA
:::success
Złożoność obliczeniową deniniujemy jako rząd wielkośći liczby operacji wykonywanych przez algorytm
:::
https://www.youtube.com/watch?v=22lK4v6MsWU
---
## ITERACJA VS REKURENCJA
:::success
Procedura lub funkcja jest rekurencyjna, gdy zawiera odołanie do samej siebie.
:::
#### SILNIA
**rekurencyjna** definicja silni:
$$ n! = \begin{cases}
1 & \text{dla }\ n=1 \\ n \cdot (n-1)! & \text{dla}\ n>1
\end{cases}
$$
```cpp=
int silnia_rek(int n)
{
if(n == 0) return 1;
else return n * silnia_rek(n-1);
}
```
**klasyczna (iteracyjna)** definicja silni:
$n! = 1 \cdot 2 \cdot 3 \cdot \ ... \ \cdot (n-1) \cdot n$
```cpp=
int silnia_iter(int n)
{
int wynik = 1;
for(int i = 1; i <= n; i++) {
wynik *= i;
}
return wynik;
}
```
#### SUMA CYFR
```cpp=
int suma_cyfr_rek(int n)
{
if(n == 0) return 0;
else
{
int ostc = n % 10;
int uciety = n / 10;
return ostc + suma_cyfr_rek(uciety);
}
}
```
```cpp=
int suma_cyfr_iter(int n)
{
int w = 0;
while(n > 0) {
int ostc = n % 10;
int uciety = n / 10;
w += ostc;
n = uciety;
}
return w;
}
```
#### ZADANIE MATURLANE (maj 2019)
![](https://i.imgur.com/OpRPX6y.png)
![](https://i.imgur.com/qkUAjFC.png)
![](https://i.imgur.com/KJjLPSK.png)
---
## OPERACJE NA TEKSTACH
#### STRING(napis) czyli tablica CHAR(znaków)
![](https://i.imgur.com/DyOSbch.png)
---
#### ANAGRAM
:::success
Anagram - wyraz utworzony przez przestawienie liter innego wyrazu.
:::
##### Metoda 1
Zliczamy wystąpenia poszczególnych liter w jednym oraz drugim wyrazie i sprawdzamy czy poszczególne loitery miały taką samą liczbe wystąpień.
```cpp=
void zlicz_litery(string wyraz, int tablica[])
{
for(int i=0; i<wyraz.length(); i++)
{
int idx = (int)wyraz[i];
idx -= (int)'a';
tablica[idx] += 1;
}
}
bool czy_anagram(string wyraz1, string wyraz2)
{
int tablica1[26] = {0};
int tablica2[26] = {0};
zlicz_litery(wyraz1, tablica1);
zlicz_litery(wyraz2, tablica2);
for(int i=0; i<26; i++)
{
if(tablica1[i] != tablica2[i])
{
return false;
}
}
return true;
}
int main()
{
if(czy_anagram("hhelouyuw", "wlehoh"))
{
cout << "to sa anagramy ;D";
}
return 0;
}
```
##### Metoda 2
Zauważmy, że aby sprawdzić czy dwa wyrazy są anagramami najłatwiej będzie posortować wyrazy (czy właściwie litery w wyrazach) alfabetycznie i sprawdzić czy dwa powstałe wyraz są takie same.
```cpp=
#include <algorithm>
...
bool czy_anagram(string wyraz1, string wyraz2)
{
sort(wyraz1.begin(), wyraz1.end());
sort(wyraz2.begin(), wyraz2.end());
return wyraz1 == wyraz2;
}
```
#### PALINDROM
:::success
Palindrom - wyraz lub zdanie brzmiące tak samo przy czytaniu od lewej strony do prawej, jak i odwrotnie, np. kajak.
:::
```cpp=
bool palindrom(string wyraz)
{
int n = wyraz.length();
for (int i=0; i<n/2; i++)
{
if (wyraz[i] != wyraz[n-i-1])
{
return false;
}
}
return true;
}
```
#### ZADANIE MATURALNE (maj 2018)
zadanie 4 -- WEGA
---
## KONWERSJE SYSTEMOW LICZBOWYCH
https://towarzystwo.edu.pl/assets/prace_matematyczne/sm_14_kaczowka_makowski_rajs_szlubowski.pdf
## SORTOWANIE
:::danger
**UWAGA:** Wszystkie poniższe opisy i implementacje algorytmów przyjmują założenie, że tablicę chcemy posortować rosnąco.
:::
### Sortowanie bąbelkowe
- Dzielimy tablicę na część nieposortowaną i posortowaną (która na początku jest pusta) - nieposortowana po lewej, posortowana po prawej
- Największy element "wypływa" na wierzch tablicy nieposortowanej jak bąbelek
- Przechodzimy przez całą tablicę nieposortowaną i zamieniamy miejscami dwa sąsiadujące elementy, jeśli nie są ustawione w docelowej kolejności
- Powtarzamy dopóki cała tablica nie jest posortowana
- Jeśli w trakcie pojedynczej iteracji ani razu nie zamienimy dwóch elementów miejscami, to możemy zakończyć algorytm
- Złożoność:
- Optymistyczna: $O(n)$ dla tablicy wejściowej posortowanej rosnąco
- Pesymistyczna: $O(n^2)$ dla tablicy wejściowej posortowanej malejąco
#### C++
```cpp=
void bubbleSort(int arr[], int size) {
for (int i = 1; i < size; i++) {
bool swapped = false;
for (int j = 0; j < size - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
swapped = true;
}
}
if (!swapped) {
return;
}
}
}
```
#### Python
```python=
def bubbleSort(arr, size):
for i in range(1, size):
swapped = False
for j in range(size - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
return
```
### Sortowanie przez wybór
- Dzielimy tablicę na część nieposortowaną i posortowaną (która na początku jest pusta) - nieposortowana po prawej, posortowana po lewej
- Wybieramy najmniejszy element w części nieposortowanej i zamieniamy go miejscami z pierwszym elementem tej części
- Po każdej iteracji pętli zewnętrznej tablica posortowana powiększa sie o jeden element
- Złożoność: $O(n^2)$ - mamy dwie pętle przchodzące tablicę, algorytmu nigdy nie kończymy wcześniej niż po zakończeniu wszystkich iteracji
#### C++
```cpp=
void selectSort(int arr[], int size) {
for (int i = 0; i < size; i++) {
int minIndex = i;
for (int j = i; j < size; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr[i], arr[minIndex]);
}
}
```
#### Python
```python=
def selectSort(arr, size):
for i in range(size):
minIndex = i
for j in range(i, size):
if arr[j] < arr[minIndex]:
minIndex = j
arr[i], arr[minIndex] = arr[minIndex], arr[i]
```
### Sortowanie przez wstawianie
- Dzielimy tablicę na część nieposortowaną i posortowaną (która na początku jest pusta) - nieposortowana po prawej, posortowana po lewej
- Bierzemy pierwszy element z części posortowanej i wstawiamy go w odpowiednie miejsce w części posortowanej - tak żeby pozostała posortowana
- Po każdej iteracji pętli zewnętrznej tablica posortowana powiększa sie o jeden element
- Złożoność:
- Optymistyczna: $O(n)$ dla tablicy wejściowej posortowanej rosnąco - każda wewnętrzna pętla wykona się tylko raz
- Pesymistyczna: $O(n^2)$ dla tablicy wejściowej posortowanej malejąco - z każdym elementem będziemy musieli przejść przez całą tablicę posortowaną
#### C++
```cpp=
void insertSort(int arr[], int size) {
for (int i = 0; i < size; i++) {
int j = i - 1;
while (j >= 0) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
j -= 1;
}
}
}
```
#### Python
```python=
def insertSort(arr, size):
for i in range(size):
j = i - 1
while j >= 0:
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
j -= 1
```
### Sortowanie przez scalanie
- Stosujemy strategię _dziel i zwyciężaj_
- Dzielimy tablicę na dwie części, które niezależnie sortujemy (też przez scalanie), po czym scalamy posortowane części w jedną
- Podczas scalania iterujemy się niezależnie po dwóch posortowanych tablicach. Wybieramy mniejszy z elementów, które aktualnie sprawdzamy, przepisujemy go na kolejne miejsce w tablicy wynikowej i przechodzimy do kolejnego elementu w tablicy, z której go wybraliśmy. Jeśli z jednej z tablic przepiszemy wszystkie elementy - wybieramy element z drugiej.
- Złożoność: $O(n log\ n)$ - mamy $log\ n$ poziomów rekurencji (bo dzielimy na dwa) i w każdym z nich łącznie $n$ elementów do scalenia. Scalanie ma złożoność liniową.
#### C++
```cpp=
void merge(int arr[], int l, int m, int r) {
int lSize = m - l + 1;
int rSize = r - m;
int lArr[lSize];
int rArr[rSize];
for (int i = 0; i < lSize; i++) {
lArr[i] = arr[l + i];
}
for (int i = 0; i < rSize; i++) {
rArr[i] = arr[m + 1 + i];
}
int lIndex = 0;
int rIndex = 0;
for (int i = 0; i < lSize + rSize; i++) {
if (rIndex >= rSize || (lIndex < lSize && lArr[lIndex] < rArr[rIndex])) {
arr[l + i] = lArr[lIndex];
lIndex += 1;
} else {
arr[l + i] = rArr[rIndex];
rIndex += 1;
}
}
}
void mergeSort(int arr[], int l, int r) {
if (l >= r) {
return;
}
int m = (l + r) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
```
#### Python
```python=
def merge(arr, l, m, r):
lSize = m - l + 1
rSize = r - m
# Fajna, krótka implementacja przepisywania części tablicy
# do nowej z użyciem tablic składanych.
# Robi to samo, co linijki 11-18.
# lArr = [arr[i] for i in range(l, l + lSize)]
# rArr = [arr[i] for i in range(m + 1, m + 1 + rSize)]
lArr = []
rArr = []
for i in range(lSize):
lArr.append(arr[l + i])
for i in range(rSize):
rArr.append(arr[m + 1 + i])
lIndex = 0
rIndex = 0
for i in range(lSize + rSize):
if rIndex >= rSize or (lIndex < lSize and lArr[lIndex] < rArr[rIndex]):
arr[l + i] = lArr[lIndex]
lIndex += 1
else:
arr[l + i] = rArr[rIndex]
rIndex += 1
def mergeSort(arr, l, r):
if l >= r:
return
m = floor((l + r) / 2)
mergeSort(arr, l, m)
mergeSort(arr, m + 1, r)
merge(arr, l, m, r)
```
<p style="font-size:4px;"> Robert Lewandowski to giga kox </p>