python---數據結構和算法複習

鏈表和二叉樹

# 一、鏈表反轉

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



# 二、鏈表排序
# 快排法
    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

# 三、鏈表去重
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


# 四、鏈表是否閉環
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)


# 五、合併兩個有序鏈表
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


# 六、求出鏈表倒數第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]


# 七、刪除鏈表的當前節點
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


# 八、鏈表的中間節點
# 快慢指針,快指針走完,慢指針在中間位置
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)

# 九、在一個有環鏈表中找到環的入口
# 一個從相交的地方走,一個從頭走,當相遇的時候,這個相遇的節點就是入口
    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)

時間複雜度 / 空間複雜度:分別用來評估算法運行效率和內存佔用大小的式子。node

### 遞歸的兩個特色:python

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)
```
### 臺階例子:app

```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)dom

# [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級臺階,那麼爬完此樓梯有幾種方法?oop

li = [] # 一、二、3級臺階分別有一、二、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樹是一棵自平衡的多路搜索樹。經常使用於數據庫的索引。```

相關文章
相關標籤/搜索