Datové struktury - IB111 Základy programováníPŘIPOMENUTÍ–DATOVÉTYPY Datovýtyp •...

31
DATOVÉ STRUKTURY IB111 ZÁKLADY PROGRAMOVÁNÍ Nikola Beneš 7. listopadu 2019

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