Basic Data Structures

Python Algorithm Study - Week 3, 4

Algorithmpython

2017-08-03


Problem Solving with Algorithms and Data Structures using Python ์ฑ…์„ ์ฝ์œผ๋ฉฐ ์Šคํ„ฐ๋””ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์—ฌ ์—ฐ์žฌํ•ฉ๋‹ˆ๋‹ค.


1. Stack

  1. Last In, First Out: insertion and deletion of items takes place only at a single end called top of the stack
  2. Stacks are fundamentally important, as they can be used to reverse the order of items.
  3. e.g. browser history

  1. Queue
  2. Deque

4. List

1. O(1) vs O(n)

  • ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Stack ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๊ฒฝ์šฐ, pop() ์ด๋‚˜ append()๋Š” O(1)์ด๋‹ค. ์ž๋ฆฌ์— ์ƒ๊ด€ ์—†์ด ๋งจ ๋งˆ์ง€๋ง‰์— ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ.
  • ๊ทธ๋Ÿฌ๋‚˜ pop(0), insert(0, x) ๋Š” O(n)์ด๋‹ค. ์•ž์—์„œ๋ถ€ํ„ฐ ์ž๋ฆฌ์ˆ˜๊ฐ€ ๋‹ค ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ.
  • ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ฐ™๋”๋ผ๋„, ๋ฒค์น˜๋งˆํฌ ํ…Œ์ŠคํŒ… ์„ ํ•˜๋ฉด ์†๋„๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. (even though the implementations are logically equivalent, they would have very different timings when performing benchmark testing)

2. Linked Lists

  1. Linked list ๋ž€? (Wikipedia)

    • Linear ํ•œ ๋ฐ์ดํ„ฐ์˜ ๋ชจ์Œ. ์ธ๋ฑ์Šค(?)๊ฐ€ ๋ถ€์—ฌ๋˜์–ด ์žˆ์ง€ ์•Š์Œ (๊ทธ๋งŒํผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์•ˆ๋จน์Œ)

      ํŒŒ์ด์ฌ์˜ ๋ฆฌ์ŠคํŠธ๋Š” ์ธ๋ฑ์Šค๊ฐ€ ๋ถ€์—ฌ๋˜์–ด ์žˆ๋‹ค! ๊ทธ๋ž˜์„œ ์ค‘๊ฐ„์— insert ํ•˜๋Š”๊ฑด O(n)

    • ๋Œ€์‹  pointer ๋ผ๊ณ  ํ•˜๋Š” ์• ๊ฐ€ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ์Œ
    • ๋ฌผ๋ก  ์ฒซ ๋ฐ์ดํ„ฐ๋Š” ์ฒ˜์Œ์ด๋ผ๋Š” ํ‘œ์‹œ๊ฐ€ ์žˆ๊ณ , ๋งˆ์ง€๋ง‰ ๋ฐ์ดํ„ฐ๋Š” ํฌ์ธํ„ฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š์Œ
    • ๊ฐ๊ฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ node ๋ผ๊ณ  ๋ถ€๋ฆ„. ์ด node ๊ฐ€ ๋ชจ์ธ ๊ฒƒ์ด sequence. ์ฆ‰, ๊ฐ๊ฐ์˜ node ๋Š” ๋ฐ์ดํ„ฐ์™€ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” ์…ˆ
  2. Linked list ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ด์œ 

    • ๋ฆฌ์ŠคํŠธ ์ค‘๊ฐ„์— ์•„์ดํ…œ์„ ๋„ฃ๊ฑฐ๋‚˜ ๋บ„ ์ˆ˜ ์žˆ๋‹ค.
    • ์Šคํƒ๊ณผ ํ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
    • ์ฒ˜์Œ์— ์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  3. Linked list ์˜ ๋‹จ์ 

    • pointer ๋•Œ๋ฌธ์— array ๋ณด๋‹ค ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋” ๋จน๋Š”๋‹ค.
    • sequence ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ์•„์ดํ…œ์„ ์ฐพ์œผ๋ ค๋ฉด ์ง„์ž…์ ๋ถ€ํ„ฐ ํ•˜๋‚˜ ํ•˜๋‚˜ ์ฒดํฌํ•ด์•ผ ํ•œ๋‹ค.

1-1. Unordered List

