1. 程式人生 > >python---資料結構和演算法複習

python---資料結構和演算法複習

連結串列和二叉樹

# 1、連結串列反轉

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


def nonrecurse(head):  # 迴圈的方法反轉連結串列
    if head is None or head.next is None:
        return head
    pre = None
    cur = head
    h = head
    while cur:
        h = cur
        tmp 
= cur.next cur.next = pre pre = cur cur = tmp return h head = ListNode(1) # 測試程式碼 p1 = ListNode(2) # 建立連結串列1->2->3->4->None; p2 = ListNode(3) p3 = ListNode(4) head.next = p1 p1.next = p2 p2.next = p3 p = nonrecurse(head) # 輸出連結串列 4->3->2->1->None
while p: print(p.val) p = p.next # 2、連結串列排序 # 快排法 def sortList2(self, head): # write your code here if head is None or head.next is None: return head pivot = head p = pivot l1 = ListNode(0) l2 = ListNode(0) s = l1 f
= l2 tmp = head.next while tmp is not None: if tmp.val < pivot.val: s.next = tmp s = s.next elif tmp.val == pivot.val: p.next = tmp p = p.next else: f.next = tmp f = f.next tmp = tmp.next s.next = None f.next = None p.next = None l3 = self.sortList(l1.next) l4 = self.sortList(l2.next) if l3 is not None: l5 = l3 while l5.next is not None: l5 = l5.next l5.next = pivot p.next = l4 return l3 else: p.next = l4 return pivot # 3、連結串列去重 class Solution: """ @param head: A ListNode @return: A ListNode """ def deleteDuplicates(self, head): if head is None: return head record = {[head.val]} cur, pre = head.next, head while cur: if cur.val in record: pre.next = cur.next cur = cur.next else: record.add(cur.val) pre = pre.next cur = cur.next return head # 4、連結串列是否閉環 class Node(): # 定義一個Node類,構造兩個屬性,一個是item節點值,一個是節點的下一個指向 def __init__(self, item=None): self.item = item self.next = None def findbeginofloop(head): # 判斷是否為環結構並且查詢環結構的入口節點 slowPtr = head # 將頭節點賦予slowPtr fastPtr = head # 將頭節點賦予fastPtr loopExist = False # 預設環不存在,為False if head == None: # 如果頭節點就是空的,那肯定就不存在環結構 return False while fastPtr.next != None and fastPtr.next.next != None: # fastPtr的下一個節點和下下個節點都不為空 slowPtr = slowPtr.next # slowPtr每次移動一個節點 fastPtr = fastPtr.next.next # fastPtr每次移動兩個節點 if slowPtr == fastPtr: # 當fastPtr和slowPtr的節點相同時,也就是兩個指標相遇了 loopExist = True print("存在環結構") break if loopExist == True: slowPtr = head while slowPtr != fastPtr: fastPtr = fastPtr.next slowPtr = slowPtr.next return slowPtr print("不是環結構") return False if __name__ == "__main__": node1 = Node(1) node2 = Node(2) node3 = Node(3) node4 = Node(4) node5 = Node(5) node1.next = node2 node2.next = node3 node3.next = node4 node4.next = node5 node5.next = node2 print(findbeginofloop(node1).item) # 5、合併兩個有序連結串列 class ListNode: def __init__(self, x): self.val = x self.next = None class Solution: def mergeTwoLists(self, l1, l2): """ :type l1: ListNode :type l2: ListNode :rtype: ListNode """ head = ListNode(0) first = head while l1 != None and l2 != None: if l1.val > l2.val: head.next = l2 l2 = l2.next else: head.next = l1 l1 = l1.next head = head.next if l1 == None: head.next = l2 elif l2 == None: head.next = l1 return first.next # 6、求出連結串列倒數第k個值 def FindKthToTail(self, head, k): # write code here res = [] # 用列表來代替棧 while head: res.append(head) head = head.next if k > len(res) or k < 1: return None return res[-k] # 7、刪除連結串列的當前節點 def removeElements(self, head, val): """ :type head: ListNode :type val: int :rtype: ListNode """ if head: while head.val == val: head = head.next if head is None: return head q = head p = q.next while p: if p.val == val: q.next = p.next else: q = q.next p = p.next return head # 8、連結串列的中間節點 # 快慢指標,快指標走完,慢指標在中間位置 def midoflist(self, pHead): fast, slow = pHead, pHead while fast and fast.next: # 這個條件很重要,如果設定為fast.next的話會出現Nonetype沒有next這個錯誤。 # fast 在前,如果fast已經為None,那麼,fast.next也一定不存在 fast = fast.next.next slow = slow.next print(slow.val) # 9、在一個有環連結串列中找到環的入口 # 一個從相交的地方走,一個從頭走,當相遇的時候,這個相遇的節點就是入口 def circlestart(self, pHead): fast, slow = pHead, pHead while fast and fast.next: fast = fast.next.next slow = slow.next if (fast == slow): break slow = pHead while fast != slow: fast = fast.next slow = slow.next print(fast.val) return fast # 二叉樹 class Node(object): """節點類""" def __init__(self, elem=-1, lchild=None, rchild=None): self.elem = elem self.lchild = lchild self.rchild = rchild class Tree(object): """樹類""" def __init__(self): self.root = Node() self.myQueue = [] def add(self, elem): """為樹新增節點""" node = Node(elem) if self.root.elem == -1: # 如果樹是空的,則對根節點賦值 self.root = node self.myQueue.append(self.root) else: treeNode = self.myQueue[0] # 此結點的子樹還沒有齊。 if treeNode.lchild == None: treeNode.lchild = node self.myQueue.append(treeNode.lchild) else: treeNode.rchild = node self.myQueue.append(treeNode.rchild) self.myQueue.pop(0) # 如果該結點存在右子樹,將此結點丟棄。 def front_digui(self, root): """利用遞迴實現樹的先序遍歷""" if root == None: return print(root.elem) self.front_digui(root.lchild) self.front_digui(root.rchild) def middle_digui(self, root): """利用遞迴實現樹的中序遍歷""" if root == None: return self.middle_digui(root.lchild) print(root.elem) self.middle_digui(root.rchild) def later_digui(self, root): """利用遞迴實現樹的後序遍歷""" if root == None: return self.later_digui(root.lchild) self.later_digui(root.rchild) print(root.elem) def front_stack(self, root): """利用堆疊實現樹的先序遍歷""" if root == None: return myStack = [] node = root while node or myStack: while node: #從根節點開始,一直找它的左子樹 print(node.elem) myStack.append(node) node = node.lchild node = myStack.pop() #while結束表示當前節點node為空,即前一個節點沒有左子樹了 node = node.rchild #開始檢視它的右子樹 def middle_stack(self, root): """利用堆疊實現樹的中序遍歷""" if root == None: return myStack = [] node = root while node or myStack: while node: #從根節點開始,一直找它的左子樹 myStack.append(node) node = node.lchild node = myStack.pop() #while結束表示當前節點node為空,即前一個節點沒有左子樹了 print(node.elem) node = node.rchild #開始檢視它的右子樹 def later_stack(self, root): """利用堆疊實現樹的後序遍歷""" if root == None: return myStack1 = [] myStack2 = [] node = root myStack1.append(node) while myStack1: #這個while迴圈的功能是找出後序遍歷的逆序,存在myStack2裡面 node = myStack1.pop() if node.lchild: myStack1.append(node.lchild) if node.rchild: myStack1.append(node.rchild) myStack2.append(node) while myStack2: #將myStack2中的元素出棧,即為後序遍歷次序 print(myStack2.pop().elem) def level_queue(self, root): """利用佇列實現樹的層次遍歷""" if root == None: return myQueue = [] node = root myQueue.append(node) while myQueue: node = myQueue.pop(0) print(node.elem) if node.lchild != None: myQueue.append(node.lchild) if node.rchild != None: myQueue.append(node.rchild) if __name__ == '__main__': """主函式""" elems = range(10) #生成十個資料作為樹節點 tree = Tree() #新建一個樹物件 for elem in elems: tree.add(elem) #逐個新增樹的節點 print('佇列實現層次遍歷:') tree.level_queue(tree.root) print('\n\n遞迴實現先序遍歷:') tree.front_digui(tree.root) print('\n遞迴實現中序遍歷:') tree.middle_digui(tree.root) print('\n遞迴實現後序遍歷:') tree.later_digui(tree.root) print('\n\n堆疊實現先序遍歷:') tree.front_stack(tree.root) print('\n堆疊實現中序遍歷:') tree.middle_stack(tree.root) print('\n堆疊實現後序遍歷:') tree.later_stack(tree.root)

