day30-算法

算法、數據結構、金融      算法ppt ,72頁!
day30 算法(algorithm):11個py文件 (查找和排序)
1.簡介--算法必考
一個計算過程,解決問題的方法
時間複雜度:用來估算算法運行時間的一個式子(單位),通常來講,時間
複雜度高的算法比複雜度低的算法慢。

空間複雜度:用來評估算法內存佔用大小的一個式子----通常不討論了

時間複雜度小結:
時間複雜度是用來估計算法運行時間的一個式子(單位)。
通常來講,時間複雜度高的算法比複雜度低的算法慢。
常見的時間複雜度(按效率排序)
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
不常見的時間複雜度(看看就好)
O(n!) O(2n) O(nn) …

如何一眼判斷時間複雜度?
循環減半的過程O(logn)
幾回循環就是n的幾回方的複雜度
小結


遞歸:調用自身,結束條件
# 斐波那契數列
# 1 1 2 3 5 8 13
# 1 2 3 4 5
# def func(x):
#     if x>0:
#         func(x-1)
#         print(x)
#
# func(5)

# 斐波那契數列
# 1 1 2 3 5 8 13

# def fib(n):
#     if n == 0 or n == 1:
#         return 1
#     else:
#         return fib(n-1) + fib(n-2)
# print(fib(100))


# def fib(n):
#     li = [1,1]
#     for i in range(2,n+1):
#         li.append(li[-1]+li[-2])
#     return li[-1]
# print(fib(100))


def fib(n):
    if n == 0 or n == 1:
        return 1
    a = 1
    b = 1
    c = 1
    for i in range(2, n + 1):
        a = b
        b = c
        c = a + b
    return c


print(fib(100))
遞歸

漢諾塔問題:移盤子
def hanoi(n, A, B, C):
    if n > 0:
        hanoi(n-1, A, C, B)
        print('%s->%s' % (A, C))
        hanoi(n-1, B, A, C)

hanoi(5, 'A', 'B', 'C')
hanoi
走樓梯,遞歸問題,相似於斐波那契,按照最後一步是一階或者兩階去算
鋪磚問題同理


列表查找:順序查找、二分查找(須要是有序列表)
第21頁ppt兩個查找代碼
def linear_search(data_set, value):  
    for i in range(range(data_set)):
        if data_set[i] == value:      
            return i  
    return

時間複雜度:O(n)


def bin_search(li, val):
    low = 0
    high = len(li) - 1
    while low <= high:
        mid = (low + high) // 2
        if li[mid] > val:
            high = mid - 1
        elif li[mid] < val:
            low = mid + 1
        else:   # =
            return mid
    return None

時間複雜度:O(logn)

應用:
li = [1,3,4,5,6,8,9,80]
print(bin_search(li,5))
查找

遞歸版二分查找
列表是對象,能夠按照key去查

列表排序:冒泡,選擇,插入;快速排序,堆排序,歸併排序

算法關鍵點:有序區、無序區
1.冒泡:必需要會
一趟以後,最大的元素在最上面,共須要n-1趟
順序: [0,n-i-1]
箭頭到n-i-2,但前包後不包,for循環寫n-i-1
import random
from timewrap import cal_time

@cal_time
# def bubble_sort(li):
#     for i in range(len(li)-1): # i表示第i趟
#         for j in range(len(li)-i-1): # j表示箭頭位置,
#             if li[j] > li[j+1]:
#                 li[j], li[j+1] = li[j+1], li[j]
#         print(li)
# li = list(range(10))
# random.shuffle(li) #將函數打亂
# print(li)  #這10個數開始的順序
# bubble_sort(li)




@cal_time
def bubble_sort_2(li):
    for i in range(len(li)-1): # i表示第i趟
        exchange = False
        for j in range(len(li)-i-1): # j表示箭頭位置
            if li[j] > li[j+1]:
                li[j], li[j+1] = li[j+1], li[j]
                exchange = True
        if not exchange:
            return


li = list(range(1000))
random.shuffle(li) #將函數打亂
# print(li)
bubble_sort_2(li)
#print(li)
冒泡排序
import time


def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." % (func.__name__, t2-t1))
        return result
    return wrapper

2.選擇排序:每次選出一個最小的,比冒泡少了交換,因此速度快一些
import random
from timewrap import cal_time

def find_min(li): #找最小值
    min_val = li[0]
    for i in range(1, len(li)):
        if li[i] < min_val:
            min_val = li[i]
    return min_val

def find_min_pos(li): #找最小值位置
    min_pos = 0
    for i in range(1, len(li)):
        if li[i] < li[min_pos]:
            min_pos = i
    return min_pos

