python中的一些算法


兩個基礎知識點:遞歸和時間復雜度

遞歸

遞歸函數的特點:自己調用自己,有結束條件,看下面例子:

def fun1(x):
    """無結束條件,報錯"""
    print(x)
    fun1(x-1)

def fun2(x):
    """結束條件為錯誤條件,報錯"""
    if x>0:
        print(x)
        fun2(x+1)

def fun3(x):
    """打印倒序"""
    if x>0:
        print(x)
        fun3(x-1)

def fun4(x):
    """打印正序"""
    if x > 0:
        fun4(x-1)
        print(x)

fun3(7)
fun4(7)

結果:

7
6
5
4
3
2
1
*******
1
2
3
4
5
6
7

時間復雜度

用來評估算法運行效率的東西:

print('Hello World')
#時間復雜度:O(1)

for i in range(n):
    '''時間復雜度:O(n)'''
    print('Hello World')

for i in range(n):
    '''時間復雜度:O(n^2)'''
    for j in range(n):
        print('Hello World')
        
for i in range(n):
    '''時間復雜度:O(n^3)'''
    for j in range(n):
        for k in range(n):
            print('Hello World')
            
while n > 1:
    '''時間復雜度:O(log2n)或者O(logn)'''
    print(n)
    n = n // 2  

小結:

  • 時間復雜度是用來估算一個算法運行時間的標准
  • 一般說來,時間復雜度高的要比時間復雜度低的算法慢
  • 常見的復雜度按效率排行:
    O(1) < O(logn) <O(n) <O(nlogn) < O(n^2) < O(n^2 logn) < O(n^3)

那么如何一樣判斷時間復雜度?

  • 循環減半的過程,O(logn)
  • 幾次循環就是n的幾次方的復雜度

列表查找

  • 輸入:列表或者待查找元素
  • 輸出:元素下標或者未查到的元素

順序查找

從元素的第一個開始,按順序進行查找,直到找到為止

二分查找

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

import time

#定義一個計算運行時間的裝飾器
def cal_time(func):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        res = func(*args,**kwargs)
        t2 = time.time()
        print('%s:%s'%(func.__name__,t2-t1))
        return res
    return wrapper


#順序查找
@cal_time
def linear_search(data_set,value):
    for i in range(len(data_set)):
        if data_set[i] == value:
            return i

#二分查找
@cal_time
def bin_search(data_set,value):
    low = 0
    high = len(data_set) - 1
    while low <= high:
        mid = (low + high) // 2
        if data_set[mid] == value:
            return mid
        elif data_set[mid] < value:
            low = mid + 1
        else:
            high = mid - 1


ret = linear_search(list(range(100000)),99999)

ret2 = bin_search(list(range(100000)),99999)

但有個問題:理論上順序查找的時間復雜度為O(n),二分查找的為O(logn),看結果二分查找的結果為一個科學記數法,相差2個量級.

linear_search:0.009849071502685547
bin_search:1.5974044799804688e-05

列表排序

將無序列表變為有序列表

low逼三人組:冒泡、選擇、插入

之前寫過:http://www.cnblogs.com/ccorz/p/5581066.html

冒泡

序列中,相鄰的兩個元素比較大小,如果前面的比后面的元素大,那么交換位置,以此類推...

import random

data = list(range(10000))
random.shuffle(data)


def bubbel_sort(data):
    for i in range(len(data) - 1):
        for j in range(len(data) - i - 1):
            if data[j] > data[j+1]:
                data[j], data[j+1] = data[j+1], data[j]

bubbel_sort(data)
print(data)
冒泡的優化:

如果排序執行了一趟數據沒有交換,那么說明列表已經是有序狀態,可以直接結束算法:

def bubbel_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:
            break

選擇

遍歷一趟,選擇最小的數放到第一個位置,接着遍歷剩下的序列,選擇其中最小的,放到剩下序列的第一個位置,如此循環.

import random

data = list(range(1000))
random.shuffle(data)