時間複雜度 / 空間複雜度:分別用來評估演算法執行效率和記憶體佔用大小的式子。

### 遞迴的兩個特點:

1. 呼叫自身
2. 結束條件

```python
def func(x):
if x > 0:
func(x - 1)
print(x)


func(5)
# 1,2,3,4,5 遞迴呼叫func,遞迴結束後,從內至外print(x)
```

### 斐波那契數列:
```python
a = 0
b = 1
while b < 1000:
print(b)
a, b = b, a + b
```
```python
li = []
for i in range(20):
if i == 0 or i == 1:
li.append(1)
else:
li.append(li[i-1] + li[i-2])

print(li)
```
### 臺階例子:

```python
# 有20級臺階的樓梯,一次可以邁一級或兩級臺階,那麼爬完此樓梯有幾種方法?
li = []
for i in range(20):
if i == 0:
li.append(1) # 一級臺階1種走法
elif i == 1:
li.append(2) # 二級臺階2種走法
else:
li.append(li[i - 2] + li[i - 1]) # 後一級臺階的走法是前兩級走法之和
print(li)

# [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946]
```

```python
# 一樓梯共有20級臺階,規定每步可以邁1級臺階或2級臺階或3級臺階,那麼爬完此樓梯有幾種方法?

li = [] # 1、2、3級臺階分別有1、2、4種走法
for i in range(20):
if i == 0:
li.append(1)
elif i == 1:
li.append(2)
elif i == 2:
li.append(4)
else:
li.append(li[i-3] + li[i-2] + li[i-1]) # 後一級臺階的走法是前三級走法之和

print(li)

# [1, 2, 4, 7, 13, 24, 44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513, 35890, 66012, 121415]

```
### 二分查詢
針對的是有序列表。從有序列表的候選區data[0:n]開始,通過對待查詢的值與候選區中間值的比較,可以使候選區減少一半。

