Python 第八階段 學習記錄之---算法

算法(Algorithm): 一個計算過程, 解決問題的方法

一、遞歸的兩個特色
- 調用自身
- 結束條件

時間複雜度
- 時間複雜度是用來估計算法運行時間的一個式子(單位)
- 通常來講,時間複雜度高的算法比複雜度低的算法快
空間複雜度
用來評估算法內存佔用大小的一個式子

列表查找: 從列表中查找指定元素
輸入:無序
輸出:有序
順序查找:
從列表第一個元素開始,順序進行搜索,直到找到爲止。

二分查找:
從有序列表的候選區data[0:n]開始,經過對待查找的值與候選區中間值的比較,可使候選區減小一半。
找任何一個數,次數都不會超過LOG 2 N, 2爲底N的對數, 2 ** x <= n
在10**5內找一個數,那麼找到它,須要找的次數 2 **x < 10**5, x = 16

列表排序:

列表排序: 將無序列表變爲有序列表
應用場景:
各類榜單、表格、給二分排序用,給其它算法用

排序lowB三人組:
冒泡排序
選擇排序
插入排序
快速排序
排序NB二人組:
- 堆排序
- 歸併排序
沒什麼人用的排序:
基數排序
希爾排序
桶排序


1、LOW
時間複雜度:O(n**2)
空間複雜度:O(1)

冒泡排序:
時間複雜度:O(n**2)
random.shuffle(data) #
排序一萬個數得10多秒,
首先,列表每兩個相鄰的數,若是前邊的比後邊的大,那麼交換這兩個數
若是冒泡排序中執行一趟而沒有交換,則列表已是有序狀態,能夠直接結束算法。
選擇排序
一趟遍歷記錄最小的數,放到第一個位置;
再一趟遍歷記錄剩餘列表中最小的數,繼續放置;
找到最小的數:
if xx < xxx: xxx=xx, xx,xx=xx,xx, 小於則交換
插入排序:(打牌同樣,來一個數、插入一次)
列表被分爲有序區和無序區兩個部份。最初有序區只有一個元素。
每次從無序區選擇一個元素,插入到有序區的位置,直到無序區變空。


2、快排(快速排序)
快速排序:
好寫的排序算法裏最快的
快的排序裏最好寫的

快排思路:
取一個元素P(第一個元素), 使P歸位;
列表被P分紅兩部分,左邊都比P小,右邊都比P大;
遞歸完成排序

效率:
快排相比冒泡快了多少?
問題:
最壞狀況
遞歸

最好狀況 通常狀況 最壞狀況
快排 O(nlogn) O(nlogn) O(n^2)
冒泡 O(n) O(n^2) O(n^2)

代碼實現:



樹是一種數據結構 好比:目錄結構
樹是一種能夠遞歸定義的數據結構
樹是由n個節點組成的集合:
若是n=0,那這是一棵空樹;
若是n>0,那存在1個節點做爲樹的根節點,其餘節點能夠分爲m個集合,每一個集合自己又是一棵樹。
一些概念
根節點、葉子節點
樹的深度(高度)
樹的度: 有幾個子結點(下一層,)
孩子節點/父節點
子樹

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

二叉樹的存儲方式:
鏈式存儲方式
順序存儲方式(列表)

父節點和左孩子節點的編號下標有什麼關係?
i -> 2*i + 1

父節點和右孩子節點的編號下標有什麼關係?
i -> 2i+2
好比,咱們要找根節點左孩子的左孩子
x = 2*(2*0+1)+1 = 3, list_[3]
最後一個有子結點的堆
x = n // 2 - 1 # n = len()

二叉樹是度不超過2的樹
滿二叉樹與徹底二叉樹
(徹底)二叉樹能夠用列表來存儲,經過規律能夠從父親找到孩子或從孩子找到父親


大根堆:一棵徹底二叉樹,知足任一節點都比其孩子節點大
小根堆:一棵徹底二叉樹,知足任一節點都比其孩子節點小

假設:節點的左右子樹都是堆,但自身不是堆
當根節點的左右子樹都是堆時,能夠經過一次向下的調整來將其變換成一個堆
堆排序過程:
創建堆
獲得堆頂元素,爲最大元素
去掉堆頂,將堆最後一個元素放到堆頂,此時可經過一次調整從新使堆有序。
堆頂元素爲第二大元素。
重複步驟3,直到堆變空。