def select_sort(data):
    for i in range(len(data) - 1):
        min_loc = i
        for j in range(i + 1, len(data)):
            if data[j] < data[min_loc]:
                min_loc = j
        if min_loc != i:
            data[i], data[min_loc] = data[min_loc], data[i]

select_sort(data)
print(data)

插入排序

列表被分為有序區和無序區兩個部分,並且最初有序區只有一個元素.

import random

data = list(range(1000))
random.shuffle(data)

def insert_sort(data):
    for i in range(1,len(data)):
        tmp = data[i]
        j = i - 1
        while j >= 0 and data[j] > tmp:
            data[j+1] = data[j]
            j -= 1
        data[j+1] = tmp

insert_sort(data)
print(data)

快速排序(簡稱快排)

好些的算法里最快的,快的排序算法中最好寫的。

思路:

  • 取第一個元素,是這個元素(P)歸位(對的位置)
  • 列表被P元素分成兩部分
  • 遞歸這兩部分列表,以此類推

總結一句話就是:先整理,后遞歸

import sys, random
#解除python默認遞歸次數的限制
sys.setrecursionlimit(10000)

data = list(range(1000))
random.shuffle(data)


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:
        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

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

堆排序

二叉樹

滿二叉樹是指這樣的一種二叉樹:除最后一層外,每一層上的所有結點都有兩個子結點。在滿二叉樹中,每一層上的結點數都達到最大值,即在滿二叉樹的第k層上有2k-1個結點,且深度為m的滿二叉樹有2m-1個結點。

完全二叉樹是指這樣的二叉樹:除最后一層外,每一層上的結點數均達到最大值;在最后一層上只缺少右邊的若干結點。

二叉樹

二叉樹的數據存儲

二叉樹的存儲

二叉樹總結
  • 二叉樹是不超過2個節點的樹
  • 滿二叉樹是完全二叉樹,完全二叉樹不一定是滿二叉樹
  • 完全二叉樹可以用列表來存儲,通過規律可以從父親找到孩子,或從孩子找到父親

大根堆:一顆完全二叉樹,滿足任何一節點都比其子節點大
小根堆:一顆完全二叉樹,滿足任何一節點都比其子節點小

堆

堆排序

堆排序

堆排序的過程:

  1. 建立堆
  2. 得到堆頂元素,假設為最大元素
  3. 去掉堆頂元素,將最后一個元素放到堆頂,此時可以通過一次調整,重新使堆有序
  4. 堆頂元素為第二大元素
  5. 重復步驟三

代碼:

import sys, random

sys.setrecursionlimit(10000)

data = list(range(100))
random.shuffle(data)


def sift(data,low,high):
    '''堆整理,選出最大的元素'''
    i = low
    j = 2*i+1
    k = j +1
    tmp = data[i]
    while j <= high:
        if j < high and data[j] < data[k]:
            j+=1
        if tmp < data[j]:
            data[i]=data[j]
            i=j
            j=2*i+1
        else:
            break
    data[i]=tmp

def heap_sort(data):
    n = len(data)
    for i in range(n//2-1,-1,-1):
        sift(data,i,n-1)
    for i in range(n-1,-1,-1):
        data[0],data[i]=data[i],data[0]
        sift(data,0,i-1)

heap_sort(data)
print(data)

out:

[0, 1, 2, 3, 12, 5, 4, 6, 7, 8, 25, 9, 10, 11, 17, 23, 13, 73, 29, 14, 15, 19, 40, 28, 44, 64, 30, 27, 18, 16, 21, 70, 22, 20, 31, 24, 34, 32, 33, 26, 35, 36, 39, 46, 37, 41, 38, 42, 86, 45, 43, 51, 62, 47, 49, 75, 72, 54, 50, 48, 57, 63, 68, 56, 87, 60, 67, 59, 55, 78, 58, 61, 69, 52, 53, 80, 83, 65, 89, 66, 94, 91, 71, 82, 92, 90, 77, 81, 74, 84, 95, 76, 85, 88, 93, 79, 96, 98, 97, 99]


免責聲明!

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



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