```python
def bin_search(data,value):
low = 0
high = len(data) - 1
while low <= high:
mid = (low + high) // 2
if data[mid] == value:
return mid
elif data[mid] > value:
high = mid -1
else:
low = mid + 1
else:
return "查無此值"


li = list(range(1,10))
print(bin_search(li,5))
```
### 遞迴版二分查詢

```python
def bin_search_rec(data, value, low, high):
if low <= high:
mid = (low + high) // 2
if data[mid] == value:
return mid
elif data[mid] > value:
return bin_search_rec(data, value, low, mid - 1)
else:
return bin_search_rec(data, value, mid + 1, high)
else:
return "查無此值"


li = list(range(1, 10))
high = len(li) - 1
print(bin_search_rec(li, 6, 0, high))
```

### 列表排序
將無序列表變為有序列表。

```python
排序low B三人組:
氣泡排序 # 重點掌握
選擇排序
插入排序

排序NB三人組:
快速排序 # 重點掌握
堆排序
歸併排序

基本無人用的排序(瞭解):
基數排序
希爾排序
桶排序
```

### 氣泡排序
關鍵點:趟 | 無序區。

```python
def bubble_sort(li):
# 趟
for i in range(len(li) - 1):
# 無序區
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1] , li[j]


import random

li = list(range(1, 11))
random.shuffle(li)
bubble_sort(li)
print(li)

時間複雜度:O(n2),空間複雜度O(1)
```

氣泡排序--優化。如果氣泡排序中執行一趟而沒有交換,則列表已經是有序狀態,可以直接結束演算法。

```python
def bubble_sort(data):
# 趟
for i in range(len(data) - 1):
exchange = False
# 無序區
for j in range(len(data) - i - 1):
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
exchange = True
if not exchange:
return


import random

li = list(range(1, 16))
random.shuffle(li)
bubble_sort(li)
print(li)
```

### 快速排序

快排思路:

1. 取一個元素p(第一個元素),使元素p歸位;
2. 列表被p分為兩部分,左邊都比p小,右邊都比p大;
3. 遞迴完成排序。

關鍵點:整理 | 遞迴

```python
# 分割函式
def partition(data, left, right):
tem = data[left]
while left < right:
while left < right and data[right] >= tem:
right -= 1
data[left] = data[right]

while left < right and data[left] <= tem:
left += 1
data[right] = data[left]

data[left] = tem
return left


def quick_sort(data, left, right):
if left < right:
mid = partition(data, left, right)
quick_sort(data, left, mid - 1)
quick_sort(data, mid + 1, right)


li = list(range(1, 11))
import random

random.shuffle(li)
quick_sort(li, 0, len(li) - 1)

print(li)

時間複雜度O(nlogn),空間複雜度:平均情況O(logn) | 最壞情況O(n)
```
### 二叉樹

二叉樹:度不超過2的樹(節點最多有兩個叉);