@cal_time
def select_sort(li): #排序方法
    for i in range(len(li)-1): # i表示第i趟
        # 無序區的範圍 [i, len(li))
        min_pos = i
        for j in range(i+1, len(li)):
            if li[j] < li[min_pos]:
                min_pos = j
        li[i], li[min_pos] = li[min_pos], li[i]
        # print(li) #每趟結束以後打印


li = list(range(10000))
#random.shuffle(li)
# print(li)
select_sort(li)
#print(li)
選擇排序

3.插入排序:相似於摸牌插入(摸到的牌,手裏的牌) 共須要n-1趟
時間複雜度:n方級別,速度與選擇差很少

選擇速度最快只能是n方,插入和冒泡多是n
import random
from timewrap import cal_time


# 什麼時候插入? 1. j位置的值小於tmp  2.j=-1   li[j+1]=tmp

@cal_time
def insert_sort(li):
    for i in range(1, len(li)):
        j = i - 1
        tmp = li[i]
        while j >= 0 and li[j] > tmp:  # 循環繼續的條件
            li[j + 1] = li[j]
            j -= 1
        li[j + 1] = tmp
        # print(li)


li = list(range(10000))
# random.shuffle(li)
# print(li)
insert_sort(li)
插入排序
# 布爾值的短路問題
def func():
    print('aaa')


x = -3
x > 0 and func()  # func()不執行

#證實快排空間複雜度
def A(a, b):
    return a + b


def B(a, b):
    return A(a + b, b)


def C(x):
    return B(x // 2, x + 5)


x = C(10)
print(x)
測試

4.快速排序:重點!必須掌握!
取一個元素歸位,遞歸完成排序
算法關鍵點:整理(partition)、遞歸()
空位的概念,左右尋找(從後開始)
降序排序,只需將兩個while條件中的與tmp比較的大於小於號寫反
import sys
import random
from timewrap import cal_time

sys.setrecursionlimit(1100) #設置遞歸深度


def _quick_sort(li, left, right):  # 遞歸函數 前面加_
    if left < right:  # 至少兩個元素
        mid = partition(li, left, right)
        _quick_sort(li, left, mid - 1)
        _quick_sort(li, mid + 1, right)


@cal_time
def quick_sort(li): #封裝參數的非遞歸函數
    _quick_sort(li, 0, len(li) - 1)


def partition(li, left, right):
    i = random.randint(left, right) #隨機化選一個,最壞機率下降
    li[left], li[i] = li[i], li[left] #隨機化選一個,最壞機率下降
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]
    li[left] = tmp
    return left


# @cal_time
# def sys_sort(li): #系統自帶的排序,底層是c語言,因此速度快
#     li.sort()


li = list(range(100000, -1, -1)) #把數據倒着
random.shuffle(li)
quick_sort(li)
# sys_sort(li)
快速排序!!


5.堆排序:難!
樹與二叉樹簡介:樹是一種數據結構,能夠遞歸定義的數據結構
樹的度:最多節點孩子的個數;根節點的度:其對應孩子的個數
二叉樹:度不超過2的樹(節點最多有兩個叉),分左右孩子
滿二叉樹:每個結點都達到最大值
徹底二叉樹:葉結點只能出如今最下層和次下層,且最下面一層的結點都集中在最左邊
二叉樹的存儲方式:鏈式存儲方式--存。。、順序存儲方式(列表)--存徹底二叉樹
i表示下標:左孩子:2i+1 右孩子:2i+2 孩子找父親:(i-1)//2

堆:大根堆:一顆徹底二叉樹,任一節點都比孩子節點大 (排完升序)
小根堆:一顆徹底二叉樹,任一節點都比孩子節點小(排完降序)
堆的向下調整性質 :左右子樹都是堆,但自身不是堆,一次調整便可成堆!
挨個出數:棋子從最後一個非葉子節點開始,退休,棋子,調整
構造堆:從子樹開始從下往上調整
堆排序過程:1.創建堆2.獲得最大元素3.去掉堆頂,將堆最後一個元素放到堆頂
4.堆頂元素爲第二大元素 5.重複3,直到堆變空

代碼書寫:high表示最後元素的位置;堆排序函數,重點是兩個條件
問題:topK問題,如今有n個數,設計算法找出前k大的數(k<n)
1.排序後切片 O(nlogn +k) 2.LowB三人組思想 O(kn)
3.堆排序:創建小根堆, O(nlogk) 建堆klogk +調整 (n-k)logk = O(nlogk)
或用內置模塊
import random
from timewrap import cal_time

