演算法之迷宮問題 資料結構相關知識
資料結構相關知識
一、什麼是資料結構?
資料結構是指相互之間存在著一種或多種關係的資料元素的集合和該集合中資料元素之間的關係組成。
簡單來說,資料結構就是設計資料以何種方式組織並存儲在計算機中。
比如:列表、集合與字典等都是一種資料結構。
“程式=資料結構+演算法”
二、資料結構的分類
資料結構按照其邏輯結構可分為線性結構、樹結構、圖結構
- 線性結構:資料結構中的元素存在一對一的相互關係
- 樹結構:資料結構中的元素存在一對多的相互關係
- 圖結構:資料結構中的元素存在多對多的相互關係
下面就來說說線性結構,樹結構見連結,圖結構待續。。
三、線性結構
(1)棧
1、定義:棧是一個數據集合,可以理解為只能在一端進行插入或者刪除操作的列表。
2、棧的特點:後進先出(last-in,first-out),簡稱LTFO表
3、棧的概念:
- 棧頂:允許插入和刪除的這一端稱之為棧頂
- 棧底:另一固定的一端稱為棧底
- 空棧:不含任何元素的棧稱為空棧
4、棧的基本操作:
- 進棧(壓棧):push
- 出棧:pop
- 取棧頂:gettop
如圖:
5、棧的Python實現
不需要自己定義,使用列表結構即可。
- 進棧函式:append
- 出棧函式:pop
- 檢視棧頂元素:li[-1]
li = [] li.append(1) li.append(2) li.append(3) print(li) print(li.pop()) print(li.pop()) print(li.pop()) # print(li.pop()) #當棧空的時候就會報錯 # print(li[-1]) #檢視棧頂元素
6、棧的應用----括號匹配問題
問題:給一個字串,其中包含小括號,中括號,大括號,求該字串中的括號是否匹配
例如:
()()[]{} 匹配
([{()}]) 匹配
[]( 不匹配
[(]) 不匹配
程式碼如下:
# 方式一 def brace_match(s): '''括號匹配問題''' stack = [] # {()}[] match = {')': '(', '}': '{', ']': '['} match2 = {'(': ')', "{": "}", "[": "]"} for ch in s: # print(i) if ch in {'(', '{', '['}: # 如果左括號在裡面就把左括號新增進去,等待和右括號匹配 stack.append(ch) elif len(stack) == 0: # 如果再次進來括號的時候,這時候發現棧空了,說明缺少了左括號了 print('缺少了%s' % match[ch]) return False elif stack[-1] == match[ch]: # 比如棧頂元素是(,ch=')' stack.pop() else: print('括號不匹配') return False if len(stack) > 0: # 如果棧剩餘了,說明缺少了右括號 print('缺少了%s' % match2[stack[-1]]) return False return '匹配成功' # 方式二 def check_kuohao(s): stack = [] for char in s: if char in {'(', '[', '{'}: stack.append(char) elif char == ')': if len(stack) > 0 and stack[-1] == '(': stack.pop() else: return False elif char == ']': if len(stack) > 0 and stack[-1] == '[': stack.pop() else: return False elif char == '}': if len(stack) > 0 and stack[-1] == '{': stack.pop() else: return False if len(stack) == 0: return True else: return False ret = brace_match('{()}]') print(ret) print(check_kuohao('{()}]'))
(2)佇列
1、介紹
- 佇列是一個數據集合,僅允許在列表的一端進行插入,另一端進行刪除,
- 進行插入的一端稱為隊尾(rear),插入動作稱之為進隊或入隊
- 進行刪除的一端稱之為對頭(front),刪除動作稱為出隊
雙向佇列:對列的兩端都允許進行進隊和出隊操作
2、佇列的實現
佇列能否簡單用列表實現?為什麼?
- 初步設想:列表+兩個下標指標
- 建立一個列表和兩個變數,front變數指向隊首,rear變數指向隊尾。初始時,front和rear都為0。
- 進隊操作:元素寫到li[rear]的位置,rear自增1。
- 出隊操作:返回li[front]的元素,front自減1。
3、佇列的實現原理-----環形對列
環形佇列:當隊尾指標front == Maxsize + 1時,再前進一個位置就自動到0。
- 實現方式:求餘數運算
- 隊首指標前進1:front = (front + 1) % MaxSize
- 隊尾指標前進1:rear = (rear + 1) % MaxSize
- 隊空條件:rear == front
- 隊滿條件:(rear + 1) % MaxSize == front
3、對列的內建模組
使用方法:from collections import deque #deque是支援雙向佇列的
- 建立佇列:queue = deque(li)
- 進隊:append
- 出隊:popleft
- 雙向佇列隊首進隊:appendleft
- 雙向佇列隊尾進隊:pop
from collections import deque queue = deque()#建立佇列 queue.append('first') queue.append('second') queue.append('third') print(queue.popleft()) print(queue.popleft()) print(queue.popleft())#出隊,,先進先出 print([i for i in queue]) #檢視佇列裡的元素 queue.appendleft('one')#雙向佇列隊首進隊 queue.appendleft('two')#雙向佇列隊首進隊 queue.appendleft('five')#雙向佇列隊首進隊 print(queue.pop()) print(queue.pop())#雙向佇列從隊尾出隊 print([i for i in queue]) #單向佇列 from queue import Queue q = Queue() q.put('a') q.put('b') q.put('c') print(q.get()) #a
(3)單鏈表
連結串列中每一個元素都是一個物件,每一個物件都是一個節點,包含有資料域key和指向下一個節點的指標next。通過各個節點之間的互相連線,最終串聯成一個列表
1、節點定義:
class Node(object): def __init__(self, item): self.item = item self.next = None
2、建立連結串列
頭插法
尾插法
3、連結串列的遍歷
4、連結串列節點的插入和刪除
# 插入: p.next = curNode.next curNode.next = p #刪除: p = curNode.next curNode.next = p.next #當前節點的下一個指向就指向他下一個的下一個 del p
插入:
刪除:
< >(4)雙鏈表
雙鏈表中的每個節點有兩個指標:一個指向後面節點、一個指向前面節點
1、節點定義:
class Node(object): def __init__(self,item): self.item = item #資料 self.next = None #下一個指向 self.prior = None #上一個指向
2、雙鏈表節點的插入和刪除
#插入 p.next = curNode.next curNode.next.prior = p p.prior = curNode curNode.next = p #刪除 p = curNode.next curNode.next = p.next p.next.prior = curNode del p
(1)
(2)
(3)
(4)
這四張圖片分別對應上面的四行程式碼
(5)雜湊表
雜湊表(又稱為散列表),是一種線性表的儲存結構。雜湊表由一個順序表(陣列)和一個雜湊函式組成。雜湊函式h(k)將k作為自變數,返回元素的儲存下標。
1、簡單雜湊函式
- 除法雜湊:h(k) = k mod m #mod就是% #除留餘數法
- 乘法雜湊:h(k) = floor(m(kA mod 1)) 0<A<1 #floor是向下取整
假設有一個長度為7的陣列,雜湊函式h(k)=k%7。元素集合{14,22,3,5}的儲存方式如下圖。
2、雜湊衝突
由於雜湊表的大小是有限的,而要儲存的值的總數量是無限的,因此對於任何雜湊函式,都會出現兩個不同元素對映到同一個位置上的情況 ,
這種情況叫做雜湊衝突。
比如h(k)=k%7, h(0)=h(7)=h(14)=...
3、解決雜湊衝突的辦法
- a、開放定址法
- b、拉鍊發
開放定址法:如果雜湊函式返回的位置已經有值,則可以向後探查新的位置來儲存這個值。
- 線性探查:如果位置i被佔用,則探查i+1, i+2,……
- 二次探查:如果位置i被佔用,則探查i+12,i-12,i+22,i-22,……
- 二度雜湊:有n個雜湊函式,當使用第1個雜湊函式h1發生衝突時,則嘗試使用h2,h3,……
拉鍊法:雜湊表每個位置都連線一個連結串列,當衝突發生時,衝突的元素將被加到該位置連結串列的最後。
4、雜湊表在Python中的應用
- a、字典與集合都是通過雜湊表來實現的
- b、 在Python中的字典:a = {'name': 'Alex', 'age': 18, 'gender': 'Man'},使用雜湊表儲存字典,通過雜湊函式將字典的鍵對映為下標。
假設h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,則雜湊表儲存為[None, 18, None, ’Alex’, ‘Man’]
- c、在字典鍵值對數量不多的情況下,幾乎不會發生雜湊衝突,此時查詢一個元素的時間複雜度為O(1)。
一、什麼是資料結構?
資料結構是指相互之間存在著一種或多種關係的資料元素的集合和該集合中資料元素之間的關係組成。
簡單來說,資料結構就是設計資料以何種方式組織並存儲在計算機中。
比如:列表、集合與字典等都是一種資料結構。
“程式=資料結構+演算法”
二、資料結構的分類
資料結構按照其邏輯結構可分為線性結構、樹結構、圖結構
- 線性結構:資料結構中的元素存在一對一的相互關係
- 樹結構:資料結構中的元素存在一對多的相互關係
- 圖結構:資料結構中的元素存在多對多的相互關係
下面就來說說線性結構,樹結構見連結,圖結構待續。。
三、線性結構
(1)棧
1、定義:棧是一個數據集合,可以理解為只能在一端進行插入或者刪除操作的列表。
2、棧的特點:後進先出(last-in,first-out),簡稱LTFO表
3、棧的概念:
- 棧頂:允許插入和刪除的這一端稱之為棧頂
- 棧底:另一固定的一端稱為棧底
- 空棧:不含任何元素的棧稱為空棧
4、棧的基本操作:
- 進棧(壓棧):push
- 出棧:pop
- 取棧頂:gettop
如圖:
5、棧的Python實現
不需要自己定義,使用列表結構即可。
- 進棧函式:append
- 出棧函式:pop
- 檢視棧頂元素:li[-1]
li = [] li.append(1) li.append(2) li.append(3) print(li) print(li.pop()) print(li.pop()) print(li.pop()) # print(li.pop()) #當棧空的時候就會報錯 # print(li[-1]) #檢視棧頂元素
6、棧的應用----括號匹配問題
問題:給一個字串,其中包含小括號,中括號,大括號,求該字串中的括號是否匹配
例如:
()()[]{} 匹配
([{()}]) 匹配
[]( 不匹配
[(]) 不匹配
程式碼如下:
# 方式一 def brace_match(s): '''括號匹配問題''' stack = [] # {()}[] match = {')': '(', '}': '{', ']': '['} match2 = {'(': ')', "{": "}", "[": "]"} for ch in s: # print(i) if ch in {'(', '{', '['}: # 如果左括號在裡面就把左括號新增進去,等待和右括號匹配 stack.append(ch) elif len(stack) == 0: # 如果再次進來括號的時候,這時候發現棧空了,說明缺少了左括號了 print('缺少了%s' % match[ch]) return False elif stack[-1] == match[ch]: # 比如棧頂元素是(,ch=')' stack.pop() else: print('括號不匹配') return False if len(stack) > 0: # 如果棧剩餘了,說明缺少了右括號 print('缺少了%s' % match2[stack[-1]]) return False return '匹配成功' # 方式二 def check_kuohao(s): stack = [] for char in s: if char in {'(', '[', '{'}: stack.append(char) elif char == ')': if len(stack) > 0 and stack[-1] == '(': stack.pop() else: return False elif char == ']': if len(stack) > 0 and stack[-1] == '[': stack.pop() else: return False elif char == '}': if len(stack) > 0 and stack[-1] == '{': stack.pop() else: return False if len(stack) == 0: return True else: return False ret = brace_match('{()}]') print(ret) print(check_kuohao('{()}]'))
(2)佇列
1、介紹
- 佇列是一個數據集合,僅允許在列表的一端進行插入,另一端進行刪除,
- 進行插入的一端稱為隊尾(rear),插入動作稱之為進隊或入隊
- 進行刪除的一端稱之為對頭(front),刪除動作稱為出隊
雙向佇列:對列的兩端都允許進行進隊和出隊操作
2、佇列的實現
佇列能否簡單用列表實現?為什麼?
- 初步設想:列表+兩個下標指標
- 建立一個列表和兩個變數,front變數指向隊首,rear變數指向隊尾。初始時,front和rear都為0。
- 進隊操作:元素寫到li[rear]的位置,rear自增1。
- 出隊操作:返回li[front]的元素,front自減1。
3、佇列的實現原理-----環形對列
環形佇列:當隊尾指標front == Maxsize + 1時,再前進一個位置就自動到0。
- 實現方式:求餘數運算
- 隊首指標前進1:front = (front + 1) % MaxSize
- 隊尾指標前進1:rear = (rear + 1) % MaxSize
- 隊空條件:rear == front
- 隊滿條件:(rear + 1) % MaxSize == front
3、對列的內建模組
使用方法:from collections import deque #deque是支援雙向佇列的
- 建立佇列:queue = deque(li)
- 進隊:append
- 出隊:popleft
- 雙向佇列隊首進隊:appendleft
- 雙向佇列隊尾進隊:pop
from collections import deque queue = deque()#建立佇列 queue.append('first') queue.append('second') queue.append('third') print(queue.popleft()) print(queue.popleft()) print(queue.popleft())#出隊,,先進先出 print([i for i in queue]) #檢視佇列裡的元素 queue.appendleft('one')#雙向佇列隊首進隊 queue.appendleft('two')#雙向佇列隊首進隊 queue.appendleft('five')#雙向佇列隊首進隊 print(queue.pop()) print(queue.pop())#雙向佇列從隊尾出隊 print([i for i in queue]) #單向佇列 from queue import Queue q = Queue() q.put('a') q.put('b') q.put('c') print(q.get()) #a
(3)單鏈表
連結串列中每一個元素都是一個物件,每一個物件都是一個節點,包含有資料域key和指向下一個節點的指標next。通過各個節點之間的互相連線,最終串聯成一個列表
1、節點定義:
class Node(object): def __init__(self, item): self.item = item self.next = None
2、建立連結串列
頭插法
尾插法
3、連結串列的遍歷
4、連結串列節點的插入和刪除
# 插入: p.next = curNode.next curNode.next = p #刪除: p = curNode.next curNode.next = p.next #當前節點的下一個指向就指向他下一個的下一個 del p
插入:
刪除:
< >(4)雙鏈表
雙鏈表中的每個節點有兩個指標:一個指向後面節點、一個指向前面節點
1、節點定義:
class Node(object): def __init__(self,item): self.item = item #資料 self.next = None #下一個指向 self.prior = None #上一個指向
2、雙鏈表節點的插入和刪除
#插入 p.next = curNode.next curNode.next.prior = p p.prior = curNode curNode.next = p #刪除 p = curNode.next curNode.next = p.next p.next.prior = curNode del p
(1)
(2)
(3)
(4)
這四張圖片分別對應上面的四行程式碼
(5)雜湊表
雜湊表(又稱為散列表),是一種線性表的儲存結構。雜湊表由一個順序表(陣列)和一個雜湊函式組成。雜湊函式h(k)將k作為自變數,返回元素的儲存下標。
1、簡單雜湊函式
- 除法雜湊:h(k) = k mod m #mod就是% #除留餘數法
- 乘法雜湊:h(k) = floor(m(kA mod 1)) 0<A<1 #floor是向下取整
假設有一個長度為7的陣列,雜湊函式h(k)=k%7。元素集合{14,22,3,5}的儲存方式如下圖。
2、雜湊衝突
由於雜湊表的大小是有限的,而要儲存的值的總數量是無限的,因此對於任何雜湊函式,都會出現兩個不同元素對映到同一個位置上的情況 ,
這種情況叫做雜湊衝突。
比如h(k)=k%7, h(0)=h(7)=h(14)=...
3、解決雜湊衝突的辦法
- a、開放定址法
- b、拉鍊發
開放定址法:如果雜湊函式返回的位置已經有值,則可以向後探查新的位置來儲存這個值。
- 線性探查:如果位置i被佔用,則探查i+1, i+2,……
- 二次探查:如果位置i被佔用,則探查i+12,i-12,i+22,i-22,……
- 二度雜湊:有n個雜湊函式,當使用第1個雜湊函式h1發生衝突時,則嘗試使用h2,h3,……
拉鍊法:雜湊表每個位置都連線一個連結串列,當衝突發生時,衝突的元素將被加到該位置連結串列的最後。
4、雜湊表在Python中的應用
- a、字典與集合都是通過雜湊表來實現的
- b、 在Python中的字典:a = {'name': 'Alex', 'age': 18, 'gender': 'Man'},使用雜湊表儲存字典,通過雜湊函式將字典的鍵對映為下標。
假設h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,則雜湊表儲存為[None, 18, None, ’Alex’, ‘Man’]
- c、在字典鍵值對數量不多的情況下,幾乎不會發生雜湊衝突,此時查詢一個元素的時間複雜度為O(1)。