算法(二)- 算法应用

算法的稳定性:当列表中有重复元素时,重复元素的位置是否变化,变化则不稳定。
应用: 当有第多个关键字时。如:

name               age
Naruto             16
Hinata              16

想要排序,则先按name进行一次排序,然后按age进行一次稳定的排序(如果不稳定,可能会打乱排好的顺序)
tips:快排是不稳定的,所以当重复元素很多时,很慢。

算法(二)- 算法应用_第1张图片
小总结

计数排序

计数排序的时间复杂度是O(N),但他是有条件的:元素的值必须在一个已经给定的范围内。
code.

def count_sort(li, max_num):
    # 开设一个指定长度的列表,全部填充 0,max_num 也会在列表中,所以要加一
    count = [0] * (max_num + 1)
    # 等价于
    # count = [ 0 for i in range(max_num + 1)]

    # 每从原列表li来一个数,就在新列表count中,以那个数做下标,给他的值+1(计数)
    for num in li:
        count[num] += 1

    # 设定一个i,用来存原数组li的下标
    i = 0
    # 在列表count中,下标num 是数,值frequency是这个数出现的次数
    for num, frequency in enumerate(count):
        # 按次数进行循环,然后将数放到li列表中,每放一次,原数组l的下标i加一,j没有实际意义
        for j in range(frequency):
            li[i] = num
            i += 1

复杂度O(N),原因: N是li的长度,而所有的 count * frequency 加起来就是 N

应用:现在有一个列表,列表中的数范围都在0到100之间,列表长度大约为100万。设计算法在O(n)时间复杂度内将列表进行排序。(如按年龄排序)

count_sort(li, 100)

问题:
现在有n个数,设计算法按大小顺序得到前十大的数(榜单Top10)。
思路一:进行一次排序,取后10个(60分)
思路二:拿到一个包含10个数的列表(如原列表前面10个数),进行一次插排,然后每次遍历一个数,就进行一次插排。因为只有10个数,所以插排排序一次也只操作这10个数,复杂度O(10N) = O(KN) (80分)
code.

# 一趟插入
def insert(li, i):
    tmp = li[i]
    j = i - 1
    while j >= 0 and li[j] > tmp:
        li[j+1] = li[j]
        j = j - 1
    li[j+1] = tmp

def insert_sort(li):
    for i in range(1, len(li)):
        insert(li, i)

def topk(li, k):
        # 取前k+1个数,多一个数用来存储即将进来进行排序的数
    top = li[0: k+1]
    insert_sort(top)
    for i in range(k+1, len(li)):
        top[k] = li[i]
        insert(top, k)
    return top[:-1]

思路三:因为是第一次出最大的,第二次出第二大的,类似堆排序的特性,所以取列表前十个元素建立一个小根堆(因为要从大到小排),堆顶就是目前第十大的数(这十个里最小的)。
然后依次遍历原列表后面的数,对于列表中的元素,如果小于堆顶,则忽略该元素,大于则对调,对调后进行一次调整。
当完成遍历后,倒序弹出堆。复杂度为 O(NlogK)(99分)

code.
# 小根堆的调整
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 topn(li, n):
1   heap = li[0: n]
2   for i in range(n//2-1, -1, -1):      1 2 3为建堆 
3       sift(heap, i, n-1)

4   for i in range(n, len(li)):
5       if li[i] > heap[0]:
6           heap[0] = li[i]              4 5 6 7 为与堆顶比较
7           sift(heap, 0, n-1) 

8   for i in range(n-1, -1, -1):
9       heap[0], heap[i] = heap[i], heap[0]  8 9 10 11 为出数
10      sift(heap, 0, i-1)
11  return heap

堆的应用 - 优先队列
优先队列:一些元素的集合,pop(弹出)操作每次执行都会从优先队列中弹出最大(或最小)的元素。
Python内置模板 heapq
code.

import heapq
import random

heap = []
data = list(range(1000))
random.shuffle(data)
for num in data:
    heapq.heappush(heap, num)       ->        heapq.heappush(heap, item),每传入一个item,就进行一次调整,让他变成小根堆
for i in range(len(data)):
    heapq.heappop(heap)             ->        heapq.heappop(heap),按升序依次弹出堆内元素

还有
heapq.nlargest(n, data),Topn
heapq.nsmallest(n, data),最小的n个数

你可能感兴趣的:(算法(二)- 算法应用)