what's the 數據結構


 

目錄

      

      隊列

      鏈表雙向鏈表

      哈希表

        二叉搜索樹


 

 

 

what's the 數據結構

  數據結構是指相互之間存在着一種或多種關系的數據元素的集合和該集合中數據元素之間的關系組成。 簡單來說,數據結構就是設計數據以何種方式組織並存儲在計算機中。 比如:列表、集合與字典等都是一種數據結構。 

  通常情況下,精心選擇的數據結構可以帶來更高的運行或者存儲效率。數據結構往往同高效的檢索算法和索引技術有關。

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

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

下面我來重點來學習數據結構中比較重要的幾種:棧、隊列、鏈表、哈希表、二叉搜索樹

  棧(Stack)是一個數據集合,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。可以將棧理解為只能在一端進行插入或刪除操作的列表。

  棧的特點:后進先出、先進后出(類似於往箱子里放東西,要拿的時候只能拿最上面的而最上面的是最后進的)

  棧操作:進棧push、出棧pop、取棧頂gettop(在Python中,不用自定義棧,直接用列表就行,進棧函數:append 出棧函數:pop 查看棧頂函數:li[-1])

棧的應用

  棧可以應用在處理括號匹配問題——括號匹配問題:給一個字符串,其中包含小括號、中括號、大括號,求該字符串中的括號是否匹配。

基本思路:按順序遍歷字符串是左括號則進棧,來的是右括號則將棧頂左括號pop,若來的右括號與棧頂左括號不匹配或空棧情況下來了右括號則返回錯誤信息

def brace_match(s):
    stack = []
    match = {')':'(', ']':'[', '}':'{'}
    match2 = {'(':')', '[':']', '{':'}'}
    for ch in s:
        if ch in {'(', '[', '{'}:
            stack.append(ch)#左括號入棧
        elif len(stack) == 0:
            print("缺少%s" % match[ch])#棧空了,但卻來了一個右括號
            return False
        elif stack[-1] == match[ch]:#棧頂左括號與來的右括號相匹配
            stack.pop()#出棧
        else:
            print("括號不匹配")
            return False
    if len(stack) > 0:#全部操作完后棧內還有剩余左括號
        print("缺少%s" % (match2[stack[-1]]))#顯示棧頂左括號對應的右括號
        return False
    print('合法')
    return True


brace_match("[{()[]}{}{}")#缺少]

隊列

  隊列(Queue)是一個數據集合,僅允許在列表的一端進行插入,另一端進行刪除。 進行插入的一端稱為隊尾(rear),插入動作稱為進隊或入隊;進行刪除的一端稱為隊頭(front),刪除動作稱為出隊。和棧一樣,隊列是一種操作受限制的線性表。

  隊列的性質:先進先出(可以將隊列理解為排隊買東西)

  特殊情況——雙向隊列:隊列的兩端都允許進行進隊和出隊操作。

如何用列表實現隊列:

  1. 初步設想:列表+兩個下標指針
  2. 創建一個列表和兩個變量,front變量指向隊首,rear變量指向隊尾。初始時,front和rear都為0。
  3. 進隊操作:元素寫到li[rear]的位置,rear自增1。
  4. 出隊操作:返回li[front]的元素,front自減1。

以上就是隊列實現的基本思路,但是其實隊列的實現原理是一個環形隊列

環形隊列:當隊尾指針front == Maxsize + 1時,再前進一個位置就自動到0。

  • 實現方式:求余數運算
  • 隊首指針前進1:front = (front + 1) % MaxSize
  • 隊尾指針前進1:rear = (rear + 1) % MaxSize
  • 隊空條件:rear == front
  • 隊滿條件:(rear + 1) % MaxSize == front

 

在Python中,有一個內置模塊可以幫我們快速建立起一個隊列——deque模塊

"""
使用方法:from collections import deque
創建隊列:queue = deque(li)
進隊:append()
出隊:popleft()
雙向隊列隊首進隊:appendleft()
雙向隊列隊尾進隊:pop()
"""

 

棧和隊列的知識點應用:

  用棧和隊列的知識來解決迷宮問題:http://www.cnblogs.com/zhuminghui/p/8414307.html

鏈表

  鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的 指針鏈接次序實現的。鏈表由一系列 結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。
  每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。 相比於線性表順序結構,操作復雜。由於不必須按順序存儲,鏈表在插入的時候可以達到O(1)的復雜度,比另一種線性表順序表快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而線性表和順序表相應的時間復雜度分別是O(logn)和O(1)。
  使用鏈表結構可以克服數組鏈表需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但是鏈表失去了數組隨機讀取的優點,同時鏈表由於增加了結點的指針域,空間開銷比較大。鏈表最明顯的好處就是,常規數組排列關聯項目的方式可能不同於這些數據項目在記憶體或磁盤上順序,數據的存取往往要在不同的排列順序中轉換。鏈表允許插入和移除表上任意位置上的節點,但是不允許隨機存取。鏈表有很多種不同的類型:單向鏈表,雙向鏈表以及循環鏈表。鏈表可以在多種編程語言中實現。像Lisp和Scheme這樣的語言的內建數據類型中就包含了鏈表的存取和操作。程序語言或面向對象語言,如C,C++和Java依靠易變工具來生成鏈表。

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