歸併:
- 假設如今的列表分紅兩段有序, 如何將其合成爲一個有序列表
- 從兩邊分別取,取最小的 ===> 這個過程就叫歸併
確定有一邊先取完,剩下的就沒必要再比較,直接取下來,xx[:] = xxx[:]

有了歸併如何使用?
分解: 將列表越分越小,直至分紅一個元素。
一個元素是有序的
合併: 將兩個有序列表歸併,列表愈來愈大。


時間複雜度:O(nlogn)
空間複雜度:O(n)

* 三種排序算法的時間複雜度是O(nlogn)
- 通常狀況下,就運行時間而言:
快速排序 < 歸併排序 < 堆排序

- 三種排序算法的缺點:
- 快速排序: 極端狀況下排序效率底
- 歸併排序: 須要額外的內存開銷
- 堆排序: 在快的排序算法中相對較慢

希爾排序:
希爾排序是一種分組插入排序算法。
首先取一個整數d1=n/2,將元素分爲d1個組,每組相鄰量元素之間距離爲d1,在各組內進行直接插入排序;
取第二個整數d2=d1/2,重複上述分組排序過程,直到di=1,即全部元素在同一組內進行直接插入排序。
希爾排序每趟並不使某些元素有序,而是使總體數據愈來愈接近有序;最後一趟排序使得全部數據有序。

時間複雜度: O((1+T)n) # O(1.3n)

排序:
- 兩個排序條件
姓名、年齡
排序的穩定性,


冒泡、快排、堆、歸併
算法穩定性:
就是算法的穩定性:假定在待排序的記錄序列中,存在多個具備相同的關鍵字的記錄(有相同的元素),
若通過排序,這些記錄的相對次序保持不變,
即在原序列中,ri=rj,且ri在rj以前,而在排序後的序列中,ri仍在rj以前,則稱這種排序算法是穩定的;不然稱爲不穩定的。

練習1:
一、如今有一個列表,列表中的數範圍都在0到100之間,列表長度大約爲100萬,設計算法在O(n)時間複雜度內將列表進行排序。
- 計數排序,統計每一個數出現的次數。只能是數值型,而後再寫回列表
這時使用快排等會比較慢,有大量重複數據

二、有N(n>1000000)個數,如何取出最大的前10個數?
- 取列表前10個元素創建一個小根堆。堆頂就是目前第10大的數
- 依次向後遍歷原列表,對於列表中的元素,若是小於堆頂,則忽略該元素;若是大於堆頂,則將堆頂更換爲該元素,而且對堆進行一次調整;
- 遍歷列表全部元素後,倒序彈出堆頂。


堆----> 優先排列
練習2:
給定一個升序列表和一個整數,返回該整數在列表中的下標範圍。
例如: 列表[1,2,3,3,3,4,4,5], 若查找3,則返回(2,4);若查找0,由返回(0,0)


study
#-*- coding: utf-8 -*-
# Wind clear raise
# 2017/7/22 下午4:33


import random
import runtime

@runtime.call_time
def bubble_sort(x):
    print(id(x))
    for i in range(len(x)-1):

        for j in range(len(x) -i - 1):
            # 相鄰兩個數相比較
            #
            if x[j] > x[j+1]:
                x[j], x[j+1] = x[j+1], x[j]


@runtime.call_time
def bubble_sort_2(x):
    """冒泡排序優化"""
    print(id(x))
    for i in range(len(x)-1):
        exchange = False
        for j in range(len(x) -i - 1):
            # 相鄰兩個數相比較
            #
            if x[j] > x[j+1]:
                x[j], x[j+1] = x[j+1], x[j]
                exchange = True
        if not exchange:
            print(exchange)
            break


#選擇排序
@runtime.call_time
def select_sort(li):
    for i in range(len(li) -1 ):
        min_loc = i
        for j in range(i+1, len(li)):
            if li[j] < li[min_loc]:
                min_loc = j
        li[i], li[min_loc] = li[min_loc], li[i]


