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三人组-小结

小结:挨着换的稳定

 


快排、归并必须理解!
排序理解:思路,代码写法,时间复杂度

下周:不常用的排序方式:希尔排序、计数排序、桶排序、基数排序
posted @ 2019-01-08 17:20  yuyou123  阅读(131)  评论(0编辑  收藏  举报