數據結構與算法之線性結構

什麼是數據結構

數據結構是指相互之間存在着一種或多種關係的數據元素的集合和該集合中數據元素之間的關係的組成。python

  • 數據結構就是設計數據以何種方式存儲在計算機中,列表、字典等都算是數據結構。linux

  • 程序=數據結構+算法,數據結構屬於靜態的部分,算法的調用爲動態部分算法

數據結構的分類

根據邏輯結構劃分:編程

  • 線性結構:數據結構中的元素一對一的關係,一前驅,一後繼。
  • 樹結構:數據結構中元素一對多的關係,一前驅,多後繼。
  • 圖結構:數據結構中元素存在多對多的關係,多前驅,多後繼,我也不會。
    • 判斷一個圖形能不能一筆畫完,就判斷它的奇數度節點數目是否爲0或2.這種能一筆畫完的就是歐拉圖,奇數度節點爲四個,就是兩筆畫完。

線性結構

列表

列表和數組

python中的列表和其餘語言中的數組很類似,區別爲:數組

  • 數組是定長的。
  • 數組的數據類型也必須一致。
  • 對列表或數組來講,它們的下標操做是最快的。

列表解決的變長問題的方式

  • 假設一開始在內存中分配了四個元素存儲的空間,那麼前四個元素的append操做不會出現問題。
  • 當第五次append操做時,會先在內存中分配一個可以存儲八個元素的空間,也就是翻倍。
  • 而後進行復制,把之前的四個元素依次放到相應的位置上。
  • 若再次超出長度,則繼續執行上述操做。
  • 也就是使用了動態表的原理

append操做會不會使速度變慢?安全

  • 根據攤還分析,沒有變長時的append和變長時的append均攤,最後的複雜度時O(3).
  • append越日後,變長時的出現頻率就會越小
  • 浪費了一部分空間,最壞狀況應該是浪費了長度除二減一的空間。

列表解決多數據類型問題的方式

  • 對於純整數的數組,它的每個元素佔4個字節,那麼就事先計算好內存分配的大小,計算方法爲:- 第一個元素的地址+元素個數 乘 4
  • python的列表裏存的不是值,而是指向這個值的內存地址。
  • 地址的大小是同樣的,32位裏地址是4個字節,64位裏地址是8個字節。
  • 這種方法的缺點是內存開銷翻倍,這也是python被人詬病的地方。

相關知識點

老是能聽到一個詞 堆棧 ,堆(heap)和棧(stack)是兩個東西,傳統的編程語言中把內存分爲兩個地方,堆空間和棧空間,堆存儲的是一些動態生成的對象,與數據結構中的堆是不一樣的,棧空間由系統調用,存放函數的參數值,局部變量的值。
應該是早年間翻譯的問題,通常聽到堆棧指的就是棧。數據結構

  • 棧是一個數據集合,能夠理解爲只能在一端進行插入和刪除操做的列表。
  • 棧的特色:後進先出(last-in,first-out)
    • 棧頂:操做永遠在棧頂。
    • 棧底:最後一個元素。
  • 棧的基本操做:
    • 進棧(壓棧):push
    • 出棧:pop
    • 取棧頂: gettop
  • 關於出棧順序的問題:
    • 對於某個元素,若是進展順序在它前面的元素出棧時在它後面,那麼前面的元素順序是相反的。
    • 不知道說的明不明白
    • 卡特蘭數,n個數的出棧順序,就是卡特蘭數的第n項。

棧的應用--括號匹配問題

  • 給定一個字符串,問其中字符串是否匹配。
  • 括號自己知足棧的性質
  • 匹配失敗的狀況:
    • 括號不匹配
    • 匹配完畢棧沒空
    • 棧空了又進元素
     
    1. def brace_match(s):app

    2. stack = []編程語言

    3. d ={'(':')','[':']','{':'}'}函數

    4. for ch in s:

    5. if ch in {'(','[','{'}:

    6. stack.append(ch)

    7. elif len(stack):

    8. print('多了%s' %ch)

    9. return False

    10. elif d[stack[-1]] == ch:

    11. stack.pop()

    12. else:

    13. print('%s不匹配'%ch)

    14. if len(stack)==0:

    15. return True

    16. else:

    17. print("未匹配")

    18. return False

隊列

相關知識點:

隊列是一個數據集合,僅容許在列表的一端插入,另外一端刪除。

  • 進行插入的時隊尾,進行刪除操做的是隊首,插入和刪除操做也被稱爲進隊(push)和出隊(pop)。
  • 隊列的性質:先進先出(first-in,first-out)
  • 雙向隊列:兩邊都能進行插入刪除操做的隊列。

隊列的數組實現:

  • 簡單的pop(0)操做複雜度太高,不採用。
  • 因爲數組定長,不能繼續添加數據,若是是列表,出隊的操做就會出現空位,因此想辦法讓數組變成一個圓環。

  • 設置兩個指針,隊首指針front,隊尾指針rear。
  • 因爲,隊列滿的時候和隊列空的時候rear和front都在一個位置,那麼就沒法判斷了。因而設置成隊列滿的時候減去一作爲隊滿的標誌。
  • 這種隊列就叫作環形隊列。
    • 當隊尾指針front=最大長度+1時,再前進一個位置就自動到0.
    • 實現方式:求餘數運算
      • 隊首指針前進1:front=(www.tiaotiaoylzc.com front+1)%maxsize
      • 隊尾指針前進1:rear=(www.yongshi123.cn rear+1)%maxsize
      • 隊空條件:rear=www.yongshiyule178.com front
      • 隊滿條件:(rear+1)www.dfgjpt.com%maxsize=front

經過兩個棧作一個隊列的方法

  • 1號棧進棧 模擬進隊操做。
  • 2號站出棧,若是2號棧空,把1號站依次出棧並進2號棧,模擬出隊操做。
  • 經過攤還分析,時間複雜度仍是O(1)。

python關於隊列的模塊

 
  1. import queue #涉及線程安全用queue

  2. from collections import deque #經常使用解題的用deque

  3.  
  4. q = deque() #是一種雙向隊列,popleft出隊

  5.  
  6. #模擬linux命令 head和tail,假如是tail 5

  7. deque(open('a.text','r',encooding='utf8'),5)

  8. #創建一個定長的隊列,當隊列滿了以後,就會刪除第一行,繼續添加

鏈表

相關知識點:

鏈表就是非順序表,與隊列和棧對應。

  • 鏈表中每個元素都是一個對象,每一個對象稱爲一個節點,包含有數據域key和指向下一個節點的next,經過各個節點之間的相互鏈接,最終串聯成一個鏈表。

  • 在機械硬盤中,文件就是以鏈表的形式存儲的。
  • 以FAT32爲例,文件的單位是文件塊(block),一個文件塊的大小是4k,一個文件的內容是由鏈表的方式鏈接文件塊組成的。
  • 鏈表的第一個節點被稱爲頭節點,數據能夠是空的,也能夠有值。
  • 頭節點爲空也是爲了表示空鏈表,也叫作帶空節點的鏈表,頭節點也能夠記錄鏈表的長度

節點定義

 
  1. class Node(object):

  2. def __init__(self,item):

  3. self.item=item

  4. self.next=None

  5. #eg

  6. a=Node(1)

  7. b=Node(2)

  8. c=Node(3)

  9. a.next=b

  10. b.next=c #鏈表的最後一個節點的next就爲None

鏈表類的實現

 
  1. class LinkList:

  2. def __init___(self,li,method='tail'):

  3. self.head = None

  4. self.tail = None

  5. if method == 'head':

  6. self.create_linklist_head(li)

  7. if method == 'tail'

  8. self.create_linklist_tail(li)

  9. else:

  10. rais ValueError('unsupport')

  11.  
  12. #頭插法

  13. def create_linklist_head(self,li):

  14. self.head = Node(0)

  15. for v in li:

  16. n = Node(v)

  17. n.next = l.next #當插入下一個元素時,應該與下一個節點鏈接後再跟頭節點鏈接

  18. self.head.next = n

  19. self.head.data += 1

  20.  
  21. #尾插法

  22. def create_linlist_tail(self,li):

  23. self.head = Node(0)

  24. self.tail = self.head

  25. for v in li:

  26. p = Node(v)

  27. self.tail.next = p

  28. self.tail = p

  29. self.head.data += 1

  30.  
  31. #鏈表的遍歷輸出

  32. def traverse_linlist(self):

  33. p = self.head.next

  34. while p:

  35. yield p.data

  36. p = p.next

