Datové struktury - IB111 Základy programováníPŘIPOMENUTÍ–DATOVÉTYPY Datovýtyp •...
Transcript of Datové struktury - IB111 Základy programováníPŘIPOMENUTÍ–DATOVÉTYPY Datovýtyp •...
-
DATOVÉ STRUKTURYIB111 ZÁKLADY PROGRAMOVÁNÍ
Nikola Beneš7. listopadu 2019
-
PRÁCE S DATY
• jaká data budu zpracovávat?• jaká data budu potřebovat k řešení problému?• jaké operace s daty budu chtít provádět?• jak rychle budu chtít, aby operace s daty fungovaly?
1
-
POHLED NA DATA – DVA SVĚTY
tabulka
databáze
telefonní seznam
vložit
najít
sečístobarvit na modro
0x1e 0x1f 0x20 0x21
... ...add
mov
loadstore
mul
cops = set()cops.add("Jake Peralta")cops.add("Rosa Diaz")
2
-
PŘIPOMENUTÍ – DATOVÉ TYPY
Datový typ
• rozsah hodnot, které patří do daného typu• operace, které je možno s těmito hodnotami provádět
Abstraktní datový typ (uživatelský pohled na data)
• rozhraní• popis operací, které chceme provádět (případně i složitost)
Konkrétní datová struktura (implementační pohled na data)
• implementace• přesný popis uložení dat v paměti• definice funkcí pro práci s těmito daty
3
-
ABSTRAKTNÍ DATOVÉ TYPY
Nejznámější ADT:
• seznam• zásobník• fronta; prioritní fronta• množina• slovník (asociativní pole)
4
-
DATOVÝ TYP ZÁSOBNÍK
Zásobník
• obsahuje prvky v pořadí LIFO (Last In First Out)• operace
• push (vložení)• pop (odstranění)• top (náhled na horní prvek)• empty (test prázdnosti)
• mnohá použití• procházení grafů• analýza syntaxe• vyhodnocování výrazů• rekurze
push pop
5
-
MOTIVACE PRO ZÁSOBNÍK
• procházení bludiště bez smyček
6
-
ZÁSOBNÍK V PYTHONU
• implementace pomocí seznamu• místo push máme append• místo top máme [-1]
def push(stack, element):stack.append(element)
def pop(stack):return stack.pop()
def top(stack):return stack[-1]
def empty(stack):return len(stack) == 0# more “Pythonic”: return not stack 7
# stack operations
def push(stack, element): stack.append(element)
def pop(stack): return stack.pop()
def top(stack): return stack[-1]
def empty(stack): return len(stack) == 0 # more “Pythonic”: return not stack
def eval_rpn(line): stack = [] for token in line.split(): if token == '*': b = pop(stack) a = pop(stack) push(stack, a * b) elif token == '+': b = pop(stack) a = pop(stack) push(stack, a + b) else: push(stack, int(token)) return top(stack)
-
ZÁSOBNÍK V PYTHONU
Příklad: postfixová notace
• infixová notace• operátory mezi operandy• např. 1 + 2, (3 + 7) * 9• je třeba používat závorky
• prefixová notace (polská notace)• operátory před operandy• např. + 1 2, * + 3 7 9• není třeba závorky
• postfixová notace (reverzní polská notace, RPN)• operátory za operandy• např. 1 2 +, 3 7 + 9 *• není třeba závorky• snadné vyhodnocení pomocí zásobníku
8
-
ZÁSOBNÍK V PYTHONU
Příklad: postfixová notace
def eval_rpn(line):stack = []for token in line.split():
if token == '*':b = pop(stack)a = pop(stack)push(stack, a * b)
elif token == '+':b = pop(stack)a = pop(stack)push(stack, a + b)
else:push(stack, int(token))
return top(stack) 9
# stack operations
def push(stack, element): stack.append(element)
def pop(stack): return stack.pop()
def top(stack): return stack[-1]
def empty(stack): return len(stack) == 0 # more “Pythonic”: return not stack
def eval_rpn(line): stack = [] for token in line.split(): if token == '*': b = pop(stack) a = pop(stack) push(stack, a * b) elif token == '+': b = pop(stack) a = pop(stack) push(stack, a + b) else: push(stack, int(token)) return top(stack)
-
ZÁSOBNÍK V PYTHONU
Příklad vyhodnocení postfixové notace: 7 4 7 + * 8 +
vstup akce zásobník
7 4 7 + * 8 + push4 7 + * 8 + push 77 + * 8 + push 7 4+ * 8 + + 7 4 7* 8 + * 7 118 + push 77+ + 77 8
85
10
-
PŘÍKLAD: MRAKODRAPY
• ze kterých střech je výhled na jezero? (vpravo)
11
-
PŘÍKLAD: MRAKODRAPY
def clear_view(heights: List[int]) -> List[int]:stack: List[int] = []for current in range(len(heights)):
while len(stack) > 0 and \heights[stack[-1]] < heights[current]:
stack.pop()stack.append(current)
return stack
K zamyšlení pro pokročilé
• je tento algoritmus korektní?• jakou má časovou složitost?
12
from typing import List
def clear_view(heights: List[int]) -> List[int]: stack: List[int] = [] for current in range(len(heights)): while len(stack) > 0 and \ heights[stack[-1]] < heights[current]: stack.pop() stack.append(current)
return stack
-
DATOVÝ TYP FRONTA
Fronta
• obsahuje prvky v pořadí FIFO (First In First Out)• operace
• enqueue (vložení)• dequeue (odstranění)• front (náhled na přední prvek)• empty (test prázdnosti)
• použití• zpracovávání příchozích požadavků
13
-
FRONTA V PYTHONU
• implementace pomocí seznamů by byla pomalá• přidávání a odebírání na začátek seznamu vyžaduje přesun
• použití knihovny collections• datový typ deque (oboustranná fronta)• vložení do fronty pomocí append• odebrání z fronty pomocí popleft• přední prvek fronty je [0]
from collections import deque
q = deque(["Eric", "John", "Michael"])q.append("Terry") # Terry arrivesq.append("Graham") # Graham arrivesq.popleft() # Eric leavesq.popleft() # John leavesprint(q)
14
from collections import deque
q = deque(["Eric", "John", "Michael"])q.append("Terry") # Terry arrivesq.append("Graham") # Graham arrivesq.popleft() # Eric leavesq.popleft() # John leavesprint(q)
-
DATOVÝ TYP MNOŽINA
Množina
• neuspořádaná kolekce dat bez vícenásobných prvků• operace
• insert (vložení)• find (vyhledání prvku, test přítomnosti)• delete (odstranění)
• použití• grafové algoritmy (označení navštívených vrcholů)• rychlé vyhledávání• výpis unikátních slov
15
-
MOTIVACE PRO MNOŽINU
• procházení bludiště se smyčkami
16
-
MNOŽINA V PYTHONU
• speciální datový typ set
set(l) # vytvoří množinu ze seznamulen(s) # počet prvků množiny ss.add(x) # přidání prvku do množinys.remove(x) # odebrání prvku z množinyx in s # obsahuje množina x?s1
-
MNOŽINA V PYTHONU
basket = ['apple', 'orange','apple', 'orange', 'banana']
fruit = set(basket)print(fruit)print('orange' in fruit)print('tomato' in fruit)
a = set("abracadabra")b = set("engineering")print(a)print(b)print(a | b)print(a & b)print(a - b)print(a ^ b)
18
basket = ['apple', 'orange', 'apple', 'orange', 'banana']fruit = set(basket)print(fruit)print('orange' in fruit)print('tomato' in fruit)
a = set("abracadabra")b = set("engineering")print(a)print(b)print(a | b)print(a & b)print(a - b)print(a ^ b)
-
PŘÍKLAD POUŽITÍ MNOŽIN
• unikátní prvky ze seznamu
def unique_elements(my_list):return list(set(my_list))
• co kdybychom chtěli zachovat jejich pořadí?
def unique_elements2(my_list):seen = set()result = []for elem in my_list:
if elem not in seen:result.append(elem)seen.add(elem)
return result
• v čem by byl rozdíl, kdyby byl seen seznam?19
def unique_elements(my_list): return list(set(my_list))
def unique_elements2(my_list): seen = set() result = [] for elem in my_list: if elem not in seen: result.append(elem) seen.add(elem) return result
-
DATOVÝ TYP SLOVNÍK
Slovník (dictionary, map, asociativní pole)
• neuspořádaná množina dvojic (klíč, hodnota)• klíče jsou unikátní• operace jako u množiny (insert, find, delete)• navíc přístup k hodnotě pomocí klíče• klíče jsou neměnné, ale hodnoty se smí měnit• použití
• překlad UČO na jméno, jméno na tel. číslo apod.• počet výskytů slov v textu• „cache“ výsledků náročných výpočtů
20
-
SLOVNÍK V PYTHONU
• zápis do složených závorek {}• klíč a hodnotu oddělujeme dvojtečkou• záznamy oddělujeme čárkami
phone = {"Buffy": 5550101, "Xander": 5550168}phone["Dawn"] = 5550193
print(phone)print(phone["Xander"])
del phone["Buffy"]
print(phone)print("Dawn" in phone)
21
phone = {"Buffy": 5550101, "Xander": 5550168}phone["Dawn"] = 5550193
print(phone)print(phone["Xander"])
del phone["Buffy"]
print(phone)print("Dawn" in phone)
for name in phone: print(name + "'s number is", phone[name])
for name, num in phone.items(): print(name + "'s number is", num)
-
OPERACE SE SLOVNÍKEM V PYTHONU
• vytvoření nebo změna položky: d[key] = value• čtení hodnoty položky
d[key] # pokud klíč není, chybad.get(key, default) # pokud klíč není, vrátí defaultd.get(key) # pokud klíč není, vrátí None
• hledání: key in dictionary (vrací True/False)
• procházení slovníku – jen klíče:
for name in phone:print(name + "'s number is", phone[name])
• procházení celých položek slovníku – .items()
for name, num in phone.items():print(name + "'s number is", num) 22
phone = {"Buffy": 5550101, "Xander": 5550168}phone["Dawn"] = 5550193
print(phone)print(phone["Xander"])
del phone["Buffy"]
print(phone)print("Dawn" in phone)
for name in phone: print(name + "'s number is", phone[name])
for name, num in phone.items(): print(name + "'s number is", num)
-
SLOVNÍK V PYTHONU – PŘÍKLADY POUŽITÍ I
Frekvence slov
def word_freq(text):# first, eliminate all interpunctiontext = "".join(char for char in text
if char.isalpha() or char.isspace())word_list = text.lower().split()freq = {}for word in word_list:
freq[word] = freq.get(word, 0) + 1return freq
• jaký je význam předposledního řádku?• pokud ve slovníku freq není položka s klíčem word,vytvoří se s hodnotou 1
• v opačném případě se k hodnotě položky s klíčem word přičte 123
def word_freq(text): # first, eliminate all interpunction text = "".join(char for char in text if char.isalpha() or char.isspace()) word_list = text.lower().split() freq = {} for word in word_list: freq[word] = freq.get(word, 0) + 1 return freq
def output_word_freq(text): freq = word_freq(text) my_list = [(freq[word], word) for word in freq] my_list.sort(reverse=True) print("Word frequencies, " "sorted from the most frequent:") for count, word in my_list: print(count, word)
def output_word_freq_alt(text): freq = sorted(word_freq(text).items(), key=lambda x: (x[1], x[0]), reverse=True) print("Word frequencies, " "sorted from the most frequent:") for word, count in freq: print(count, word)
laf = """I must not fear.Fear is the mind-killer.Fear is the little-death that brings total obliteration.I will face my fear.I will permit it to pass over me and through me.And when it has gone past I will turn the inner eye to see its path.Where the fear has gone there will be nothing.Only I will remain."""
output_word_freq(laf)
-
SLOVNÍK V PYTHONU – PŘÍKLADY POUŽITÍ I
Frekvence slov – výpis frekvencí
def output_word_freq(text):freq = word_freq(text)my_list = [(freq[word], word) for word in freq]my_list.sort(reverse=True)print("Word frequencies, "
"sorted from the most frequent:")for count, word in my_list:
print(count, word)
24
def word_freq(text): # first, eliminate all interpunction text = "".join(char for char in text if char.isalpha() or char.isspace()) word_list = text.lower().split() freq = {} for word in word_list: freq[word] = freq.get(word, 0) + 1 return freq
def output_word_freq(text): freq = word_freq(text) my_list = [(freq[word], word) for word in freq] my_list.sort(reverse=True) print("Word frequencies, " "sorted from the most frequent:") for count, word in my_list: print(count, word)
def output_word_freq_alt(text): freq = sorted(word_freq(text).items(), key=lambda x: (x[1], x[0]), reverse=True) print("Word frequencies, " "sorted from the most frequent:") for word, count in freq: print(count, word)
laf = """I must not fear.Fear is the mind-killer.Fear is the little-death that brings total obliteration.I will face my fear.I will permit it to pass over me and through me.And when it has gone past I will turn the inner eye to see its path.Where the fear has gone there will be nothing.Only I will remain."""
output_word_freq(laf)
-
SLOVNÍK V PYTHONU – PŘÍKLADY POUŽITÍ I
Frekvence slov – výpis frekvencí (alternativní řešení pomocí lambda)
def output_word_freq_alt(text):freq = sorted(word_freq(text).items(),
key=lambda x: (x[1], x[0]),reverse=True)
print("Word frequencies, ""sorted from the most frequent:")
for word, count in freq:print(count, word)
25
def word_freq(text): # first, eliminate all interpunction text = "".join(char for char in text if char.isalpha() or char.isspace()) word_list = text.lower().split() freq = {} for word in word_list: freq[word] = freq.get(word, 0) + 1 return freq
def output_word_freq(text): freq = word_freq(text) my_list = [(freq[word], word) for word in freq] my_list.sort(reverse=True) print("Word frequencies, " "sorted from the most frequent:") for count, word in my_list: print(count, word)
def output_word_freq_alt(text): freq = sorted(word_freq(text).items(), key=lambda x: (x[1], x[0]), reverse=True) print("Word frequencies, " "sorted from the most frequent:") for word, count in freq: print(count, word)
laf = """I must not fear.Fear is the mind-killer.Fear is the little-death that brings total obliteration.I will face my fear.I will permit it to pass over me and through me.And when it has gone past I will turn the inner eye to see its path.Where the fear has gone there will be nothing.Only I will remain."""
output_word_freq(laf)
-
SLOVNÍK V PYTHONU – PŘÍKLADY POUŽITÍ II
Hláskovací tabulka
SPELLING = {'A': 'Adam', 'B': 'Bozena', 'C': 'Cyril', ... }
def to_spelling(text):result = ""for char in text:
char = char.upper()if char in SPELLING:
result += SPELLING[char] + " "return result[:-1]
print(to_spelling("Ahoj!"))# Adam Helena Otakar Josef
26
SPELLING = {'A': 'Adam', 'Á': 'Á s čárkou', 'B': 'Božena', 'C': 'Cyril', 'Č': 'Čeněk', 'D': 'David', 'Ď': 'Ďáblice', 'E': 'Emil', 'Ě': 'É s háčkem', 'É': 'É s čárkou', 'F': 'František', 'G': 'Gustav', 'H': 'Helena', 'I': 'Ivan', 'Í': 'Í s čárkou', 'J': 'Josef', 'K': 'Karel', 'L': 'Ludvík', 'M': 'Marie', 'N': 'Norbert', 'Ň': 'Nina', 'O': 'Otakar', 'Ó': 'Ó s čárkou', 'P': 'Petr', 'Q': 'Quido', 'R': 'Rudolf', 'Ř': 'Řehoř', 'S': 'Svatopluk', 'Š': 'Šimon', 'T': 'Tomáš', 'Ť': 'Těšnov', 'U': 'Urban', 'Ů': 'Ú s kroužkem', 'Ú': 'Ú s čárkou', 'V': 'Václav', 'W': 'Dvojité Vé', 'X': 'Xaver', 'Y': 'Ypsilon', 'Ý': 'Ypsilon s čárkou', 'Z': 'Zuzana', 'Ž': 'Žofie'}
def to_spelling(text): result = "" for char in text: char = char.upper() if char in SPELLING: result += SPELLING[char] + " " return result[:-1]
print(to_spelling("Ahoj!"))# Adam Helena Otakar Josef
-
SLOVNÍK V PYTHONU – PŘÍKLADY POUŽITÍ III
Substituční šifra
def encrypt(text, subst):result = ""for char in text:
if char in subst:result += subst[char]
else:result += char
return result
my_cipher = {'A': 'Q', 'B': 'W', 'C': 'E'} # etc.
print(encrypt("ATTACK AT DAWN", my_cipher))# QCCQEF QC PQUN
27
def encrypt(text, subst): result = "" for char in text: if char in subst: result += subst[char] else: result += char return result
my_cipher = {'A': 'Q', 'B': 'W', 'C': 'E', 'D': 'P', 'E': 'A', 'F': 'K', 'G': 'G', 'H': 'M', 'I': 'D', 'J': 'S', 'K': 'F', 'L': 'H', 'M': 'O', 'N': 'N', 'O': 'B', 'P': 'J', 'Q': 'L', 'R': 'Y', 'S': 'X', 'T': 'C', 'U': 'V', 'V': 'I', 'W': 'U', 'X': 'R', 'Y': 'Z', 'Z': 'T'}
print(encrypt("ATTACK AT DAWN", my_cipher))# QCCQEF QC PQUN
-
IMPLEMENTACE MNOŽIN A SLOVNÍKŮ
• cíl: rychlé vyhledávání / vkládání / mazání prvků
Obecné
• stromové struktury• typicky tzv. vyhledávací stromy• logaritmická složitost operací• jiné možnosti (trie, …)
• hashovací tabulky• v nejhorším případě až lineární složitost• ale očekávaná složitost je konstantní• reálná efektivita záleží na řadě vlivů• tohle používá Python pro set a dict
Specifické (máme-li nějaké informace o rozložení dat)
• seznam, pole (klíče jsou indexy), …28
-
POZNÁMKA K EFEKTIVITĚ
x in data
• pokud je data seznam, lineární složitost (průchod přes všechnyprvky)
• pokud je data množina (set) nebo slovník (dict)• očekávaná složitost je konstantní• máme-li dat víc, je to typicky mnohem rychlejší než hledánív seznamu
K zamyšlení – co je špatně?
def lookup(phonebook, person_name):for name, phone in phonebook.items():
if name == person_name:return phone
return None29
# Warning: this code is really bad. Don't write things like this.
def lookup(phonebook, person_name): for name, phone in phonebook.items(): if name == person_name: return phone return None
-
Doing linear scans over an associative array is like trying to clubsomeone to death with a loaded Uzi. (Larry Wall)
30