Data Structure

數據結構

  • 數據結構是指相互之間存在着一種或多種關係的數據元素的集合和該集合中數據元素之間的關係組成。
  • 簡單來講,數據結構就是設計數據以何種方式組織並存儲在計算機中。
  • 好比:列表、集合與字典等都是一種數據結構。
  • N.Wirth: 「程序=數據結構+算法」

分類

數據結構按照其邏輯結構可分爲線性結構、樹結構、圖結構node

  • 線性結構:數據結構中的元素存在一對一的相互關係
  • 樹結構:數據結構中的元素存在一對多的相互關係
  • 圖結構:數據結構中的元素存在多對多的相互關係

1、列表/數組

  1. 內容:列表在內存中以一塊連續的空間存放,列表中存放的是每一個元素的引用
  2. 基本操做:
    • 新增:insert O(n),append O(1)
    • 刪除:remove O(n),pop O(1)
    • 修改:O(1)
    • 遍歷:O(n)
    • 查找:O(1)
  3. 列表和數組區別:
    • 數組:元素類型要相同
    • 數組:長度固定
    • 列表:元素類型能夠不同
    • 列表:存的不是真實的值,是地址
  4. 32位機器上,一個整數佔4個字節,一個地址佔4個字節,內存最大4個G

2、棧

  1. 內容:棧(Stack)是一個數據集合,能夠理解爲只能在一端進行插入或刪除操做的列表。
  2. 特色:後進先出(last-in, first-out)
  3. 其餘概念:棧頂 棧底
  4. 基本操做:
    • 進棧:push
    • 出棧:pop
    • 取棧頂元素:gettop
  5. 實現原理:
    • 不須要本身定義,使用列表結構便可。
    • 進棧函數:append
    • 出棧函數:pop
    • 查看棧頂函數:li[-1]
  6. 棧的應用:
    • 括號匹配問題,給一個字符串中,其中包含(,[,{ 判斷該字符串中的括號是否匹配.例 ()()[]{} √,([{()}]) √,[]( X,[(]) X
    • 思路:建立一個空棧,循環字符串,將字符串中左括號放入棧中,若是找到相匹配的右括號,將棧頂的左括號出棧
class Stack:

    def __init__(self):
        self.stack = []

    def push(self, element):
        self.stack.append(element)

    def pop(self):
        return self.stack.pop()

    def get_top(self):
        if len(self.stack) > 0:
            return self.stack[-1]
        else:
            return None

    def is_empty(self):
        return len(self.stack) == 0


def brace_match(s):
    match = {'}':'{', ']':"[", ')':'('}
    stack = Stack()
    for ch in s:
        if ch in {'(','[','{'}:
            stack.push(ch)
        else:   #ch in {'}',']',')'}
            if stack.is_empty():
                return False
            elif stack.get_top() == match[ch]:
                stack.pop()
            else: # stack.get_top() != match[ch]
                return False
    if stack.is_empty():
        return True
    else:
        return False

print(brace_match('[{()}(){()}[]({}){}]'))
print(brace_match('[]}'))
Stack.py

3、隊列

  1. 內容:
    • 隊列(Queue)是一個數據集合,僅容許在列表的一端進行插入,另外一端進行刪除。
    • 進行插入的一端稱爲隊尾(rear),插入動做稱爲進隊或入隊
    • 進行刪除的一端稱爲隊頭(front),刪除動做稱爲出隊
  2. 特色:先進先出(First-in, First-out)
  3. 雙向隊列
    • 隊列的兩端都容許進行進隊和出隊操做。
  4. 實現原理
    • 初步設想:列表+兩個下標指針
    • 實現:建立一個列表和兩個變量,front指向隊首,rear指向隊尾,初始都爲0
    • 進隊操做:元素寫到li[rear]的位置,rear自增1
    • 出隊操做:返回li[front]的位置,front自減1
    • 最終:環形隊列
    • 環形隊列:當隊尾指針front == Maxsize - 1時,再前進一個位置就自動到0。
    • 實現方式:求餘數運算
    • 隊首指針前進1:front = (front + 1) % MaxSize
    • 隊尾指針前進1:rear = (rear + 1) % MaxSize
    • 隊空條件:rear == front 隊滿條件:(rear + 1) % MaxSize == front
  5. 內置模塊
    • 使用方法: from collections import deque
    • 建立隊列: queue = deque()
    • 進隊:append
    • 出隊:popleft
    • 雙向隊列隊首進隊:appendleft
    • 雙向隊列隊尾進隊:pop

 

class Queue:
    def __init__(self, size=100):
        self.queue = [0 for _ in range(size)]
        self.size = size
        self.rear = 0  # 隊尾指針
        self.front = 0 # 隊首指針

    def push(self, element):
        if not self.is_filled():
            self.rear = (self.rear + 1) % self.size
            self.queue[self.rear] = element
        else:
            raise IndexError("Queue is filled.")

    def pop(self):
        if not self.is_empty():
            self.front = (self.front + 1) % self.size
            return self.queue[self.front]
        else:
            raise IndexError("Queue is empty.")

    # 判斷隊空
    def is_empty(self):
        return self.rear == self.front

    # 判斷隊滿
    def is_filled(self):
        return (self.rear + 1) % self.size == self.front


q = Queue(5)
for i in range(4):
    q.push(i)
print(q.pop())
q.push(4)
自定義queue
from collections import deque

# q = deque([1,2,3,4,5], 5)
# q.append(6) # 隊尾進隊
# print(q.popleft()) # 隊首出隊

# 用於雙向隊列
# q.appendleft(1) # 隊首進隊
# q.pop() # 隊尾出隊

def tail(n):
    with open('test.txt', 'r') as f:
        q = deque(f, n)
        return q

for line in tail(5):
    print(line, end='')
內置模塊deque模擬tail

4、鏈表

一、單向鏈表

  1. 內容:
    • 鏈表中每個元素都是一個對象,每一個對象稱爲一個節點,包含有數據域key和指向下一個節點的指針next。經過各個節點之間的相互鏈接,最終串聯成一個鏈表。
  2. 頭插法:
    • node.next = head
    • head = node
  3. 尾插法:
    • tail = head
    • tail.next = node
    • tail = node
  4. 遍歷鏈表:
  5. 鏈表節點的插入:
    • p.next = curNode.next
    • curNode.next = p
  6. 鏈表節點的刪除:
    • p = curNode.next
    • curNode.next = curNode.next.next
    • del p

二、雙鏈表

  1. 內容:
    • 雙鏈表中每一個節點有兩個指針:一個指向後面節點、一個指向前面節點
  2. 雙鏈表節點的插入:
    • p.next = curNode.next
    • curNode.next.prior = p
    • p.prior = curNode
    • curNode.next = p
  3. 雙鏈表節點的刪除:
    • p = curNode.next
    • curNode.next = p.next
    • p.next.prior = curNode
    • del p
  4. 鏈表總結:
    • 插入元素的時間複雜度 O(1)
    • 刪除元素的時間複雜度 O(1)
    • 修改元素的時間複雜度 O(n)
    • 查看元素的時間複雜度 O(n)

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

def create_linklist_head(li):
    head = Node(li[0])
    for element in li[1:]:
        node = Node(element)
        node.next = head
        head = node
    return head

def create_linklist_tail(li):
    head = Node(li[0])
    tail = head
    for element in li[1:]:
        node = Node(element)
        tail.next = node
        tail = node
    return head

def print_linklist(lk):
    while lk:
        print(lk.item, end=',')
        lk = lk.next

lk = create_linklist_tail([1,2,3,6,8])
print_linklist(lk)
Linklist頭插法和尾插法.py
#單鏈表
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None
#雙鏈表
class Node(object):
    def __init__(self, item=None):
        self.item = item
        self.next = None
        self.prior = None
雙鏈表

5、哈希表

  1. 內容:
    • 哈希表(Hash Table,又稱爲散列表),是一種線性表的存儲結構。哈希表由一個順序表(數組)和一個哈希函數組成。哈希函數h(k)將元素k做爲自變量,返回元素的存儲下標。
    • 例如:集合{1,6,7,9},假設哈希函數h(k)使得h(1) = 0,h(6)=2,h(7)=4,h(9)=5,那麼哈希表被存儲爲[1,None,6,None,7,9],當咱們查找元素6所在的位置 時,經過哈希函數得到該元素所在的下標,所以在2的位置能夠找到該元素
    • 集合和字典是基於哈希表來對元素進行查找
  2. 簡單哈希函數:
    • 除法哈希:h(k) = k mod m
    • 乘法哈希:h(k) = floor(m(kA mod 1)) 0<A<1
  3. 哈希衝突:
    • 因爲哈希表的大小是有限的,而要存儲的值的總數量是無限的,所以對於任何哈希函數,都會出現兩個不一樣元素映射到同一個位置上的狀況,這種狀況叫作哈希衝突。 好比h(k)=k%7, h(0)=h(7)=h(14)=...
  4. 解決哈希衝突:開放尋址法
    • 開放尋址法:若是哈希函數返回的位置已經有值,則能夠向後探查新的位置來存儲這個值。
    • 線性探查:若是位置i被佔用,則探查i+1, i+2,……
    • 二次探查:若是位置i被佔用,則探查i+12,i-12,i+22,i-22,……
    • 二度哈希:有n個哈希函數,當使用第1個哈希函數h1發生衝突時,則嘗試使用h2,h3,……
  5. 解決哈希衝突:拉鍊法
    • 拉鍊法:哈希表每一個位置都鏈接一個鏈表,當衝突發生時,衝突的元素將被加到該位置鏈表的最後。
  6. 哈希表在Python中的應用
    • 字典與集合都是經過哈希表來實現的
    • 在Python中的字典: a = {'name': 'ctz', 'age': 18, 'gender': 'Man'}
    • 使用哈希表存儲字典,經過哈希函數將字典的鍵映射爲下標。假設h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,則哈希表存儲爲[None, 18, None, ’ctz’, ‘Man’]
    • 在字典鍵值對數量很少的狀況下,幾乎不會發生哈希衝突,此時查找一個元素的時間複雜度爲O(1)。

class LinkList:
    class Node:
        def __init__(self, item=None):
            self.item = item
            self.next = None

    class LinkListIterator:
        def __init__(self, node):
            self.node = node

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
            else:
                raise StopIteration

        def __iter__(self):
            return self

    def __init__(self, iterable=None):
        self.head = None
        self.tail = None
        if iterable:
            self.extend(iterable)

    def append(self, obj):
        s = LinkList.Node(obj)
        if not self.head:
            self.head = s
            self.tail = s
        else:
            self.tail.next = s
            self.tail = s

    def extend(self, iterable):
        for obj in iterable:
            self.append(obj)

    def find(self, obj):
        for n in self:
            if n == obj:
                return True
        else:
            return False

    def __iter__(self):
        return self.LinkListIterator(self.head)

    def __repr__(self):
        return "<<"+", ".join(map(str, self))+">>"

# 相似於集合的結構
class HashTable:
    def __init__(self, size=101):
        self.size = size
        self.T = [LinkList() for i in range(self.size)]

    def h(self, k):
        return k % self.size

    def insert(self, k):
        i = self.h(k)
        if self.find(k):
            print("Duplicated Insert.")
        else:
            self.T[i].append(k)

    def find(self, k):
        i = self.h(k)
        return self.T[i].find(k)


ht = HashTable()

ht.insert(0)
ht.insert(1)
ht.insert(3)
ht.insert(102)
ht.insert(508)

#print(",".join(map(str, ht.T)))
print(ht.find(203))
hash_table.py

6、樹

  1. 內容:
    • 樹是一種數據結構 好比:目錄結構
    • 樹是一種能夠遞歸定義的數據結構 樹是由n個節點組成的集合:
    • 若是n=0,那這是一棵空樹;
    • 若是n>0,那存在1個節點做爲樹的根節點,其餘節點能夠分爲m個集合,每一個集合自己又是一棵樹。
  2. 一些概念:
    • 根節點
    • 葉子節點
    • 樹的深度(高度)
    • 樹的度
    • 孩子節點/父節點
    • 子樹

一、二叉樹

  • 度不超過2的樹
  • 每一個節點最多有兩個孩子節點
  • 兩個孩子節點被區分爲左孩子節點和右孩子節點

兩種特殊二叉樹

  • 滿二叉樹: 一個二叉樹,若是每個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。
  • 徹底二叉樹: 葉節點只能出如今最下層和次下層,而且最下面一層的結點都集中在該層最左邊的若干位置的二叉樹。

二叉樹的存儲方式:

  • 二叉樹的鏈式存儲:
  • 二叉樹的順序存儲:將二叉樹的節點定義爲一個對象,節點之間經過相似鏈表的連接方式來鏈接。
class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None
View Code
    • 二叉樹的順序存儲方式:(列表)
      • 父節點和左孩子節點的編號下標有什麼關係?
      • 0-1 1-3 2-5 3-7 4-9 =======>i---->2i+1
      • 父節點和右孩子節點的編號下標有什麼關係?
      • 0-2 1-4 2-6 3-8 4-10 =======>i---->2i+2

二叉樹的遍歷:前序遍歷 中序遍歷 後序遍歷 層次遍歷

from collections import deque


class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None

a = BiTreeNode('A')
b = BiTreeNode('B')
c = BiTreeNode('C')
d = BiTreeNode('D')
e = BiTreeNode('E')
f = BiTreeNode('F')
g = BiTreeNode('G')

e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f

root = e

#前序遍歷
def pre_order(root):
    if root:
        print(root.data, end='')
        pre_order(root.lchild)
        pre_order(root.rchild)
#EACBDGF

#中序遍歷
def in_order(root):
    if root:
        in_order(root.lchild)#a
        print(root.data, end='')
        in_order(root.rchild)
#ABCDEGF
#後序遍歷
def post_order(root):
    if root:
        post_order(root.lchild)
        post_order(root.rchild)
        print(root.data, end='')
#BDCAFGE

def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) > 0:
        node = queue.popleft()
        print(node.data,end='')
        if node.lchild:
            queue.append(node.lchild)
        if node.rchild:
            queue.append(node.rchild)
#EAGCFBD


pre_order(root)
print("")
in_order(root)
print("")
post_order(root)
print("")
level_order(root)

'''
EACBDGF
ABCDEGF
BDCAFGE
EAGCFBD
'''
View Code

二叉搜索樹

二叉搜索樹:是一顆二叉樹且知足性質:設x是二叉樹的一個節點。若是y是x左子樹的一個節點,那麼y.key ≤ x.key;若是y是x右子樹的一個節點,那麼y.key ≥ x.key.python

AVL樹:

AVL樹是一棵自平衡的二叉搜索樹。算法

AVL樹具備如下性質:數據庫

  • 根的左右子樹的高度之差的絕對值不能超過1
  • 根的左右子樹都是平衡二叉樹

AVL的實現方式:編程

  • 旋轉

B樹(B-Tree)

B樹是一棵自平衡的多路搜索樹。經常使用於數據庫的索引。數組

class Node:
    def __init__(self, name, type='dir'):
        self.name = name
        self.type = type   #"dir" or "file"
        self.children = []
        self.parent = None
        # 鏈式存儲

    def __repr__(self):
        return self.name


class FileSystemTree:
    def __init__(self):
        self.root = Node("/")
        self.now = self.root

    def mkdir(self, name):
        # name 以 / 結尾
        if name[-1] != "/":
            name += "/"
        node = Node(name)
        self.now.children.append(node)
        node.parent = self.now

    def ls(self):
        return self.now.children

    def cd(self, name):
        # "/var/python/"
        if name[-1] != "/":
            name += "/"
        if name == "../":
            self.now = self.now.parent
            return
        for child in self.now.children:
            if child.name == name:
                self.now = child
                return
        raise ValueError("invalid dir")



tree = FileSystemTree()
tree.mkdir("var/")
tree.mkdir("bin/")
tree.mkdir("usr/")

tree.cd("bin/")
tree.mkdir("python/")

tree.cd("../")

print(tree.ls())
tree.py
from collections import deque

class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None   # 左孩子
        self.rchild = None # 右孩子

a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")

e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f

root = e

def pre_order(root):
    if root:
        print(root.data, end=',')
        pre_order(root.lchild)
        pre_order(root.rchild)

def in_order(root):
    if root:
        in_order(root.lchild)
        print(root.data, end=',')
        in_order(root.rchild)

def post_order(root):
    if root:
        post_order(root.lchild)
        post_order(root.rchild)
        print(root.data, end=',')

def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) > 0: # 只要隊不空
        node = queue.popleft()
        print(node.data, end=',')
        if node.lchild:
            queue.append(node.lchild)
        if node.rchild:
            queue.append(node.rchild)


level_order(root)
bitree.py
import random

class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None   # 左孩子
        self.rchild = None  # 右孩子
        self.parent = None

class BST:
    def __init__(self, li=None):
        self.root = None
        if li:
            for val in li:
                self.insert_no_rec(val)

    def insert(self, node, val):
        if not node:
            node = BiTreeNode(val)
        elif val < node.data:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        elif val > node.data:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    def insert_no_rec(self, val):
        p = self.root
        if not p:               # 空樹
            self.root = BiTreeNode(val)
            return
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:           # 左孩子不存在
                    p.lchild = BiTreeNode(val)
                    p.lchild.parent = p
                    return
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:

                    p.rchild = BiTreeNode(val)
                    p.rchild.parent = p
                    return
            else:
                return

    def query(self, node, val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild, val)
        elif node.data > val:
            return self.query(node.lchild, val)
        else:
            return node

    def query_no_rec(self, val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rchild
            elif p.data > val:
                p = p.lchild
            else:
                return p
        return None

    def pre_order(self, root):
        if root:
            print(root.data, end=',')
            self.pre_order(root.lchild)
            self.pre_order(root.rchild)

    def in_order(self, root):
        if root:
            self.in_order(root.lchild)
            print(root.data, end=',')
            self.in_order(root.rchild)

    def post_order(self, root):
        if root:
            self.post_order(root.lchild)
            self.post_order(root.rchild)
            print(root.data, end=',')


    def __remove_node_1(self, node):
        # 狀況1:node是葉子節點
        if not node.parent:
            self.root = None
        if node == node.parent.lchild:  #node是它父親的左孩子
            node.parent.lchild = None
        else:   #右孩子
            node.parent.rchild = None

    def __remove_node_21(self, node):
        # 狀況2.1:node只有一個左孩子
        if not node.parent: # 根節點
            self.root = node.lchild
            node.lchild.parent = None
        elif node == node.parent.lchild:
            node.parent.lchild = node.lchild
            node.lchild.parent = node.parent
        else:
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    def __remove_node_22(self, node):
        # 狀況2.2:node只有一個右孩子
        if not node.parent:
            self.root = node.rchild
        elif node == node.parent.lchild:
            node.parent.lchild = node.rchild
            node.rchild.parent = node.parent
        else:
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent


    def delete(self, val):
        if self.root:   # 不是空樹
            node = self.query_no_rec(val)
            if not node: # 不存在
                return False
            if not node.lchild and not node.rchild: #1. 葉子節點
                self.__remove_node_1(node)
            elif not node.rchild:       # 2.1 只有一個左孩子
                self.__remove_node_21(node)
            elif not node.lchild:       # 2.2 只有一個右孩子
                self.__remove_node_22(node)
            else:   # 3. 兩個孩子都有
                min_node = node.rchild
                while min_node.lchild:
                    min_node = min_node.lchild
                node.data = min_node.data
                # 刪除min_node
                if min_node.rchild:
                    self.__remove_node_22(min_node)
                else:
                    self.__remove_node_1(min_node)




#
#
# tree = BST([1,4,2,5,3,8,6,9,7])
# tree.in_order(tree.root)
# print("")
#
# tree.delete(4)
# tree.delete(1)
# tree.delete(8)
# tree.in_order(tree.root)
bst.py
from bst import BiTreeNode, BST

class AVLNode(BiTreeNode):
    def __init__(self, data):
        BiTreeNode.__init__(self, data)
        self.bf = 0

class AVLTree(BST):
    def __init__(self, li=None):
        BST.__init__(self, li)

    def rotate_left(self, p, c):
        s2 = c.lchild
        p.rchild = s2
        if s2:
            s2.parent = p

        c.lchild = p
        p.parent = c

        p.bf = 0
        c.bf = 0
        return c

    def rotate_right(self, p, c):
        s2 = c.rchild
        p.lchild = s2
        if s2:
            s2.parent = p

        c.rchild = p
        p.parent = c

        p.bf = 0
        c.bf = 0
        return c

    def rotate_right_left(self, p, c):
        g = c.lchild

        s3 = g.rchild
        c.lchild = s3
        if s3:
            s3.parent = c
        g.rchild = c
        c.parent = g

        s2 = g.lchild
        p.rchild = s2
        if s2:
            s2.parent = p
        g.lchild = p
        p.parent = g

        # 更新bf
        if g.bf > 0:
            p.bf = -1
            c.bf = 0
        elif g.bf < 0:
            p.bf = 0
            c.bf = 1
        else: # 插入的是g
            p.bf = 0
            c.bf = 0
        return g

    def rotate_left_right(self, p, c):
        g = c.rchild

        s2 = g.lchild
        c.rchild = s2
        if s2:
            s2.parent = c
        g.lchild = c
        c.parent = g

        s3 = g.rchild
        p.lchild = s3
        if s3:
            s3.parent = p
        g.rchild = p
        p.parent = g

        # 更新bf
        if g.bf < 0:
            p.bf = 1
            c.bf = 0
        elif g.bf > 0:
            p.bf = 0
            c.bf = -1
        else:
            p.bf = 0
            c.bf = 0
        return g



    def insert_no_rec(self, val):
        # 1. 和BST同樣,插入
        p = self.root
        if not p:  # 空樹
            self.root = AVLNode(val)
            return
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:  # 左孩子不存在
                    p.lchild = AVLNode(val)
                    p.lchild.parent = p
                    node = p.lchild # node 存儲的就是插入的節點
                    break
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:
                    p.rchild = AVLNode(val)
                    p.rchild.parent = p
                    node = p.rchild
                    break
            else:   # val == p.data
                return

        # 2. 更新balance factor
        while node.parent:  # node.parent不空
            if node.parent.lchild == node: # 傳遞是從左子樹來的,左子樹更沉了
                #更新node.parent的bf -= 1
                if node.parent.bf < 0: # 原來node.parent.bf == -1, 更新後變成-2
                    # 作旋轉
                    # 看node哪邊沉
                    g = node.parent.parent # 爲了鏈接旋轉以後的子樹
                    x = node.parent  # 旋轉前的子樹的根
                    if node.bf > 0:
                        n = self.rotate_left_right(node.parent, node)
                    else:
                        n = self.rotate_right(node.parent, node)
                    # 記得:把n和g連起來
                elif node.parent.bf > 0: # 原來node.parent.bf = 1,更新以後變成0
                    node.parent.bf = 0
                    break
                else: # 原來node.parent.bf = 0,更新以後變成-1
                    node.parent.bf = -1
                    node = node.parent
                    continue
            else: # 傳遞是從右子樹來的,右子樹更沉了
                #更新node.parent.bf += 1
                if node.parent.bf > 0:  # 原來node.parent.bf == 1, 更新後變成2
                    # 作旋轉
                    # 看node哪邊沉
                    g = node.parent.parent # 爲了鏈接旋轉以後的子樹
                    x = node.parent  # 旋轉前的子樹的根
                    if node.bf < 0: # node.bf = 1
                        n = self.rotate_right_left(node.parent, node)
                    else:   # node.bf = -1
                        n = self.rotate_left(node.parent, node)
                    # 記得連起來
                elif node.parent.bf < 0: # 原來node.parent.bf = -1,更新以後變成0
                    node.parent.bf = 0
                    break
                else: # 原來node.parent.bf = 0,更新以後變成1
                    node.parent.bf = 1
                    node = node.parent
                    continue

            # 連接旋轉後的子樹
            n.parent = g
            if g: # g不是空
                if x == g.lchild:
                    g.lchild = n
                else:
                    g.rchild = n
                break
            else:
                self.root = n
                break


tree = AVLTree([9,8,7,6,5,4,3,2,1])

tree.pre_order(tree.root)
print("")
tree.in_order(tree.root)
AVLTree.py

 

迷宮問題

給一個二維列表,表示迷宮(0表示通道,1表示圍牆)。給出算法,求一條走出迷宮的路徑。數據結構

方法1:使用棧

思路:從上一個節點開始,任意找下一個能走的節點,當找到不能走的節點時,退回到上一個節點,尋找是否有其餘方向的點app

方法:建立一個棧,首先將入口的位置進棧,當棧不空時候循環,獲取棧頂元素,尋找下一個可走的相鄰方塊,若是找不到可走的相鄰方塊,說明當前是死路,進行回溯(將當前的點出棧,查看前面的點是否還有其餘出路),體現深度優先的思想dom

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
    [1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]

dirs = [
    lambda x,y: (x+1,y),
    lambda x,y: (x-1,y),
    lambda x,y: (x,y-1),
    lambda x,y: (x,y+1)
]

def maze_path(x1,y1,x2,y2):
    stack = []
    stack.append((x1, y1))
    while(len(stack)>0):
        curNode = stack[-1] # 當前的節點
        if curNode[0] == x2 and curNode[1] == y2:
            # 走到終點了
            for p in stack:
                print(p)
            return True

        # x,y 四個方向 x-1,y; x+1,y; x,y-1; x,y+1
        for dir in dirs:
            nextNode = dir(curNode[0], curNode[1])
            # 若是下一個節點能走
            if maze[nextNode[0]][nextNode[1]] == 0:
                stack.append(nextNode)
                maze[nextNode[0]][nextNode[1]] = 2 # 2表示爲已經走過
                break
        else:
            maze[nextNode[0]][nextNode[1]] = 2
            stack.pop()
    else:
        print("沒有路")
        return False

maze_path(1,1,8,8)
View Code

方法2:使用隊列

思路:從一個節點開始尋找,尋找下面能繼續走的點,繼續尋找直到能找出出口ide

方法:建立一個空隊列,將起點位置入隊,在隊列不空時循環,出隊一次,若是相鄰的位置爲出口,則結束.不然找出4個相鄰方塊中可走的方塊,所有入隊,體現廣度優先的思想

from collections import deque

maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
    [1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]

dirs = [
    lambda x, y: (x + 1, y),
    lambda x, y: (x - 1, y),
    lambda x, y: (x, y - 1),
    lambda x, y: (x, y + 1)
]


def print_r(path):
    curNode = path[-1]

    realpath = []

    while curNode[2] == -1:
        realpath.append(curNode[0:2])
        curNode = path[curNode[2]]

    realpath.append(curNode[0:2])  # 起點
    realpath.reverse()
    for node in realpath:
        print(node)


def maze_path_queue(x1, y1, x2, y2):
    queue = deque()
    queue.append((x1, y1, -1))
    path = []
    while len(queue) > 0:
        curNode = queue.pop()
        path.append(curNode)
        if curNode[0] == x2 and curNode[1] == y2:
            # 終點
            print_r(path)
            return True
        for dir in dirs:
            nextNode = dir(curNode[0], curNode[1])
            if maze[nextNode[0]][nextNode[1]] == 0:
                queue.append((nextNode[0], nextNode[1], len(path) - 1))  # 後續節點進隊,記錄哪一個節點帶他來的
                maze[nextNode[0]][nextNode[1]] = 2  # 標記爲已經走過
    else:
        print("沒有路")
        return False


maze_path_queue(1, 1, 8, 8)
View Code

約瑟夫問題

n我的圍成一個圈,每一個人分別標註爲一、二、...、n,要求從1號從1開始報數,報到k的人出圈,接着下一我的又從1開始報數,如此循環,直到只剩最後一我的時,該人即爲勝利者。例如當n=10,k=4時,依次出列的人分別爲四、八、二、七、三、10,九、一、六、5,則5號位置的人爲勝利者。給定n我的,請你編程計算出最後勝利者標號數。

  1. 時間複雜度: 第1種列表法的時間複雜度是O(n2).第2種鏈表法的時間複雜度是O(nm)
  2. 若是n大於m時,鏈表法優於列表法,n小於m時,列表法優於鏈表法
#n表示總人數,m表示報到的數
def yuesefu_1(n,m):
    #1.將全部元素放進列表中,並定義初始的下標爲0
    people = [i for i in range(1,n+1)]
    x = 0
    #2.在列表不空的時候循環
    while len(people) > 0:
        #3.計算報數的人的下標,
        # 1,2,3,4,5,6,7,8 報數
        # 0,1,2,3,4,5,6,7 下標,每次取出對總人數的餘數就是要找的人 
        dead_location = (x+(m-1))%len(people)
        yield people.pop(dead_location) #將找到的人移除出列表
        x = dead_location #從移除出去的人的位置上,繼續執行

print(list(yuesefu_1(9,4)))
方法1:列表操做
class LinkList:
    #自定義鏈表實現類
    class Node:
        def __init__(self,item=None):
            self.item = item
            self.next = None

    class LinkListIterator:
        def __init__(self,node):
            self.node = node

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
        def __iter__(self):
            return self

    def __init__(self,iteratbe=None):
        self.head = LinkList.Node(0)
        self.tail = self.head
        self.extend(iteratbe)
    
    #鏈表添加
    def append(self,obj):
        s = LinkList.Node(obj)
        self.tail.next = s
        self.tail = s
    
    #鏈表擴展
    def extend(self,iterable):
        for obj in iterable:
            self.append(obj)
        self.head.item += len(iterable)

    def remove_nth_node(self,node,m):
        #刪除鏈表第n個元素
        for i in range(m-2):
            node = node.next
        p = node.next
        node.next = p.next
        self.head.item -= 1
        return p


    def __iter__(self):
        return self.LinkListIterator(self.head.next)

    def __len__(self):
        return self.head.item

    def __str__(self):
        return '<<'+", ".join(map(str,self)) +">>"

def yuesefu_link(n,m):
    people = LinkList([i for i in range(1,n+1)])
    people.tail.next = people.head.next
    x = people.head.next
    while len(people)>0:
        p = people.remove_nth_node(x,m)
        x = p.next
        yield p.item

print(list(yuesefu_link(9,4)))
方法2:鏈表操做
相關文章
相關標籤/搜索