02:算法应用

算法其他篇

目录:

1.1 设计一个O(n)复杂度的算法     返回顶部

  1、问题:计数排序

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

  2、原理

      1、 必须知道这些数中最大的数是多少
      2、 然后生成一个长度等于最大数的列表
      3、 循环li列表中所有的数,li中每出现一次这个数就在生成的count列表中加一
      4、 这样就可以在count列表中的对应位置记录出列表li中所有数出现的次数
      5、 然后在循环count,这个数出现几次就依次写几个到li列表中,就出现下面效果

def count_sort(li, max_num):
   count = [0 for i in range(max_num + 1)]
   for num in li:
      count[num] += 1
   i = 0
   print('count',count)
   for num,m in enumerate(count):        #循环列表count,按顺序得到li中数出现次数
      for j in range(m):            #这个数出现多少次就在li中依次追加多少个
         li[i] = num
         i += 1
li = [3,4,5,3,2,4,5,6,7,4]
count_sort(li,10)
print(li)        #[2, 3, 3, 4, 4, 4, 5, 5, 6, 7]
答案

1.2 在大量数中找到前10个最大的数     返回顶部

  1、问题

      现在有n个数(n>10000),设计算法,按大小顺序得到前世大的数。

      应用场景:榜单TOP 10

  2、使用插入法解决思路(时间复杂度: O(kn))

      1、先取出待排序列表前11个元素,使用插入排序先有序
      2、然后依次循环未排序的数,一个个的替换TOP 10列表中的第11号元素,再进行一次插入排序
      3、这样仅循环了一次列表即可得到前11大的元素,最后列表切片,切出前10大元素即可

# 一趟插入
def insert(li,i):
   tmp = li[i]      # tmp是无序区取出的一个数
   j = i - 1        # li[j]是有序区最大的那个数
   while j >= 0 and li[j] < tmp:
      #li[j] < tmp 这个条件的大于号小于号控制是10个最大的数还是最小的数
      li[j + 1] = li[j]  # 将有序区最右边的数向右移一个位置
      j = j - 1
   li[j + 1] = tmp  # 将tmp放到以前有序区最大数的位置,再依次与前一个数比较

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

def topk(li,k):
   top = li[0:k+1]
   insert_sort(top)                #先将列表中的11个元素用插入法排序
   for i in range(k+1, len(li)):   #循环后面剩下的元素
      top[k] = li[i]              #依次取后面未排序的数放到top列表的最后面
      insert(top, k)              #然后再次对top进行依次排序
   return top[:-1]