# Pythonic way to define getter/setter/deleter - @property๋ฅผ ์“ฐ์„ธ์š”!

class Node:
    def __init__(self, initdata):
        self.data = initdata
        self.next = None

        # Note in the constructor that a node is initially created with next set to None. Since this is sometimes referred to as โ€œgrounding the node,โ€ we will use the standard ground symbol to denote a reference that is referring to None. It is always a good idea to explicitly assign None to your initial next reference values.

    def getData(self):
        return self.data

    def getNext(self):
        return self.next

    def setData(self,newdata):
        self.data = newdata

    def setNext(self,newnext):
        self.next = newnext

class PythonicNode:
    def __init__(self, initdata):
        self.data = initdata
        self.next = None

    @property
    def x(self):
        return self.data

    @x.setter
    def x(self, newdata):
        self.data = newdata

    @x.deleter
    def x(self):
        del self.data
temp = PythonicNode(93)

# getter
temp.x # 93

# setter
temp.x = 1234
temp.x # 1234

# deleter
del temp.x

1-2. The Unordered List Class

class UnorderedList:
    def __init__(self):
        self.head = None

    # ์ด ํด๋ž˜์Šค๋Š” ์ฒซ ๋…ธ๋“œ์— ๋Œ€ํ•œ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค. (... class must maintain a reference to the first node.)
    # ๊ทธ๋Ÿฐ๋ฐ ์ด ์ƒ์„ฑ์ž์— head๊ฐ€ none ์ธ ์ด์œ ๋Š” ๋ง‰ ์ƒ์„ฑ๋œ ๋ฆฌ์ŠคํŠธ๋Š” ์•„๋ฌด ๋…ธ๋“œ๋„ ์—†๊ธฐ ๋•Œ๋ฌธ!

    def isEmpty(self):
        return self.head == None
    # ์ด ๋ฆฌ์ŠคํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€ ๋ณด๋ ค๋ฉด head๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€๋งŒ ๋ณด๋ฉด ๋จ. ๊ทธ๋ž˜์„œ boolean์‹์œผ๋กœ ์•Œ์•„๋ด„

    def add(self, item):
        temp = Node(item)    # temp๋Š” ๋…ธ๋“œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์—ฌ์•ผ ํ•˜๊ณ ,
        temp.setNext(self.head)    # ์ƒˆ๋กœ์šด temp๊ฐ€ ๋“ค์–ด์˜ค๋ฉด, ์›๋ž˜ ์žˆ๋˜ head(None)๋Š” temp์˜ next๊ฐ€ ๋˜๊ณ ,
        self.head = temp    # temp๋Š” ์ƒˆ๋กœ์šด head๊ฐ€ ๋œ๋‹ค! - modify the head of the list to refer to the new node

    # 3,4๋ฒˆ์งธ ๋ผ์ธ์˜ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค!
    # x.next == None ์ด๋ฉด, x ๋Š” ์ด ๋ฆฌ์ŠคํŠธ์˜ ์ฒซ ๋ฐ์ดํ„ฐ

    def size(self):
        current = self.head
        count = 0
        while current != None:    # head ๊ฐ€ None์ด ๋‚˜์˜ฌ๋•Œ๊นŒ์ง€, ๋๊นŒ์ง€ ๋ˆ๋‹ค.
            count = count + 1     # while loop๊ฐ€ ๋„๋Š” ํšŸ์ˆ˜๋ฅผ ์„ผ๋‹ค.
            current = current.getNext()

        return count

    def search(self, item):
        current = self.head
        found = False
        while current != None and not found:
            if current.getData() == item:
                found = True
            else:
                current = current.getNext()

        return found

    # head๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์ฐพ์•„๋‚˜๊ฐ„๋‹ค. ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด found๊ฐ€ False๋ฅผ, ์ผ์น˜ํ•˜๋ฉด True๋ฅผ ๋ฑ‰์–ด๋ƒ„.

    def print_all(self):
        current = self.head
        while current != None:
            print(current.getData())
            current = current.getNext()

    def remove(self, item):    # ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์€ ๋…ธ๋“œ์˜ ์•ž,๋’ค ๋งํฌ๋ฅผ ๋Š์–ด์ฃผ๋ฉด ๋œ๋‹ค.
        current = self.head
        previous = None
        found = False
        while not found:
            if current.getData() == item:
                found = True
            else:
                previous = current
                current = current.getNext()

        if previous == None:
            self.head = current.getNext()
        else:
            previous.setNext(current.getNext())

        # ์ˆœ์„œ๋Š” ๋ฐ”๊ฟจ๋Š”๋ฐ, node ์ธ์Šคํ„ด์Šค๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ์ž–์•„? (size๋Š” ๊ทธ๋Œ€๋กœ ์•„๋‹Œ๊ฐ€?)


    # ํ•จ๊ป˜ ํ’€์–ด๋ด…์‹œ๋‹ค!!
    def append(self, item):    # item์ด last๊ฐ€ ๋˜๋ ค๋ฉด? ์ฒ˜์Œ ๋“ค์–ด์˜จ ์•„์ดํ…œ๊นŒ์ง€ ๊ฐ€์„œ ๊ทธ next์— item์„ ๋ถ™์ธ๋‹ค.
        temp = Node(item)
        current = self.head

        while current.getNext() != None:
            current = current.getNext()
        current.setNext(temp)