# def sift(li, low, high): #調整函數,重點是兩個條件 logn
#     # low 表示根位置 high 表示最後元素的位置
#     tmp = li[low]
#     i = low # i指向空位
#     j = 2 * i + 1 # j指向孩子
#     # 把tmp寫回來有兩種條件 1. tmp > li[j]  2. j位置沒有值 (也就是i已是葉子了)
#     while j <= high:    # 對應退出條件2
#         if j + 1 <= high and li[j+1] > li[j]: # 若是右孩子存在且右孩子更大
#             j += 1
#         if li[j] > tmp:
#             li[i] = li[j]
#             i = j
#             j = 2 * i + 1
#         else:       # 對應退出條件1
#             break
#     li[i] = tmp
#
# @cal_time
# def heap_sort(li):  # 堆排函數
#     n = len(li)
#     # 1. 創建堆
#     for low in range(n//2-1, -1, -1): #倒着走
#         sift(li, low, n-1)   # high=n-1
#     # print(li)
#     # 2. 挨個出數 退休-棋子-調整
#     for high in range(n-1, -1, -1):
#         li[0], li[high] = li[high], li[0]  #退休
#         sift(li, 0, high-1) #注意是high-1
#         # print(li)
#
#
# li = list(range(100000))
# random.shuffle(li)
# #print(li)
# heap_sort(li)


import heapq

li = [2, 5, 7, 8, 9, 6, 1, 4, 3]
heapq.heapify(li)
print(li)  # 小根堆,建堆的過程
heapq.heappush(li, 0)  # 往堆里加入
print(li)
print(heapq.heappop(li))  # 返回最小的值
print(heapq.heappop(li))  # 返回最小的值

print(heapq.nlargest(5, li))  # 5個最大的
print(heapq.nsmallest(5, li))  # 5個最小的
堆排序

6.歸併排序:條件分兩段有序,一次歸併
一個列表一次歸併;兩個列表一次歸併
遞歸歸併 時間複雜度:O(nlogn) 空間複雜度:O(n)
import random
from timewrap import cal_time


# def merge_two(li1, li2): #兩個有序列表 一次歸併
#     li = []
#     i = 0
#     j = 0
#     while i < len(li1) and j < len(li2):
#         if li1[i] <= li2[j]:
#             li.append(li1[i])
#             i += 1
#         else:
#             li.append(li2[j])
#             j += 1
#     while i < len(li1):
#         li.append(li1[i])
#         i += 1
#     while j < len(li2):
#         li.append(li2[j])
#         j += 1
#     return li   #不用寫回,直接return便可

def merge(li, low, mid, high):  # 一個列表 一次歸併代碼
    li_tmp = []
    i = low
    j = mid + 1
    while i <= mid and j <= high:
        if li[i] <= li[j]:
            li_tmp.append(li[i])
            i += 1
        else:
            li_tmp.append(li[j])
            j += 1
    while i <= mid:  # 左邊剩下
        li_tmp.append(li[i])
        i += 1
    while j <= high:  # 右邊剩下,兩個while二選一
        li_tmp.append(li[j])
        j += 1
    for i in range(len(li_tmp)):  # 將tmp值寫回,tmp值寫回列表
        li[i + low] = li_tmp[i]


def _merge_sort(li, low, high):  # 遞歸歸併排序
    if low < high:  # 2個元素及以上
        mid = (low + high) // 2
        _merge_sort(li, low, mid)  # 分解
        _merge_sort(li, mid + 1, high)  # 分解
        # print(li[low:mid + 1], li[mid + 1:high + 1])
        merge(li, low, mid, high)  # 合併
        # print(li[low:high + 1])


# li = [10, 4, 6, 3, 8, 2, 5, 7]
# _merge_sort(li, 0, len(li) - 1)
# print(li)


@cal_time
def merge_sort(li):
    _merge_sort(li, 0, len(li) - 1)


li = list(range(100000))
random.shuffle(li)
merge_sort(li)
歸併排序
1 三種排序算法的時間複雜度都是O(nlogn)
2 
3 通常狀況下,就運行時間而言:
4 快速排序 < 歸併排序 < 堆排序
5 
6 三種排序算法的缺點:
7 快速排序:極端狀況下排序效率低
8 歸併排序:須要額外的內存開銷
9 堆排序:在快的排序算法中相對較慢
NB三人組-小結

小結:挨着換的穩定

 

快排、歸併必須理解!排序理解:思路,代碼寫法,時間複雜度下週:不經常使用的排序方式:希爾排序、計數排序、桶排序、基數排序
相關文章
相關標籤/搜索