li = list(range(10000))
import random
random.shuffle(li)
a = topk(li,10)
print(a)
插入法解决

  3、使用堆排序解决思路时间复杂度:O(nlog(k)

      1、取列表前10个元素建立一个小根堆,堆顶就是目前的10大的数(建立小根堆)
      2、依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,如果大于堆顶,则将堆顶更换为该元素,并且对堆进行依次调整
      3、遍历列表所有元素后,倒序弹出堆顶

import random
def sift(data, low, high):
   '''  构造小根堆  堆定义:堆中某节点的值总是不大于或不小于父节点的值
   :param data: 传入的待排序的列表
   :param low:  需要进行排序的那个小堆的根对应的号
   :param high: 需要进行排序那个小堆最大的那个号
   :return:
   '''
   i = low            #i最开始创建堆时是最后一个有孩子的父亲对应根的号
   j = 2 * i+ 1       #j子堆左孩子对应的号
   tmp = data[i]      #tmp是子堆中原本根的值(拿出最高领导)
   while j <= high:   #只要没到子堆的最后(每次向下找一层)  #孩子在堆里
      if j + 1 <= high and data[j] > data[j + 1]: #如果有右孩纸,且比左孩子大
         j += 1
      if tmp > data[j]:       #如果孩子还比子堆原有根的值tmp大,就将孩子放到子堆的根
         data[i] = data[j]    #孩子成为子堆的根
         i = j                #孩子成为新父亲(向下再找一层)
         j = 2 * i + 1        #新孩子  (此时如果j<=high证明还有孩,继续找)
      else:
         break    #如果能干就跳出循环就会流出一个空位
   data[i] = tmp               #最高领导放到父亲位置

def topn(li,n):
   heap = li[0:n]                #先取10个元素
   for i in range(n//2 -1, -1, -1):    #使用这10个元素构建出一个小根堆
      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):        # 上面的for循环已经找出了前十大元素,这里是排序
      heap[0], heap[i] = heap[i], heap[0]
      sift(heap, 0, i-1)
   return heap
li = list(range(1000))
import random
random.shuffle(li)
a = topn(li,10)
print(a)                                #[999, 998, 997, 996, 995, 994, 993, 992, 991, 990]
堆排序解决
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import random

def sift(data, low, high):
    '''  构造小根堆 : 堆中某节点的值总是不小于父节点的值
    :param data: 传入的待排序的列表
    :param low:  需要进行排序的那个小堆的根对应的号
    :param high: 需要进行排序那个小堆最大的那个号
    :return:
    '''
    i = low                # i最开始创建堆时是最后一个有孩子的父亲对应根的号
    j = 2 * i + 1          # j子堆左孩子对应的号
    tmp = data[i]          # tmp是子堆中原本根的值(拿出最高领导)
    while j <= high:       # 只要没到子堆的最后(每次向下找一层)孩子在堆里
        if j + 1 <= high and data[j] > data[j + 1]:  # 如果有右孩纸,且比左孩子更小
            j += 1
        if tmp > data[j]:         # 如果孩子还比子堆原有根的值tmp小,就将孩子放到子堆的根
            data[i] = data[j]     # 孩子成为子堆的根
            i = j                 # 孩子成为新父亲(向下再找一层)
            j = 2 * i + 1         # 新孩子  (此时如果j<=high证明还有孩,继续找)
        else:
            break                # 如果能干就跳出循环就会流出一个空位
    data[i] = tmp                 # 最高领导放到父亲位置

def topn(li, n):
    # 1、构建10个数量的小根堆
    heap = li[0:n]                        # 先取10个元素
    for i in range(n // 2 - 1, -1, -1):   # 使用这10个元素构建出一个小根堆
        sift(heap, i, n - 1)

    # 2、找出li中前10大元素放入heap中
    for i in range(n, len(li)):          # 依次取出剩下的元素
        if li[i] > heap[0]:              # 如果新元素比堆顶元素大
            heap[0] = li[i]              # 就将新元素替换堆顶元素
            sift(heap, 0, n - 1)         # 重新构建小根堆

    # 3、利用堆排将这个小根堆倒序排列
    for i in range(n - 1, -1, -1):       # 上面的for循环已经找出了前十大元素,这里是排序
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i - 1)
    return heap

li = list(range(1000))
import random
random.shuffle(li)

print topn(li, 10)        # [999, 998, 997, 996, 995, 994, 993, 992, 991, 990]
堆排找前10大元素

  4、使用python自带的heapq模块找到前十大元素

import heapq
import random
heap = []
data = list(range(1000))
random.shuffle(data)
print(heapq.nlargest(10,data))
# [999, 998, 997, 996, 995, 994, 993, 992, 991, 990]
heapq模块解决

1.3 其他     返回顶部

  1、问题

      1. 在一个有重复数的升序列表中找到指定数的下标范围

      2. 例如:列表[1,2,3,4,5],若查找3,则返回(2,4)若查找1,则返回(0,0)

l = list(range(1,101))
def bin_search(data_set,val):
   low = 0
   high = len(data_set) - 1
   while low <= high:
      mid = (low+high)//2
      if data_set[mid] == val:
         left = mid
         right = mid
         while left >=0 and data_set[left] == val:
            left -= 1
         while right < len(data_set) and data_set[right] == val:
            right += 1
         return (left+1,right-1)
      elif data_set[mid] < val:
         low = mid + 1
      else:
         high = mid - 1
   return
n = bin_search(l,2)
print(n)                         # 返回1在列表中的下标范围: (1, 1)
答案

  2、在列表中找到两个数的和等于给出的数(返回找到的下标)

import copy
li = [1,2,4,3,5,]

target = 5
def bin_search(data_set,val,low,high):
   while low <= high:
      mid = (low+high)//2
      if data_set[mid] == val:
         return mid
      elif data_set[mid] < val:
         low = mid + 1
      else:
         high = mid - 1
   return

def func2():
   li2 = copy.deepcopy(li)
   li2.sort()
   for i in range(len(li2)):
      a = i
      b = bin_search(li2, target - li2[a], i+1, len(li2)-1)
      if b:
         return (li.index(li2[a]),li.index(li2[b]))
print(func2())                                             # (0, 2)
答案

  3、新建一个列表存储当前列表的下标,当前列表的值作为新下标的号

    1. 原理
      1、比如现在有列表 li1 = [2,1,4] ,使用这种方法必须知道li1中最大的数是多少(比如这里最大的数是:6)
      2、现在新建一个列表初始值 li2 = [None,None,None,None,None]
      3、使用for循环遍历li1,第一次取到li1[0]中的2,就在li2中设置li2[2] = 0
      4、第二次取到的是1就在li2[1]=0
      5、第三次取到的是4就在li2[4]=0
      6、最后得到的li2 = [None,0,0,None,0]

    2. 根据上面原理可以在列表中找到两个数的和等于给出的数(返回找到的下标)
      注:使用这种方法必须知道当前列表中的最大值

import copy
li = [1,2,4,3,5,]

target = 3
max_num = 10

def func():
   a = [None for i in range(max_num+1)]    #生成一个新列表a,列表长度是li中最大值,初始全为None
   for i in range(len(li)):
      a[li[i]] = i
      if a[target-li[i]] != None:
         return (a[li[i]], a[target-li[i]])
print(func())
答案

 

posted @ 2018-03-15 16:55  不做大哥好多年  阅读(548)  评论(2编辑  收藏  举报