插入刪除總結

  • 插入
 
  1. #p表示待插入節點,curNode表示當前節點

  2. p.next = curNode.next #不能當前鏈接直接斷開

  3. curNode,next = p

  • 刪除
 
  1. p = curNode.next

  2. curNode.next = p.next

  3. del p #不寫也同樣,引用計數,python的內存回收機制

雙鏈表

雙鏈表中每一個節點有兩個指針:一個指向後面節點、一個指向前面節點。
節點定義:

 
  1. class Node(object):

  2. def __init__(self, item=None):

  3. self.item = item

  4. self.next =www.myzx1.com None

  5. self.prior = None

雙鏈表的插入和刪除

  • 插入
 
  1. p.next = curNode.next

  2. curNode.www.ycjszpgs.com next.prior = p

  3. p.prior =www.dfzx157.com curNode

  4. curNode.next = p

  • 刪除
 
  1. p = curNode.next

  2. curNode.next = p.next

  3. p.next.prior = curNode

  4. del p

鏈表的複雜度分析

鏈表與列表相比

  • 按元素值查找:列表可使用二分法是O(logn),鏈表是O(n)
  • 按下標查找:O(1),O(n)
  • 再某元素後插入:O(n),O(1)
  • 刪除莫元素:O(n),O(1)
    總的來講鏈表再插入和刪除某元素的操做時明顯快於順序表,並且經過雙鏈表能夠更容易實現棧和隊列。

哈希表

直接尋址表

哈希表就是直接尋址表的改進。當關鍵字的全域U比較小時,直接尋址是一種簡單有效的方法。

  • 全域的意思就是它的取值範圍。
  • 也就是直接把關鍵字爲key的value放在key的位置上
    直接尋址的缺點:
  • 當域U很大時,須要消耗大量內存。
  • 若是U很大,但關鍵字不多,浪費大量空間。
  • 若關鍵字不是數字則沒法處理。
    直接尋址表的改進:
  • 構建大小爲m的尋址表T
  • key爲k的元素放到h(k)上
  • h(k)是一個函數,其將域U映射到表T(0,1,..,m-1)

哈希表

哈希表是一個經過哈希函數計算數據存儲位置的線性表的存儲結構,又叫作散列表。

  • 哈希表由一個直接尋址表和一個哈希函數組成。
  • 哈希函數h(k)將元素關鍵字k做爲自變量,返回元素的存儲下標。
  • 哈希表的基本操做:
    • insert(key,value):插入鍵值對。
    • get(key):若是存在鍵爲key的鍵值對則返回其value。
    • delete(key):刪除鍵爲key的鍵值對。

簡單哈希函數

  • 除法哈希:h(k)= k mod m
  • 乘法哈希:h(k) = floor(m(KA mod 1)) 0<A<1

哈希衝突

因爲哈希表的大小是有限的,而要存儲信息的數量是無限的,所以,對於任何哈希函數,都會出現兩個元素映射到同一個位置的狀況,這種狀況就叫作哈希衝突。
解決哈希衝突的方法:
開放尋址法:若是哈希函數返回的位置已經有值,則能夠向後探查新的位置來儲存這個值。

  • 線性探查:若是位置p被佔用,則探查 p+1,p+2....
  • 二次探查:若是位置p被佔用,則探查p+1**2,p-1**2,p+2**2
  • 二度哈希:有n個哈希函數,當使用第一個哈希函數h1發生衝突時,則使用h2。
  • 哈希表的快速查找能夠以空間換時間,須要保證元素個數除以數組容積小於0.5,這個比值就是裝載率。
    拉鍊法:哈希表的每一個位置都鏈接一個鏈表,當衝突發生時,衝突的元素被加到該位置鏈表的最後。
  • 拉鍊表須要保證每個鏈表的長度都不要太長。
  • 拉鍊法的裝載率是能夠大於一的。
  • 插入、查找等操做的時間複雜度是O(1)的。

哈希在python中的應用

  • 字典和集合都是經過哈希表來實現的
  • 集合能夠看做沒有value的字典,由於集合也有不重複的性質。
  • 經過哈希函數把字典的鍵映射爲函數:
 
  1. dic = {'name':'cui'}

  2. #能夠認爲是h('name')=1,則哈希表爲[None,'cui

相關文章
相關標籤/搜索