#插入排序
@runtime.call_time
def insert_sort(li):
    for i in range(1, len(li)):
        tmp = li[i]
        j = i - 1
        while j >=0 and tmp < li[j]:
            li[j+1] = li[j]
            j -= 1
        li[j+1] = tmp

## 快排


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)

def partition(data, left, right):

    tmp = data[left]
    while left < right:  # left == right 終止
        while left < right and data[right] >= tmp:
            right -= 1
        data[left] = data[right]

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

    data[left] = tmp

    return left

@runtime.call_time
def quick_sort_x(data):

    quick_sort(data, 0 , len(data) -1)



#
def sift(data, low, high):
    i = low
    j = 2 * i + 1
    tmp = data[i]

    while j <= high:  # 孩子在堆裏
        if j < high and data[j] < data[j+1]: # 若是有右孩子且比左孩子大
            j += 1
        if tmp < data[j]:  # 孩子比根大
            data[i] = data[j]
            i = j    # 新的根
            j = 2 * i + 1   # 新的孩子

        else:
            break
    data[i] = tmp
# 降序
def sift_desc(data, low, high):
    i = low
    j = 2 * i + 1
    tmp = data[i]

    while j <= high:  # 孩子在堆裏
        if j < high and data[j] > data[j+1]: # 若是有右孩子且比左孩子小
            j += 1
        if tmp > data[j]:  # 孩子比根小
            data[i] = data[j]
            i = j    # 新的根
            j = 2 * i + 1   # 新的孩子

        else:
            break
    data[i] = tmp

@runtime.call_time
def heap_sort(data):

    n = len(data)
    # print(data)
    # 最後一個有子結點的堆 n // 2 -1
    for i in range(n // 2 -1, -1, -1):
        sift(data, i, n -1)
    # 堆建好了。。
    # print(data)

    for i in range(n -1, -1, -1):   # i指向堆的最後
        data[0], data[i] = data[i], data[0]  # 領導退休,下屬上位
        sift(data, 0, i - 1)   # 調整新領導
    # print(data)


## 歸併
def merge(li, low, mid, high):
    i = low
    j = mid + 1
    ltmp = []
    while i <= mid and j<=high:
        if li[i] < li[j]:
            ltmp.append(li[i])
            i += 1
        else:
            ltmp.append(li[j])
            j += 1

    while i<= mid:
        ltmp.append(li[i])
        i += 1
    while j<= high:
        ltmp.append(li[j])
        j += 1

    li[low:high+1] = ltmp
def mergesort(li, low, high):
    if low < high:
        mid = (low + high) // 2
        # print(low, high, mid)
        mergesort(li, low, mid)
        mergesort(li, mid+1, high)
        merge(li, low, mid, high)

@runtime.call_time
def mergesort_x(*args):
    mergesort(*args)


# 希爾
@runtime.call_time
def shell_sort(li):
    gap = len(li) // 2
    while gap >0:
        for i in range(gap, len(li)):
            tmp = li[i]
            j = i - gap
            while j >= 0 and tmp < li[j]:
                li[j+gap] = li[j]
                j -= gap
            li[j + gap] = tmp
        gap //= 2



import copy

data = list(range(10000))
#
random.shuffle(data)  # 打亂一個列表
data1 = copy.deepcopy(data)
data2 = copy.deepcopy(data)
data3 = copy.deepcopy(data)
data4 = copy.deepcopy(data)
# data = [1, 5, 3, 7, 8, 9]

# bubble_sort(data)
# random.shuffle(data)
# bubble_sort_2(data)
# print(id(data))

##xxx 1 5 3 7 8 9
## 5 4 8 3 9
# data = [5, 4, 8, 3, 9]

# insert_sort(data2)
# select_sort(data1)
# bubble_sort_2(data4)
# print(data)
print("快排")
quick_sort_x(data3)
# print(data3)
print("")
heap_sort(data1)

# 歸併
print("歸併")
mergesort_x(data4, 0, len(data4)-1)

print("shell sort")
shell_sort(data2)
# print(data2)




def topn(li, n=10):
    heap = li[0:n]
    for i in range(n//2-1, -1, -1):
        sift(heap, i, n-1)

    for i in range(n, len(li)):
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap, 0, n -1 )

    for i in range(n -1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i-1)

    return heap
study.py
相關文章
相關標籤/搜索