python常用代码_Python常用算法学习(4) 数据结构(原理+代码)-最全总结
數(shù)據(jù)結(jié)構(gòu)簡介
1,數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù)結(jié)構(gòu)是指相互之間存在著一種或多種關(guān)系的數(shù)據(jù)元素的集合和該集合中數(shù)據(jù)元素之間的關(guān)系組成。簡單來說,數(shù)據(jù)結(jié)構(gòu)就是設(shè)計(jì)數(shù)據(jù)以何種方式組織并存貯在計(jì)算機(jī)中。比如:列表,集合與字典等都是一種數(shù)據(jù)結(jié)構(gòu)。而之前已經(jīng)學(xué)習(xí)過列表,字典,集合,元組等,這里就簡單說一下不再贅述。
N.Wirth:“程序=數(shù)據(jù)結(jié)構(gòu)+算法”
數(shù)據(jù):數(shù)據(jù)即信息的載體,是能夠輸入到計(jì)算機(jī)中并且能被計(jì)算機(jī)識別,存儲和處理的符號總稱。
數(shù)據(jù)元素:數(shù)據(jù)元素是數(shù)據(jù)的基本單位,又稱之為記錄(Record),一般,數(shù)據(jù)元素若由若干基本型(或稱字段,域,屬性)組成。
2,數(shù)據(jù)結(jié)構(gòu)的分類
數(shù)據(jù)結(jié)構(gòu)按照其邏輯結(jié)構(gòu)可分為線性結(jié)構(gòu),樹結(jié)構(gòu),圖結(jié)構(gòu)
- 線性結(jié)構(gòu):數(shù)據(jù)結(jié)構(gòu)中的元素存在一對一的相互關(guān)系
- 樹結(jié)構(gòu):數(shù)據(jù)結(jié)構(gòu)中的元素存在一對多的相互關(guān)系
- 圖結(jié)構(gòu):數(shù)據(jù)結(jié)構(gòu)中的元素存在多對多的相互關(guān)系
3,存儲結(jié)構(gòu)
1,特點(diǎn)
- 1,是數(shù)據(jù)的邏輯結(jié)構(gòu)在計(jì)算機(jī)存儲器中的映象(或表示)
- 2,存儲結(jié)構(gòu)是通過計(jì)算機(jī)程序來實(shí)現(xiàn),因而是依賴于具體的計(jì)算機(jī)語言的
2,存儲結(jié)構(gòu)分類
- 1,順序存儲(Sequential Storage):將數(shù)據(jù)結(jié)構(gòu)中各元素按照其邏輯順序存放于存儲器一片連續(xù)的存儲空間中。
- 2,鏈?zhǔn)酱鎯?Linkend Storage):將數(shù)據(jù)結(jié)構(gòu)中各元素分布到存儲器的不通電,用記錄下一個(gè)結(jié)點(diǎn)位置的方式建立他們之間的聯(lián)系,由此得到的存儲結(jié)構(gòu)為鏈?zhǔn)酱鎯Y(jié)果。
- 3,索引存儲(Indexed Storage):在存儲數(shù)據(jù)的同時(shí),建立一個(gè)附加的索引表,即索引存儲結(jié)構(gòu)=數(shù)據(jù)文件+索引表。
常用的數(shù)據(jù)結(jié)構(gòu)
1,列表
列表(其他語言稱為數(shù)組,但是數(shù)組和Python的列表還是有區(qū)別的)是一種基本數(shù)據(jù)類型。
列表,在Python中稱為list,使用中括號包含,其中內(nèi)部元素使用逗號分隔,內(nèi)部元素可以是任何類型包含空。
1.1 數(shù)組和列表不同點(diǎn)
- 1,數(shù)組元素類型要相同,而Python列表不需要
- 2,數(shù)組長度固定,而Python列表可以增加
Python列表操作方法請參考此博客:https://www.cnblogs.com/wj-1314/p/8433116.html
2,元組
元組,Python中類為tuple。使用小括號包含,內(nèi)部元素使用逗號分隔,可為任意值。與列表不同之處為,其內(nèi)部元素不可修改,而且不能做刪除,更新操作。
注意:當(dāng)元組中元素只有一個(gè)時(shí),結(jié)尾要加逗號,若不加逗號,Python解釋器都將解釋成元素本身的類型,而非元組類型。
舉個(gè)例子:
>>> a = (1,2,3)>>> b = ('1',[2,3])>>> c = ('1','2',(3,4))>>> d = ()>>> e = (1,)2.1 元組的操作
通過下標(biāo)操作
如下,做出更改和刪除會報(bào)異常:
>>> c[0] = 1Traceback (most recent call last):??File?"", line 1,?in?TypeError:?'tuple'?object?does not support item assignment?>>> del c[0]Traceback (most recent call last):??File?"", line 1,?in?TypeError:?'tuple'?object?doesn't support item deletion2.2 運(yùn)算符及內(nèi)建函數(shù)操作
元組本身是不可變,但是可以通過 + 來構(gòu)成新的元組。
>>> a(1, 2, 3)>>> b('1', [2, 3])>>> a + b(1, 2, 3,?'1', [2, 3])可以通過使用內(nèi)建函數(shù) len 獲取元組長度,可以使用 * 元素來實(shí)現(xiàn)元素的重復(fù)
>>> a = (1,2,3)>>> len(a)3>>> a*3(1, 2, 3, 1, 2, 3, 1, 2, 3)元組也支持 in 和 not in 成員運(yùn)算符。
3,集合
集合用于包含一組無序的對象,與列表和元組不同,集合是無序的,也無法通過數(shù)字進(jìn)行索引。此外,集合中的元素不能重復(fù)。set和dict一樣,只是沒有 value,相當(dāng)于 dict 的 key 集合,由于 dict 的 key 是不重復(fù)的,且 key 是不可變對象,因此 set 有如下特性:
- 1,不重復(fù),(互異性),也就是說集合是天生去重的
- 2,元素為不可變對象,(確定性,元素必須可 hash)
- 3,集合的元素沒有先后之分,(無序性)
Python的集合與數(shù)學(xué)中的集合一樣,也有交集,并集和合集。
舉例如下:
>>> s1 = {1,2,3,4,5}>>> s2 = {1,2,3}>>> s3 = {4,5}>>> s1&s2?? # 交集{1, 2, 3}>>> s1|s2?? # 并集{1, 2, 3, 4, 5}?>>> s1 - s2? # 差差集{4, 5}>>> s3.issubset(s1)?? # s3 是否為s1 的子集True>>> s1.issuperset(s2)? # s1 是否為 s2 的超集True更多集合相關(guān)的操作參考博客:https://www.cnblogs.com/wj-1314/p/8423273.html
4,棧
棧(Stack)是一個(gè)數(shù)據(jù)集合,可以理解為只能在一段進(jìn)行插入或刪除操作的列表。
棧的特點(diǎn):后進(jìn)先出 LIFO(Last-in, First-out)
棧的概念有:棧頂,棧底
棧的基本操作:
對棧的理解,就像是一摞書,只能從上面放和取。
4.1 棧的實(shí)現(xiàn)
使用一般列表結(jié)構(gòu)即可實(shí)現(xiàn)棧:
那現(xiàn)在我們寫一個(gè)類實(shí)現(xiàn)它:
class?Stack:?????def __init__(self):????????self.stack = []?????def push(self, element):????????self.stack.append(element)?????def pop(self):????????# 移除列表中的一個(gè)元素(默認(rèn)最后一個(gè)),并返回該元素的值????????return?self.stack.pop()?????def get_top(self):????????if?len(self.stack) > 0:????????????return?self.stack[-1]????????else:????????????return?None?stack = Stack()stack.push(1)stack.push(2)print(stack.pop())?? # 24.2 棧的應(yīng)用——括號匹配問題
我們可以通過棧來解決括號匹配的問題,就是解決IDE實(shí)現(xiàn)括號未匹配成功報(bào)錯(cuò)的問題。
代碼:
class?Stack:?????def __init__(self):????????self.stack = []?????def push(self,element):????????return?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:????????????if?stack.is_empty():????????????????return?False????????????elif stack.get_top() == match[ch]:????????????????stack.pop()????????????else:????????????????return?False?????if?stack.is_empty():????????return?True????else:????????return?False?print(brace_match('{[{()}]}'))5,隊(duì)列
隊(duì)列(Queue)是一個(gè)數(shù)據(jù)集合,僅允許在列表的一端進(jìn)行插入,另一端進(jìn)行刪除。進(jìn)行插入的一端稱為隊(duì)尾(rear),插入動作稱為進(jìn)隊(duì)或者入隊(duì)。進(jìn)行刪除的一端稱為對頭(front),刪除動作稱為出隊(duì)。
隊(duì)列的性質(zhì):先進(jìn)先出(First-in , First-out)
隊(duì)列可以并發(fā)的派多個(gè)線程,對排列的線程處理,并切每個(gè)需要處理線程只需要將請求的數(shù)據(jù)放入隊(duì)列容器的內(nèi)存中,線程不需要等待,當(dāng)排列完畢處理完數(shù)據(jù)后,線程在準(zhǔn)時(shí)來取數(shù)據(jù)即可,請求數(shù)據(jù)的線程只與這個(gè)隊(duì)列容器存在關(guān)系,處理數(shù)據(jù)的線程down掉不會影響到請求數(shù)據(jù)的線程,隊(duì)列會派給其他線程處理這份數(shù)據(jù),它實(shí)現(xiàn)了解耦,提高效率。隊(duì)列內(nèi)會有一個(gè)順序的容器,列表與這個(gè)容器是有區(qū)別的,列表中數(shù)據(jù)雖然是排列的,但數(shù)據(jù)被取走后還會保留,而隊(duì)列中這些容器的數(shù)據(jù)被取后將不會保留。當(dāng)必須在多個(gè)線程之間安全的交換信息時(shí),隊(duì)列在線程編程中特別有用。
5.1 隊(duì)列的實(shí)現(xiàn)
隊(duì)列能否用列表簡單實(shí)現(xiàn)?為什么?
隊(duì)列其實(shí)就是一個(gè)先進(jìn)先出的線性表,只能在隊(duì)首執(zhí)行刪除操作,在隊(duì)尾執(zhí)行插入操作,用列表表示隊(duì)列,用 append() 方法實(shí)現(xiàn)在隊(duì)尾插入元素,用 pop(0)方法實(shí)現(xiàn)在隊(duì)首刪除元素。
觀察上圖,我們會發(fā)現(xiàn)是把列表的最左邊(最上邊)當(dāng)做了隊(duì)列的首,把最右邊(最下邊)當(dāng)做了尾巴(左首右尾,上首下尾),用 insert()或者 append()方法實(shí)現(xiàn)隊(duì)尾插入元素,用 pop()方法實(shí)現(xiàn)隊(duì)尾刪除元素。
環(huán)形隊(duì)列的實(shí)現(xiàn)方式
環(huán)形隊(duì)列:當(dāng)隊(duì)尾指針 front == MaxSize + 1 時(shí),再前進(jìn)一個(gè)位置就自動到0。
- 隊(duì)首指針前進(jìn)1: front = (front + 1) % MaxSize
- 隊(duì)尾指針前進(jìn)1: rear = (rear + 1) % MaxSize
- 隊(duì)空條件: front == rear
- 隊(duì)滿條件: (rear + 1) % MaxSize == front
雙向隊(duì)列的完整用法:
# _*_coding:utf-8_*_?# 創(chuàng)建雙向隊(duì)列from?collections import deque?d = deque()?# append(往右邊添加一個(gè)元素)d.append(1)d.append(2)print(d)? # deque([1, 2])?# appendleft(往左邊添加一個(gè)元素)d.appendleft(11)d.appendleft(22)print(d)? # deque([22, 11, 1, 2])?# clear清空隊(duì)列d.clear()print(d)? # deque([])?# 淺copy copyd.append(1)new_d = d.copy()print(new_d)? # deque([1])?#? count(返回指定元素的出現(xiàn)次數(shù))d.append(1)d.append(1)print(d)? # deque([1, 1, 1])print(d.count(1))? # 3?# extend(從隊(duì)列右邊擴(kuò)展一個(gè)列表的元素)d.append(2)d.extend([3, 4, 5])print(d)? # deque([1, 1, 1, 2, 3, 4, 5])?# extendleft(從隊(duì)列左邊擴(kuò)展一個(gè)列表的元素)d.extendleft([3, 4, 5])print(d)? # deque([5, 4, 3, 1, 1, 1, 2, 3, 4, 5])?#? index(查找某個(gè)元素的索引位置)d.clear()d.extend(['a',?'b',?'c',?'d',?'e'])print(d)print(d.index('e'))print(d.index('c', 0, 3))? # 指定查找區(qū)間'''deque(['a',?'b',?'c',?'d',?'e'])42'''?#? insert(在指定位置插入元素)d.insert(2,?'z')print(d)# deque(['a', 'b', 'z', 'c', 'd', 'e'])?# pop(獲取最右邊一個(gè)元素,并在隊(duì)列中刪除)x = d.pop()print(x)print(d)'''edeque(['a',?'b',?'z',?'c',?'d'])'''?# popleft(獲取最左邊一個(gè)元素,并在隊(duì)列中刪除)print(d)x = d.popleft()print(x)print(d)'''deque(['a',?'b',?'z',?'c',?'d'])adeque(['b',?'z',?'c',?'d'])'''?# remove(刪除指定元素)print(d)d.remove('c')print(d)'''deque(['b',?'z',?'c',?'d'])deque(['b',?'z',?'d'])'''?# reverse(隊(duì)列反轉(zhuǎn))print(d)d.reverse()print(d)'''deque(['b',?'z',?'d'])deque(['d',?'z',?'b'])'''?# rotate(把右邊元素放到左邊)d.extend(['a',?'b',?'c',?'d',?'e'])print(d)d.rotate(2)? # 指定次數(shù),默認(rèn)1次print(d)'''deque(['d',?'z',?'b',?'a',?'b',?'c',?'d',?'e'])deque(['d',?'e',?'d',?'z',?'b',?'a',?'b',?'c'])'''Python四種類型的隊(duì)列
Queue:FIFO 即 first in first out 先進(jìn)先出
LifoQueue:LIFO 即 last in first out 后進(jìn)先出
PriorityQueue:優(yōu)先隊(duì)列,級別越低,越優(yōu)先
deque:雙邊隊(duì)列
導(dǎo)入三種隊(duì)列,包
from?queue import Queue,LifoQueue,PriorityQueueQueue:先進(jìn)先出隊(duì)列
#基本FIFO隊(duì)列? 先進(jìn)先出 FIFO即First in First Out,先進(jìn)先出#maxsize設(shè)置隊(duì)列中,數(shù)據(jù)上限,小于或等于0則不限制,容器中大于這個(gè)數(shù)則阻塞,直到隊(duì)列中的數(shù)據(jù)被消掉q = Queue(maxsize=0)?#寫入隊(duì)列數(shù)據(jù)q.put(0)q.put(1)q.put(2)?#輸出當(dāng)前隊(duì)列所有數(shù)據(jù)print(q.queue)#刪除隊(duì)列數(shù)據(jù),并返回該數(shù)據(jù)q.get()#輸也所有隊(duì)列數(shù)據(jù)print(q.queue)?# 輸出:# deque([0, 1, 2])# deque([1, 2])LifoQueue:后進(jìn)先出隊(duì)列:
#LIFO即Last in First Out,后進(jìn)先出。與棧的類似,使用也很簡單,maxsize用法同上lq = LifoQueue(maxsize=0)?#隊(duì)列寫入數(shù)據(jù)lq.put(0)lq.put(1)lq.put(2)?#輸出隊(duì)列所有數(shù)據(jù)print(lq.queue)#刪除隊(duì)尾數(shù)據(jù),并返回該數(shù)據(jù)lq.get()#輸出隊(duì)列所有數(shù)據(jù)print(lq.queue)?#輸出:# [0, 1, 2]# [0, 1]優(yōu)先隊(duì)列
# 存儲數(shù)據(jù)時(shí)可設(shè)置優(yōu)先級的隊(duì)列# 優(yōu)先級設(shè)置數(shù)越小等級越高pq = PriorityQueue(maxsize=0)?#寫入隊(duì)列,設(shè)置優(yōu)先級pq.put((9,'a'))pq.put((7,'c'))pq.put((1,'d'))?#輸出隊(duì)例全部數(shù)據(jù)print(pq.queue)?#取隊(duì)例數(shù)據(jù),可以看到,是按優(yōu)先級取的。pq.get()pq.get()print(pq.queue)?#輸出:[(9,?'a')]雙邊隊(duì)列
#雙邊隊(duì)列dq = deque(['a','b'])?#增加數(shù)據(jù)到隊(duì)尾dq.append('c')#增加數(shù)據(jù)到隊(duì)左dq.appendleft('d')?#輸出隊(duì)列所有數(shù)據(jù)print(dq)#移除隊(duì)尾,并返回print(dq.pop())#移除隊(duì)左,并返回print(dq.popleft())#輸出:deque(['d',?'a',?'b',?'c'])cd6,鏈表
當(dāng)我們存儲一大波數(shù)據(jù)時(shí),我們很多時(shí)候是使用數(shù)組,但是當(dāng)我們執(zhí)行插入操作的時(shí)候就是非常麻煩,比如,有一堆數(shù)據(jù)1,2,3,5,6,7 我們要在3和5之間插入4,如果用數(shù)組,我們會怎么辦?當(dāng)然是將5之后的數(shù)據(jù)往后退一位,然后再插入4,這樣非常麻煩,但是如果用鏈表,就直接在3和5之間插入4即可。
所以鏈表的節(jié)點(diǎn)結(jié)構(gòu)如下:
data為自定義的數(shù)據(jù),next為下一個(gè)節(jié)點(diǎn)的地址。head保存首位節(jié)點(diǎn)的地址。
首先可以看一個(gè)小鏈表,定義鏈表:
class?Node:????def __init__(self, item):????????self.item = item????????self.next = None?a = Node(1)b = Node(2)c = Node(3)# 通過next 將 a,b,c聯(lián)系起來a.next = bb.next = c# 打印a的下一個(gè)的下一個(gè)結(jié)果是什么print(a.next.next.item) # 3當(dāng)然,我們不可能這樣next來調(diào)用鏈表,使用循環(huán)等,下面繼續(xù)學(xué)習(xí)。
鏈表永遠(yuǎn)有一個(gè)頭結(jié)點(diǎn),head
頭插法和尾插法的代碼如下:
class?Node:????def __init__(self, item):????????self.item = item????????self.next = None?# 頭插法# 這里我們使用 li 進(jìn)行循環(huán)插入def create_linklist_head(li):????head = Node(li[0])????for?element?in?li[1:]:????????node = Node(element)????????node.next = head? # 將頭結(jié)點(diǎn) 給剛插入的節(jié)點(diǎn)????????head = node?????? # 然后將插入的節(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn)????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????print('*********')?lk = create_linklist_head([1, 2, 3, 4])# print(lk.item)? # 4print_linklist(lk)? # 4,3,2,1,*********?lk = create_linklist_tail([1, 2, 3, 4, 5])print_linklist(lk)? # 1,2,3,4,5,插入如下,非常簡單,所以時(shí)間復(fù)雜度也低。
6.1 關(guān)于鏈表的方法
1,判斷是否為空:isEmpty()
def isEmpty(self):????return?(self.length == 0)2,增加一個(gè)節(jié)點(diǎn)(在鏈表尾添加):append()
def append(self, dataOrNode):????item = None????if?isinstance(dataOrNode, Node):????????item = dataOrNode????else:????????item = Node(dataOrNode)?????????if?not self.head:????????self.head = item????????self.length += 1????else:????????node = self.head????????while?node._next:????????????node = node._next????????node._next = item????????self.length += 13,刪除一個(gè)節(jié)點(diǎn):delete()
# 刪除一個(gè)節(jié)點(diǎn)之后,記得把鏈表長度減一def delete(self, index):????if?self.isEmpty():????????print("this chain table is empty")????????return?????if?index < 0 or index >= self.length:????????print('Error: out of index')????????return?????# 要注意刪除第一個(gè)節(jié)點(diǎn)的情況,如果有空的頭節(jié)點(diǎn)就不用這樣????if?index == 0:????????self.head = self.head._next????????self.length -= 1????????return?????# prev 為保存前導(dǎo)節(jié)點(diǎn)????# node 為保存當(dāng)前節(jié)點(diǎn)????# 當(dāng)j 與index 相等時(shí)就相當(dāng)于找到要刪除的節(jié)點(diǎn)????j = 0????node = self.head????prev = self.head????while?node._next and j < index:????????prev = node????????node = node._next????????j += 1?????????if?j == index:????????prev._next = node._next????????self.length -= 14,修改一個(gè)節(jié)點(diǎn):update()
def update(self, index, data):????if?self.isEmpty() or index < 0 or index >= self.length:????????print?'error: out of index'????????return????j = 0????node = self.head????while?node._next and j < index:????????node = node._next????????j += 1?????if?j == index:????????node.data = data5,查找一個(gè)節(jié)點(diǎn):getItem()
def getItem(self, index):????if?self.isEmpty() or index < 0 or index >= self.length:????????print?"error: out of index"????????return????j = 0????node = self.head????while?node._next and j < index:????????node = node._next????????j += 1?????return?node.data6,查找一個(gè)節(jié)點(diǎn)的索引:getIndex()
def getIndex(self, data):????j = 0????if?self.isEmpty():????????print?"this chain table is empty"????????return????node = self.head????while?node:????????if?node.data == data:????????????return?j????????node = node._next????????j += 1?????if?j == self.length:????????print?"%s not found"?% str(data)????????return7,插入一個(gè)節(jié)點(diǎn):insert()
def insert(self, index, dataOrNode):????if?self.isEmpty():????????print?"this chain tabale is empty"????????return?????if?index < 0 or index >= self.length:????????print?"error: out of index"????????return?????item = None????if?isinstance(dataOrNode, Node):????????item = dataOrNode????else:????????item = Node(dataOrNode)?????if?index == 0:????????item._next = self.head????????self.head = item????????self.length += 1????????return?????j = 0????node = self.head????prev = self.head????while?node._next and j < index:????????prev = node????????node = node._next????????j += 1?????if?j == index:????????item._next = node????????prev._next = item????????self.length += 18,清空鏈表:clear()
def clear(self):????self.head = None????self.length = 06.2 雙鏈表
6.3 鏈表復(fù)雜度分析
順序表(列表/數(shù)組)與鏈表
鏈表與順序表
- 鏈表在插入和刪除的操作上明顯快于順序表
- 鏈表的內(nèi)存可以更加靈活地分配(可以試?yán)面湵碇匦聦?shí)現(xiàn)棧和隊(duì)列)
- 鏈表這種鏈?zhǔn)酱鎯Φ臄?shù)據(jù)結(jié)構(gòu)對樹和圖的結(jié)構(gòu)有很大的啟發(fā)性
7,哈希表(Hash table)
眾所周知,HashMap是一個(gè)用于存儲 Key-Value鍵值對的集合,每一個(gè)鍵值對也叫Entry,這些鍵值對(Entry)分散存儲在一個(gè)數(shù)組當(dāng)中,這個(gè)數(shù)組就是HashMap的主干。
使用哈希表可以進(jìn)行非常快速的查找操作,查找時(shí)間為常數(shù),同時(shí)不需要元素排列有序;Python的內(nèi)建數(shù)據(jù)類型:字典就是用哈希表實(shí)現(xiàn)的。
Python中的這些東西都是哈希原理:字典(dictionary)、集合(set)、計(jì)數(shù)器(counter)、默認(rèn)字典Defaut dict)、有序字典(Order dict).
哈希表示一個(gè)高效的查找的數(shù)據(jù)結(jié)構(gòu),哈希表一個(gè)通過哈希函數(shù)來計(jì)算數(shù)據(jù)存儲位置的數(shù)據(jù)結(jié)構(gòu),通常支持如下操作:
- insert(key, value):插入鍵值對(key, value)
- get(key):如果存在鍵為key的鍵值對則返回其value,否則返回空值
- delete(key):刪除鍵為key的鍵值對
直接尋址表
直接尋址技術(shù)缺點(diǎn):
哈希
直接尋址表:key為k的元素放到k位置上
改進(jìn)直接尋址表:哈希(Hashing)
- 構(gòu)建大小為m的尋址表T
- key為k的元素放到 h(k)位置上
- h(k) 是一個(gè)函數(shù),其將域U映射到表 T[0, 1, ... , m-1]
哈希表
哈希表(Hash Table,又稱為散列表),是一種線性表的存儲結(jié)構(gòu)。哈希表由一個(gè)直接尋址表和一個(gè)哈希函數(shù)組成。哈希函數(shù)h(k)將元素關(guān)鍵字 k 作為自變量,返回元素的存儲下標(biāo)。
假設(shè)有一個(gè)長度為7的哈希表,哈希函數(shù) h(k)=k%7.元素集合 {14, 22, 3, 5}的存儲方式如下圖:
代碼如下:
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?"<>"??# 類似于集合的結(jié)構(gòu)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)7.1 哈希表的應(yīng)用
1,集合與字典
字典與集合都是通過哈希表來實(shí)現(xiàn)的。比如:
dic_res = {'name':'james',?'age':32,?'gender':?'Man'}使用哈希表存儲字典,通過哈希表將字典的鍵映射為下標(biāo),假設(shè) h('name')=3,h('age')=4,則哈希表存儲為:
[None, 32, None,?'james',?'Man']如果發(fā)生哈希沖突,則通過拉鏈法或開發(fā)尋址法解決。
2,md5算法
MD5(Message-Digest Algorithm 5)曾經(jīng)是密碼學(xué)中常用的哈希函數(shù),可以把任意長度的數(shù)據(jù)映射為128位的哈希值,其曾經(jīng)包含如下特征:
- 1,同樣的消息,其MD5值必定相同;
- 2,可以快速計(jì)算出任意給定消息的MD5值;
- 3,除非暴力的枚舉所有可能的消息,否則不可能從哈希值反推出消息本身;
- 4,兩天消息之間即使只有微小的差別,其對應(yīng)的MD5值也應(yīng)該是完全不同,完全不相關(guān)的;
- 5,不能再有意義的時(shí)間內(nèi)人工的構(gòu)造兩個(gè)不同的消息,使其具有相同的MD5值
3,md5
應(yīng)用舉例:文件的哈希值
算出文件的哈希值,若兩個(gè)文件的哈希值相同,則可認(rèn)為這兩個(gè)文件時(shí)相同的,因此:
- 1,用戶可以利用它來驗(yàn)證下載的文件是否完整
- 2,云存儲服務(wù)商可以利用它來判斷用戶要上次的文件是否已經(jīng)存在于服務(wù)器上,從而實(shí)現(xiàn)秒傳的功能,同時(shí)避免存儲過多相同的文件副本。
4,SHA2算法
歷史上MD5和SHA-1曾經(jīng)是使用最為廣泛的 cryptographic hash function,但是隨著密碼學(xué)的發(fā)展,這兩個(gè)哈希函數(shù)的安全性相繼受到了各種挑戰(zhàn)。
因此現(xiàn)在安全性較重要的場合推薦使用 SHA-A等新的更安全的哈希函數(shù)。
SHA-2包含了一系列的哈希函數(shù):SHA-224, SHA-256, SHA-384,SHA-512,SHA-512/224,SHA-512/256,其對應(yīng)的哈希值長度分別為 224, 256, 384 or 512位。
SHA-2 具有和 MD5 類似的性質(zhì)。
5,SHA2算法應(yīng)用舉例
例如在比特幣系統(tǒng)中,所有參與者需要共同解決如下問題:對于一個(gè)給定的字符串U,給定的目標(biāo)哈希值H,需要計(jì)算一個(gè)字符串V,使得 U+ V的哈希值與H的差小于一個(gè)給定值D。此時(shí),只能通過暴力枚舉V來進(jìn)行猜測。首先計(jì)算出結(jié)果的人可獲得一定獎金。而某人首先計(jì)算成功的概率與其擁有的計(jì)算量成正比,所以其獲得的獎金的期望值與其擁有的計(jì)算量成正比。
棧和隊(duì)列的應(yīng)用——迷宮問題
給出一個(gè)二維列表,表示迷宮(0表示通道,1表示圍墻)。給出算法,求一條走出迷宮的路徑。
將其轉(zhuǎn)化為數(shù)組如下:
那有兩種思路,一種是使用棧(深度優(yōu)先搜索)來存儲當(dāng)前路徑,也可以稱為回溯法。但是深度優(yōu)先有一個(gè)缺點(diǎn),就是雖然能找到路徑,但是不能保證是最短路徑,其優(yōu)點(diǎn)就是簡單,容易理解。
另一種方法就是隊(duì)列,
1,棧——深度優(yōu)先搜索
回溯法
思路:從一個(gè)節(jié)點(diǎn)開始,任意找下一個(gè)能走的點(diǎn),當(dāng)炸不到能走的點(diǎn)時(shí),退回上一個(gè)點(diǎn)尋找是否有其他方向的點(diǎn)。
使用棧存儲當(dāng)前路徑
如下圖所示:
代碼如下:
# _*_coding:utf-8_*_?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]]?# 這里我們封裝一個(gè)列表,用來表示走迷宮的四個(gè)方向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]? # 當(dāng)前節(jié)點(diǎn)是 stack最后一個(gè)位置????????if?curNode[0] == x2 and curNode[1] == y2:????????????return?True? # 如果有路則返回TRUE,沒有路則返回FALSE????????# x,y當(dāng)前坐標(biāo),四個(gè)方向上下左右分別是:x-1,y? x+1,y? x, y-1? x,y+1????????for?dir?in?dirs:????????????nextNode = dir(curNode[0], curNode[1])????????????# 如果下一個(gè)節(jié)點(diǎn)能走????????????if?maze[nextNode[0]][nextNode[1]] == 0:????????????????stack.append(nextNode)????????????????maze[nextNode[0]][nextNode[1]] = 2? # 2表示已經(jīng)走過了????????????????break????????else:? # 如果一個(gè)都找不到,就需要回退了????????????maze[nextNode[0]][nextNode[1]] = 2????????????stack.pop()? # 棧頂出棧,也就是回退?????else:????????print("沒有路")????????return?False??maze_path(1, 1, 8, 8)2,隊(duì)列——廣度優(yōu)先搜索
思路:從下一個(gè)節(jié)點(diǎn)開始,尋找所有接下來能繼續(xù)走的點(diǎn),繼續(xù)不斷尋找,直到找到出口。
使用隊(duì)列存儲當(dāng)前正在考慮的節(jié)點(diǎn)
思路圖如下:
或者這樣:
可以通過如下方式理解:
代碼如下:
# _*_coding:utf-8_*_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):????real_path = []? # 用來存儲真實(shí)的路徑????i = len(path) - 1? # 用i指向path的最后一個(gè)下標(biāo)????while?i >= 0:????????real_path.append(path[i][0:2])????????i = path[i][2]????real_path.reverse()?# 起點(diǎn)坐標(biāo)(x1, y1)? 終點(diǎn)坐標(biāo)(x2, y2)def maze_path_queue(x1, y1, x2, y2):????queue = deque()????path = []????# -1? 為當(dāng)前path的路徑的下標(biāo)????queue.append((x1, y1, -1))????while?len(queue) > 0:? # 當(dāng)隊(duì)列不空時(shí)循環(huán)????????# append是從右邊加入的,popleft()從左邊出去????????cur_Node = queue.popleft()????????path.append(cur_Node)????????if?cur_Node[0] == x2 and cur_Node[1] == y2:????????????# 如果當(dāng)前節(jié)點(diǎn)等于終點(diǎn)節(jié)點(diǎn),則說明到達(dá)終點(diǎn)????????????print_r(path)????????????return?True?????????# 隊(duì)列是找四個(gè)方向,每個(gè)能走就繼續(xù)????????for?dir?in?dirs:????????????# 下一個(gè)方向的節(jié)點(diǎn)坐標(biāo)????????????next_node = dir(cur_Node[0], cur_Node[1])????????????if?maze[next_node[0]][next_node[1]] == 0:????????????????queue.append((next_node[0], next_node[1], len(path) - 1))????????????????maze[next_node[0]][next_node[1]] = 2? # 標(biāo)記為已走過????return?False? # 如果隊(duì)列結(jié)束了,空了,還沒找到路徑,則為FALSE??maze_path_queue(1, 1, 8, 8)總結(jié)
以上是生活随笔為你收集整理的python常用代码_Python常用算法学习(4) 数据结构(原理+代码)-最全总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抖音下架课程虚拟商品:原因或为配合“苹果
- 下一篇: python中添加高斯噪声_关于pyth