滿二叉樹:一個二叉樹,如果每一層的節點數都達到最大值,則這個二叉樹就是滿二叉樹;

完全二叉樹:葉節點只能出現在最下層和次下層,並且最下面一層的節點都集中在該層最左邊若干位置的二叉樹。

```python
# 二叉樹的儲存方式:鏈式儲存方式;順序儲存方式(列表)。
父節點和左孩子節點的編號下標的關係:
2i+ 1;

父節點和右孩子節點的編號下標的關係:
2i + 2;

# (完全)二叉樹可以利用列表來儲存,通過規律可以從父親找到孩子或從孩子找到父親。

```

### 堆
大根堆:一顆完全二叉樹,滿足任一節點都比其孩子節點大;
小根堆:一個完全二叉樹,滿足任一節點都比其孩子節點小。


# 資料結構

資料結構是指相互之間存在著一種或多種關係的資料元素的集合和該集合中資料元素之間的關係組成。

簡單來說,資料結構就是設計資料以何種方式組織並存儲在計算機中。

程式 = 資料結構 + 演算法

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

1. 線性結構:資料結構中的元素存在一對一的相互關係;
2. 樹結構:資料結構中的元素存在一對多的相互關係;
3. 資料結構中的元素存在多對多的相互關係。

### 棧

```
棧(Stack)是一個數據集合,可以理解為只能在一端進行插入或刪除操作的列表。
棧的特點:後進先出(last-in, first-out)
棧的概念:棧頂、棧底
棧的基本操作:進棧(壓棧)push 出棧pop 取棧頂gettop
```
### 佇列

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

佇列的實現原理:環形佇列
```

```python
# Python處理佇列的模組
from collections import deque
# Python處理堆的模組
import heapq
```
### 連結串列

連結串列中每一個元素都是一個物件,每個物件稱為一個節點,包含有資料域key和指向下一個節點的指標next。通過各個節點之間的相互連線,最終串聯成一個連結串列。

```python
# 節點定義
class Node(object):
def __init__(self,item):
self.item = item
self.next = None
```
雙鏈表中每個節點有兩個指標:一個指向後面節點、一個指向前面節點。

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

###雜湊表
雜湊表是一個通過雜湊函式來計算資料儲存位置的資料結構。通常支援如下操作:
* insert(key, value):插入鍵值對(key,value)
* get(key):如果存在鍵為key的鍵值對則返回其value,否則返回空值
* delete(key):刪除鍵為key的鍵值對

```
雜湊表(Hash Table,又稱為散列表),是一種線性表的儲存結構。
雜湊表由一個直接定址表和一個雜湊函式組成。雜湊函式h(k)將元素關鍵字k作為自變數,返回元素的儲存下標。
```
由於雜湊表的大小是有限的,而要儲存的值的總數量是無限的,因此對於任何雜湊函式,都會出現兩個不同元素對映到同一個位置上的情況,這種情況叫做雜湊衝突。
比如:h(k)=k mod 7, h(0)=h(7)=h(14)=...

解決雜湊衝突
開放定址法:如果雜湊函式返回的位置已經有值,則可以向後探查新的位置來儲存這個值。
拉鍊法:雜湊表每個位置都連線一個連結串列,當衝突發生時,衝突的元素將被加到該位置連結串列的最後。
字典與集合都是通過雜湊表來實現的。

### 二叉樹
二叉樹的鏈式儲存:將二叉樹的節點定義為一個物件,節點之間通過類似連結串列的連結方式來連線。

**二叉樹的遍歷**
對於二叉樹,有深度遍歷和廣度遍歷,深度遍歷有前序、中序以及後序三種遍歷方法,廣度遍歷即我們平常所說的層次遍歷。
要點:遞迴思想,每一級子樹都遵循這個規則。

前序遍歷:根結點 ---> 左子樹 ---> 右子樹
中序遍歷:左子樹---> 根結點 ---> 右子樹
後序遍歷:左子樹 ---> 右子樹 ---> 根結點
層次遍歷:只需按層次遍歷即可

二叉搜尋樹是一顆二叉樹且滿足性質:設x是二叉樹的一個節點。如果y是x左子樹的一個節點,那麼y.key ≤ x.key;如果y是x右子樹的一個節點,那麼y.key ≥ x.key.
平均情況下,二叉搜尋樹進行搜尋的時間複雜度為O(nlogn)。

```
AVL樹:AVL樹是一棵自平衡的二叉搜尋樹。
B樹(B-Tree):B樹是一棵自平衡的多路搜尋樹。常用於資料庫的索引。
```