#結點的定義
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None

  建立鏈表的方式有頭插法和尾插法兩種

  • 頭插法:在一個結點的前面插入元素,head的指針由指向原來的結點變為指向新元素,新元素的指針指向原來的結點
  • 尾插法:在一個元素后面插入一個元素,原來結點的指針指向新元素

建立列表實現代碼如下:

#定義結點
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
#頭插法
def create_linklist(li):
    head = None
    for num in li:
        node = Node(num)
        node.next = head
        head = node
    return head
#尾插法
def create_linklist_tail(li):
    head = None
    if not li:
        return head
    head = Node(li[0])
    tail = head
    for num in li[1:]:
        node = Node(num)
        tail.next = node
        tail = node
    return head


def print_linklist(head):
    node = head
    while node:
        print(node.data)
        node = node.next

linklist = create_linklist_tail([1,2,3,4])
print_linklist(linklist)

 

鏈表結點的插入
  鏈表插入結點的操作的重點是指針的變換,首先我們有兩個結點A指向B,這時要在AB中間插入C,我們需要將C的指針指向B,然后將A的指針指向C,在刪除AB之間的指針,就完成了C的插入,由AB變為了ACB
                
#curNode為A結點
c.next = curNode.next
curNode.next = c

 

鏈表結點的刪除
  在鏈表中,要刪除一個結點不能直接刪掉就萬事大吉,我們需要將指向該結點的結點的指針指向該結點指針指向的結點(A指向B指向C,B為要刪除的該結點,將A的指針指向C),然后才能刪除該節點(B)
                 
#p為要刪除的結點
p = curNode.next
curNode.next = curNode.next.next#即p.next
del p
鏈表的特殊形態——雙鏈表
  雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有 兩個指針,分別指向直接后繼(后面結點)和直接前驅(前面結點)。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和后繼結點。一般我們都構造雙向循環鏈表。 
雙向鏈表的節點定義:
class Node(object):
    def __init__(self, item=None):
        self.item = item
        self.next = None
        self.prior = None

雙向鏈表結點的插入

  與鏈表相同,雙向鏈表插入結點也需要將指針進行變換。同樣是AB之間要插入C,我們需要先將C的指針指向B、B的指針由指向A轉變為指向B,然后C的另一個指針指向A,A結點的指針由指向B轉變為指向B。
    
#p為新插入的元素
p.next = curNode.next
curNode.next.prior = p
p.prior = curNode
curNode.next = p

 

雙向鏈表結點的刪除
  刪除雙向鏈表的結點前需要建立起該結點前后兩個結點的指針關系,然后才能刪除結點
#p為要刪除的結點
p = curNode.next
curNode.next = p.next
p.next.prior = curNode
del p

 

鏈表的復雜度分析
  • 按元素值查找——O(n),因為沒有下標所以沒法做二分
  • 按下標查找——O(n),因為沒有下標
  • 在某元素后插入——O(1)
  • 刪除某元素——O(1)
哈希表 

  哈希表(Hash Table,又稱為散列表),是一種線性表的存儲結構。哈希表由一個順序表(數組)和一個哈希函數組成。哈希函數h(k)將元素k作為自變量,返回元素的存儲下標。

  假設有一個長度為7的數組,哈希函數h(k)=k%7。元素集合{14,22,3,5}的存儲方式如下圖。

哈希沖突:

  由於哈希表的大小是有限的,而要存儲的值的總數量是無限的,因此對於任何哈希函數,都會出現兩個不同元素映射到同一個位置上的情況,這種情況叫做哈希沖突。

比如上圖中的哈希表就存在這哈希沖突——h(k)=k%7, h(0)=h(7)=h(14)=...

解決哈希沖突方法
方法一: 開放尋址法——如果哈希函數返回的位置已經有值,則可以向后探查新的位置來存儲這個值。
  • 線性探查:如果位置i被占用,則探查i+1, i+2,……
  • 二次探查:如果位置i被占用,則探查i+12,i-12,i+22,i-22,……
  • 二度哈希:有n個哈希函數,當使用第1個哈希函數h1發生沖突時,則嘗試使用h2,h3,……
方法二: 拉鏈法——哈希表每個位置都連接一個鏈表,當沖突發生時,沖突的元素將被加到該位置鏈表的最后。

哈希表在Python中的應用

#字典與集合都是通過哈希表來實現的
#在Python中的字典:
a = {'name': 'Damon', 'age': 18, 'gender': 'Man'}
#使用哈希表存儲字典,通過哈希函數將字典的鍵映射為下標。假設h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,則哈希表存儲為[None, 18, None, ’Damon’, ‘Man’]
#在字典鍵值對數量不多的情況下,幾乎不會發生哈希沖突,此時查找一個元素的時間復雜度為O(1)。
二叉搜索樹 
  二叉搜索樹知識點地址: http://www.cnblogs.com/zhuminghui/p/8409508.html
 
 
 
 
 
 
                           


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM