python基本算法

算法优劣评判标准

时间复杂度:

定义:用来评估算法运行效率的一个式子

print('Hello World')  O(1)

for i in range(n):
	print('Hello World')  O(n)

for i in range(n):
	for j in range(n):
		print('Hello World')  O(n^2)

for i in range(n):
	for j in range(n):
		for k in range(n):
			print('Hello World')  O(n^3)
注:O(1)、O(n)、O(n2)..是一个单位,且当n足够大时,n2的值可以忽略n的大小,所以计算时间复杂度时不会出现O(3)、O(n^2+n)的情况。
while n>1:
	print(n)
	n = n//2

输入:64

输出:  64  32  16  8  4  2


计算:2^6=64,执行次数6=log2 64

时间复杂度:O(logn)
小节:

时间复杂度是用来估计计算法运行时间的一个式子。

一般来说,时间复杂度高的算法比复杂度低的算法慢。

常见的时间复杂度(按效率排序)

O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^2logn)<O(n^3)

复杂问题的时间复杂度
O(n!) O(2^n) O(n^n)

如何简单快速地判断算法复杂度
  1. 确定问题规模n
  2. 循环减半过程 -》 logn
  3. k层关于n的循环 n^k

复杂情况:根据算法执行过程判断

空间复杂度

定义:用来评估算法内存占用大小的式子

空间复杂的表示方式与实践复杂度完全一样

算法使用了几个变量:O(1)

算法使用了长度为n的一位列表:O(n)

算法使用了m行n列的二维列表:O(mn)

空间换时间

递归算法

递归的两个特点:

  1. 调用自身
  2. 结束条件

递归:汉诺塔问题

def hanoi(n,a,b,c):
    if n>0:
        hanoi(n-1,a,c,b)
        print("moving [%s] from %s to %s" %(n,a,c))
        hanoi(n-1,b,a,c)

hanoi(3,"a","b","c")

查找问题

查找:在一些数据元素中,通过一定的方法找出与给定关键字相同的数据元素的过程

列表查找(线性表查找):从列表中产兆指定元素

  输入:列表、待查找元素

  输出:元素下表(未找到元素之一般返回None或-1)

内置列表查找函数:index()

方法一:顺序查找

顺序查找:也叫线性查找,从列表第一个元素开始,顺序进行搜索,直到找到元素或搜索到列表最后一个元素为止。

# 顺序查找
def linear_search(li,val):
    for i,v in enumerate(li):
        if v == val:
            return i
    else:
        return None

#i = linear_search([1,2,3,6,7],6)
#print(i)

复杂度:O(n)

方法二:二分查找

二分查找:又叫折半查找,从有序列表的初始候选区li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。

# 二分法查找
def binary_search(li,val):
    left,right = 0,len(li)-1
    while left<=right:  # 候选区有值
        mid = (left+right)//2
        if li[mid]==val:
            return mid
        elif li[mid]<val:
            left = mid+1
        else:
            right = mid=1
    else:
        return None

        
i = binary_search([1,2,3,6,7],10)
print(i)

复杂度:O(logn)

对比:使用二分查找的速度远远大于顺序查找,但是二分查找只能应用于线性排序的列表。

列表排序

排序:将一组“无序”的记录序列调整为“有序的记录序列”。

列表排序:将无序列表变为有序列表

  输入:列表

  输出:有序列表

升序和降序

内置排序函数:sort()

冒泡排序

列表每两个相邻的数,如果前面比后面大,则交换这两个数。

一趟排序完成后,则无序区减少一个数,有序区增加一个数。

代码关键:趟,无序区范围。

#冒泡排序

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 = [random.randint(1,1000) for _ in range(30)]
print(li)
bubble_sort(li)
print(li)

时间复杂度:O(n^2)

优化冒泡排序
#冒泡排序,当列表不再改变时 无需再循环

def bubble_sort(li):
    for i in range(len(li)-1):
        exchange = False
        for j in range(len(li)-i-1):
            if li[j] > li[j+1]:
                li[j],li[j+1] = li[j+1],li[j]
                exchange = True
        print(li)
        if not exchange:
            return

        
import random

li = [1,2,3,7,6,8,9]
print(li)
bubble_sort(li)

选择排序

每次选出列表中最大值或者最小值,放在列表的首位或者末尾。

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[min_loc],li[i] = li[i],li[min_loc]

li = [1,2,3,7,6,8,9]
print(li)
select_sort(li)
print(li)

插入排序

拿到手里的牌和之前的所有牌比较,如果比之前的牌小,则记录该位置,最后记录的值就是手里的牌正确排序后的位置。

def insert_sort(li):
    for i in range(len(li)):
        tmp = li[i]
        j = i - 1  # 候选区开头索引-1 就是有序区的结尾
        while j >= 0 and tmp < li[j]:
            li[j + 1] = li[j]
            j -= 1
        li[j + 1] = tmp


li = [9, 5, 6, 7, 3]
insert_sort(li)

快速排序

快速排序:快

快速排序思路:

  取一个元素p(第一个元素),使元素p归位;

  列表别p分成两部分,左边都比p小,右边都比p大;

  递归完成排序。

def partition(li,left,right):
    temp = li[left]
    while left < right:
        while left < right and li[right] >= temp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= temp:
            left += 1
        li[right] = li[left]
    li[left] = temp
    return left


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

li = [9,4,3,6,8,15,3,31]
quick_sort(li,0,len(li)-1)
print(li)

快速排序的效率:

  快速排序的时间复杂度 O(mlogn)

快速排序的问题:

  最坏情况
  递归

堆排序

树与二叉树

堆是一种特殊的完全二叉树

  大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
  小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小

堆的向下调整:
  假设:节点的左右子树都是堆,但是自身不是堆。

堆排序过程

堆排序过程:

  1. 建立堆。
  2. 得到堆顶元素,为最大元素。
  3. 去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序。
  4. 堆顶元素为第二大元素。
  5. 重复步骤3,知道堆变空。

堆的向下调整

向下调整函数

def sift(li,low,high):
    i = low
    tmp = li[i]
    j = i*2+1
    while j<=high:
        if j<high and li[j+1]>li[j]:
            j = j+1
        if tmp<=li[j]:
            li[i]=li[j]
            i = j
            j = i*2+1
        else:
            break
    li[i] = tmp
li = [1,9,8,7,6,5,0,2]
sift(li,0,len(li)-1)
print(li)

堆排序函数

def heap_sort(li):
    n = len(li)
    # 建堆
    for i in range(n//2-1,-1,-1):
        sift(li,i,n-1)
    # 建堆完成
    for i in range(n-1,-1,-1):
        li[i],li[0]=li[0],li[i]
        sift(li,0,i-1)

li = [2,6,4,6,8,5,3,10]
heap_sort(li)
print(li)
堆排序的内置模块heapq
import heapq
import random
li = list(range(100))
random.shuffle(li)
heapq.heapify(li)
print(li)
for i in range(len(li)-1):
	print(heapq.heappop(li),end=' ')

归并排序

假设li是以mid为分界线的左右两边都是正序列表的列表

从头比较两边分列表的值,较小的取出放在新列表里,再比较该分列表下一个值和另一列表的当前值大小

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
    li[low:high + 1] = ltmp + li[i:mid + 1] + li[j:high + 1]

def merge_sort(li, low, high):
    if low < high:
        mid = (low + high) // 2
        merge_sort(li, low, mid)
        merge_sort(li, mid + 1, high)
        merge(li, low, mid, high)

li = [3, 6, 4, 5, 8, 1, 2, 9, 0, 7]
merge_sort(li, 0, len(li) - 1)
posted @ 2018-10-08 20:49  Super-Yan  阅读(210)  评论(0编辑  收藏  举报