mylist = UnorderedList()
print(mylist.head)
# None
mylist.add(31)
mylist.add(77)
mylist.add(17)
mylist.add(93)
mylist.add(26)
mylist.add(54)
print(mylist.head)

# <__main__.Node object at 0x1045acf28>
mylist.size()
# 6

mylist.print_all()
# 54
# 26
# 93
# 17
# 77
# 31

mylist.search(17)
# True

mylist.remove(17)
mylist.print_all()
# 54
# 26
# 93
# 77
# 31

mylist.append(14923)
mylist.print_all()
# 54
# 26
# 93
# 77
# 31
# 14923

2-1. Ordered List

OrderedList()

- add(item)
- remove(item)
- search(item)
- isEmpty()
- size()
- append(item)
- index(item)
- insert(pos,item)
- pop()
- pop(pos)

(append, insert ๊ฐ€ ์•ˆ๋จ)

2-2. The Ordered List Class

class OrderedList:
    def __init__(self):
        self.head = None

    def search(self, item):    # ํฌ๊ธฐ์˜ ์ˆœ์„œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋๊นŒ์ง€ ๋Œ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
        current = self.head
        found = False
        stop = False
        while current != None and not found and not stop:
            if current.getData() == item:
                found = True
            else:
                if current.getData() > item:    # ์ฐพ๋Š” ๋ฐ์ดํ„ฐ๋ณด๋‹ค ํฐ๊ณณ๊นŒ์ง€ ์™”๋Š”๋ฐ๋„ ์—†๋‹ค๋ฉด, ์—ฌ๊ธด ์—†๋‹ค๋Š” ๋œป. iteration์„ stop ํ•ด๋„ ๋œ๋‹ค.
                    stop = True
                else:
                    current = current.getNext()

        return found

    def add(self,item):   # unordered๋ž‘ ๋‹ค๋ฅด๊ฒŒ, ์•„๋ฌด๋ฐ๋‚˜ ๋“ค์–ด๊ฐ€์„  ์•ˆ๋˜๊ณ , ํฌ๊ธฐ์— ๋งž๊ฒŒ ๋“ค์–ด๊ฐ€์•ผํ•œ๋‹ค.
        current = self.head
        previous = None
        stop = False
        while current != None and not stop:
            if current.getData() > item:
                stop = True
            else:
                previous = current
                current = current.getNext()

        temp = Node(item)    # ์ธ์Šคํ„ด์Šค ์ƒ์„ฑํ•ด์ฃผ๊ณ ,
        if previous == None:
            temp.setNext(self.head)
            self.head = temp
        else:
            temp.setNext(current)    # item ๋ณด๋‹ค ํฐ current ๋Š” item ๋’ค์— ๋„ฃ์–ด์ฃผ๊ณ 
            previous.setNext(temp)    # item๋ณด๋‹ค ์ž‘์€ previous์˜ next ์— item์„ ๋„ฃ์–ด์ค€๋‹ค.

    def print_all(self):
        current = self.head
        while current != None:
            print(current.getData())
            current = current.getNext()
mylist = OrderedList()
mylist.add(31)
mylist.add(77)
mylist.add(17)
mylist.add(93)
mylist.add(26)
mylist.add(54)

mylist.print_all()
# 17
# 26
# 31
# 54
# 77
# 93