數據結構&算法(一)_堆、棧(堆棧)、隊列、鏈表


堆:

①堆通常是一個可以被看做一棵樹的數組對象。堆總是滿足下列性質:

   ·堆中某個節點的值總是不大於或不小於其父節點的值;

   ·堆總是一棵完全二叉樹。將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。

②堆是在程序運行時,而不是在程序編譯時,申請某個大小的內存空間。即動態分配內存,對其訪問和對一般內存的訪問沒有區別。

③堆是應用程序在運行的時候請求操作系統分配給自己內存,一般是申請/給予的過程。

④堆是指程序運行時申請的動態內存,而棧只是指一種使用堆的方法(即先進后出)。

堆的應用:

#堆排序
def sift(li, left, right):
    i = left
    j = 2 * i + 1
    tmp = li[left]
    while j <= right:
        if j+1 <= right and li[j] < li[j+1]:
            j = j + 1
        if tmp < li[j]:
            li[i] = li[j]
            i = j
            j = 2 * i + 1
        else:
            break
    li[i] = tmp



def heap_sort(li):
    n = len(li)
    for i in range(n//2-1, -1, -1): #建立堆
        sift(li, i, n-1)

    for i in range(n-1, -1, -1):   #挨個出數
        li[0], li[i] = li[i],li[0]
        sift(li, 0, i-1)

li = [6,8,1,9,3,0,7,2,4,5]
heap_sort(li)
print(li)

  

棧:

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

②棧就是一個桶,后放進去的先拿出來,它下面本來有的東西要等它出來之后才能出來(先進后出)

③棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有FIFO的特性,在編譯的時候可以指定需要的Stack的大小。

棧的基本操作:
  進棧(壓棧):push
  出棧:pop
  取棧頂:gettop

用python實現堆棧

# 后進先出
class Stack():
    def __init__(self,size):
        self.size=size
        self.stack=[]
        self.top=-1

    def push(self,x):
        if self.isfull():
            raise exception("stack is full")
        else:
            self.stack.append(x)
            self.top=self.top+1

    def pop(self):
        if self.isempty():
            raise exception("stack is empty")
        else:
            self.top=self.top-1
            self.stack.pop()

    def isfull(self):
        return self.top+1 == self.size
    def isempty(self):
        return self.top == '-1'
    def showStack(self):
        print(self.stack)

s=Stack(10)
for i in range(5):
    s.push(i)
s.showStack()
for i in range(3):
    s.pop()
s.showStack()

"""
類中有top屬性,用來指示棧的存儲情況,初始值為1,一旦插入一個元素,其值加1,利用top的值樂意判定棧是空還是滿。
執行時先將0,1,2,3,4依次入棧,然后刪除棧頂的前三個元素
"""

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

括號匹配問題:給一個字符串,其中包含小括號、中括號、大括號,求該字符串中的括號是否匹配。例如:
()()[]{}    匹配
([{()}])    匹配
[](          不匹配
[(])        不匹配

def kuohao_match(exp):
    stack = []
    di = {'(':')', '{':'}', '[':']'}
    for c in exp:
        if c in {'(','{', '['}:
            stack.append(c)
        else:
            if len(stack) == 0:
                return False
            top = stack.pop()
            if di[top] != c:
                return False
    if len(stack) > 0:
        return False
    else:
        return True

print(kuohao_match('()[]{([]][]}()'))
棧的應用——括號匹配問題

棧的應用——迷宮問題

 

'''
解決思路
在一個迷宮節點(x,y)上,可以進行四個方向的探查:maze[x-1][y], maze[x+1][y], maze[x][y-1], maze[x][y+1]
思路:從一個節點開始,任意找下一個能走的點,當找不到能走的點時,退回上一個點尋找是否有其他方向的點。
方法:創建一個空棧,首先將入口位置進棧。當棧不空時循環:獲取棧頂元素,尋找下一個可走的相鄰方塊,如果找不到可走的相鄰方塊,說明當前位置是死胡同,進行回溯(就是講當前位置出棧,看前面的點是否還有別的出路)

'''

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,1,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 mpath(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
        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]] = -1  # 標記為已經走過,防止死循環
                break
        else:#四個方向都沒找到
            maze[curNode[0]][curNode[1]] = -1  # 死路一條,下次別走了
            stack.pop() #回溯
    print("沒有路")
    return False

mpath(1,1,8,8)
棧的應用__迷宮問題

 

隊列

  • 隊列(Queue)是一個數據集合,僅允許在列表的一端進行插入,另一端進行刪除。
  •   進行插入的一端稱為隊尾(rear),插入動作稱為進隊或入隊
  •   進行刪除的一端稱為隊頭(front),刪除動作稱為出隊
  • 隊列的性質:先進先出(First-in, First-out)
  • 雙向隊列:隊列的兩端都允許進行進隊和出隊操作

 

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

 隊列的實現原理

 

 

 

 

 

隊列的實現原理:環形隊列

隊列的應用:

'''
思路:從一個節點開始,尋找所有下面能繼續走的點。繼續尋找,直到找到出口。
方法:創建一個空隊列,將起點位置進隊。在隊列不為空時循環:出隊一次。如果當前位置為出口,則結束算法;否則找出當前方塊的4個相鄰方塊中可走的方塊,全部進隊。

'''

from collections import deque

mg = [
    [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,1,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_p(path):
    curNode = path[-1]
    realpath = []
    print('迷宮路徑為:')
    while curNode[2] != -1:
        realpath.append(curNode[0:2])
        curNode = path[curNode[2]]
    realpath.append(curNode[0:2])
    realpath.reverse()
    print(realpath)

def mgpath(x1, y1, x2, y2):
    queue = deque()
    path = []
    queue.append((x1, y1, -1))
    mg[x1][y1] = -1
    while len(queue) > 0:
        curNode = queue.popleft()
        path.append(curNode)
        if curNode[0] == x2 and curNode[1] == y2:
            #到達終點
            # for i,j,k in path:
            #     print("(%s,%s) %s"%(i,j,k))
            print_p(path)
            return True
        for dir in dirs:
            nextNode = dir(curNode[0], curNode[1])
            if mg[nextNode[0]][nextNode[1]] == 0:  # 找到下一個方塊
                queue.append((nextNode[0], nextNode[1], len(path) - 1))
                mg[nextNode[0]][nextNode[1]] = -1  # 標記為已經走過
    return False


mgpath(1,1,8,8)
隊列的應用--隊列解決迷宮問題

 

鏈表

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

節點的定義:

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

頭節點:

鏈表的遍歷:

#遍歷鏈表
def traversal(head):
    curNode = head #臨時用指針
    while cueNode is not None:
        print(curNode.data)
        curNode = curNode.Next

  

 

 鏈表節點的插入和刪除

插入:
p.next = curNode.next
curNode.next = p

刪除:
p = curNode.next
curNode.next = curNode.next.next
del p

  

建立鏈表

#頭插法:
def createLinkListF(li):
    l = Node()    
    for num in li:      
        s = Node(num)        
        s.next = l.next       
        l.next = s    
    return l

#尾插法
def createLinkListR(li):
    l = Node()
    r = l       #r指向尾節點
    for num in li:
        s = Node(num)
        r.next = s
        r = s

雙鏈表

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

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

雙鏈表的插入和刪除

#插入:
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

  

建立雙鏈表

def createLinkListR(li):
    l = Node()
    r = l
    for num in li:
        s = Node(num)
        r.next = s
        s.prior = r
        r = s
    return l, r

  

  

棧與隊列的異同

棧(Stack)和隊列(Queue)是兩種操作受限的線性表。

(線性表:線性表是一種線性結構,它是一個含有n≥0個結點的有限序列,同一個線性表中的數據元素數據類型相同並且滿足“一對一”的邏輯關系。

“一對一”的邏輯關系指的是對於其中的結點,有且僅有一個開始結點沒有前驅但有一個后繼結點,有且僅有一個終端結點沒有后繼但有一個前驅結點,其它的結點都有且僅有一個前驅和一個后繼結點。)

這種受限表現在:棧的插入和刪除操作只允許在表的尾端進行(在棧中成為“棧頂”),滿足“FIFO:First In Last Out”;隊列只允許在表尾插入數據元素,在表頭刪除數據元素,滿足“First In First Out”。

棧與隊列的相同點:

1.都是線性結構。

2.插入操作都是限定在表尾進行。

3.都可以通過順序結構和鏈式結構實現。、

4.插入與刪除的時間復雜度都是O(1),在空間復雜度上兩者也一樣。

5.多鏈棧和多鏈隊列的管理模式可以相同。

棧與隊列的不同點:

1.刪除數據元素的位置不同,棧的刪除操作在表尾進行,隊列的刪除操作在表頭進行。

2.應用場景不同;常見棧的應用場景包括括號問題的求解,表達式的轉換和求值,函數調用和遞歸實現,深度優先搜索遍歷等;常見的隊列的應用場景包括計算機系統中各種資源的管理,消息緩沖器的管理和廣度優先搜索遍歷等。

3.順序棧能夠實現多棧空間共享,而順序隊列不能。

 


免責聲明!

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



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