【学习日志】202403w3\数据处理和算法

20230320

b站《清华大学博士讲解python数据结构与算法》笔记

案例:汉诺塔

塔 A、B、C,A上有n个盘(下大上小),将盘移到C塔上,要求:每次只能移动一个盘;大盘不能在小盘上。
分析:如果有两个盘(1(下),2(中),3(上)),移动顺序应为:盘3从A移动到C,盘2从A移动到B,盘3从C移动到B,盘1从A移动到C,盘3从B移动到A,盘2从B移动到C,盘3从A移动到C
归纳来说:若有n个盘,上面n-1个盘整体操作是从A盘经过C盘到B盘,第n个盘从A到C,再将n-1个盘从B经过A移动到C。

def hanoi(n,a,b,c):
    if n>0:
    	# 上面n-1个盘整体操作:从A盘经过C盘到B盘
        hanoi(n-1,a,c,b)
        # 第n个盘从A到C盘
        print('moving from %s to %s' % (a,c))
        # 将n-1个盘从B经过A移动到C
        hanoi(n-1,b,a,c)
        
hanoi(3, 'A','B','C')

查找

顺序查找

从列表按索引顺序进行查找,时间复杂度O(n)。

for idx, val in enumerate(nums):
	if val == target:
		return idx
else: return -1
二分查找

有序列表(升序)的初始候选区开始,对目标值和候选区中间值进行比较,使候选区减少一半。时间复杂度O(log n)
维护候选区: left = 0, right = len(nums)-1, mid = (right+left+1)%2
target < mid —> right = mid -1, mid =
target > mid —> left = mid+1, mid=

def binary_search(li, val):
    left = 0
    right = len(li)-1
    while left<=right:
        mid = (left+right)//2
        if li[mid] == val:
            return mid
        elif: li[mid]>val:
            right = mid - 1
        elif li[mid]<val:
            left = mid + 1
    return None
排序

将无序列表变成有序列表,sort()

基础
1. 冒泡排序 ( O ( n 2 ) O(n^2) O(n2)
  • list 相邻的两个数,若前面一个比后面大,则交换这两个数。时间复杂度O(n^2)
  • 一趟排序后,无序区减少一个数,有序区增加一个数(该趟遍历的最大的数)
    • 第一趟:无序区0 : len(nums)-1;有序区len(nums)-1:len(nums)
    • 第二趟:无序区0:len(nums)-1-1;有序区len(nums)-1-1:len(nums)
    • 第三趟:无序区0:len(nums)-1-2;有序区len(nums)-1-2:len(nums)
    • 共需n-1趟, range(len(nums)-1),无序有序分界为:len(nums)-1-i
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
	    if not exchange:
	    	rerturn
2. 选择排序 ( O ( n 2 ) O(n^2) O(n2)
  • 遍历列表,需n-1趟,在无序区找到当前趟最小的数,放在有序区
def select_sort(li):
    for i in range(len(li)-1):
        min_idx = li.index(min(li[i:len(li)]))
        li[i],li[min_idx]= li[min_idx],li[i]
    return li

li = [3,4,8,1,5,2,9]
select_sort(li)
3. 插入排序 ( O ( n 2 ) O(n^2) O(n2)
  • 初始时有序区只有1张牌,每次从无序区摸一张牌,插入到有序区正确位置
def insert_sort(li):
    for i in range(1,len(li)): #i——摸到牌的下标
        tmp = li[i]  # 存储摸到的牌
        j = i - 1 # 手里牌(有序区)的下标
        while j>=0 and  li[j]>tmp: # 边界条件和右移条件
            li[j+1] = li[j]  #有序区右移
            j -= 1
        li[j+1] = tmp # 填补
    return li
li = [0,4,8,1,5,2,9]
insert_sort(li)
中阶
1. 快速排序( O ( n l o g n ) O(nlogn) O(nlogn)
  • 遍历列表,取元素i,使元素i归位——左边的元素比i小,右边的元素比i大
def partition(li, left, right):
    tmp = li[left]  # 存储第一个位置
    while left < right:# left == right 时停止循环
        #从右边找一个比tmp小的数放到左边
        while left < right and li[right]>=tmp:
            right -= 1 # 往左走一步
        li[left] = li[right]  # 找到比tmp小的数,放到left的空位上
        while left < right and li[left]<=tmp: # 找左边比tmp大的数
            left += 1  # 比tmp小,left往右走一步
        li[right] = li[left] # 把左边的值写到右边空位上
    li[left]  = tmp  # 把tmp归位
    return left
    
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)  # 右边候选区归位    
    
li = [0,4,8,1,5,2,9]
quick_sort(li,0, len(li)-1)
2. 堆排序( O ( l o g n ) O(logn) O(logn)
  • 二叉树介绍
    • 根节点、叶子节点、树的深度、树的度、孩子节点/父节点、子树
    • 满二叉树:每个层的结点 数都达到最大值
    • 完全二叉树:叶节点只出现在最下层和次下层,且最下层的结点都集中在盖层最左边。
      • 父节点 i 和左孩子节点的编号下标规律: i——2i+1,
      • 孩子节点找父节点:孩子i——父亲 (i-1)// 2
  • :特殊的完全二叉树
    • 大根堆:完全二叉树,任一节点都比其孩子节点大
    • 小根堆:完全二叉树,任一节点都比其孩子节点小
    • 堆的向下调整:当根节点的左右都是堆,但自身不是堆,可以通过一次向下调整将其变成堆。
  • 堆排序的过程
    • 建立堆(挨个出数,根节点是最大的数)
def sift(li, low, high):
    """
    :param li: 列表
    :param low: 堆的根节点位置
    :param high:堆的最后一个元素的位置
    :return:
    """
    i = low  # i指向根节点
    j = 2 * i + 1 # j是i的左孩子节点
    tmp = li[low] #把堆顶存起来
    while j<=high: #只要j位置有数
        if j + 1 <= high and li[j+1] > li[j]:  #右孩子(存在的话)大于左孩子,不能换左右节点
            j = j + 1 # j指向右孩子
        if li[j] > tmp:
            li[i] = li[j]  # 替换根节点
            i = j  # 往下看一层
            j = 2 * i +1
        else:  #tmp更大,把tmp放在i的位置上
            li[i] = tmp # 把tmp放到某一级领导位置
            break
    else:
        li[i] = tmp #把tmp放在叶节点
        
def heap_sort(li):
    n = len(li)
    # 新建堆(农村包围城市)
    for i in range((n-2)//2,-1,-1): # 叶节点
        # i 代表建堆的时候 当前调整的部分  的根的下标
        sift(li,i,n-1)
    # 建堆完成
    print(li)
    # 挨个出数,取最大的数,把high放到最大的位置,调整成堆,取最大的数
    for i in range(n-1, -1, -1):
        #i指向当前堆的最后一个元素
        li[0], li[i] = li[i],li[0]
        sift(li, 0, i-1) # i-1是新的high
    
li = [i for i in range(10)]
import random
random.shuffle(li)
print(li)
heap_sort(li)
print(li)

python中有内置函数 heapq()

# 内置函数heapq
import random
import heapq

random.shuffle(li)
    
li = [i for i in range(10)]
print(li)

heapq.heapify(li)#建堆
# 每次pop出最小值
for i in range(len(li)):
    print(heapq.heappop(li), end=',')

topk问题
n个数,设计算法得到前k大的数(k<n)。
解法:利用小根堆,建立一个容量为100的小根堆,遍历list元素与堆的根节点作比较。小于根节点则忽略,大于根节点则取代根节点,对堆做调整…留在堆里的则为前100大的数

def sift(li, root, tail):
    i = root
    j = 2 * i + 1
    tmp = li[root]
    while j <= tail:
        if j + 1 <= tail and li[j+1]<li[j]:
            j = j+1
        if li[j]<tmp:
            li[i]=li[j]
            i = j
            j = 2 * i + 1
        else:
            break
        li[i] = tmp
                
def topk(li,k):
    heap = li[0:k]
    # 建堆
    for i in range((k-1-1)//2, -1, -1):
        sift(heap, i, k-1)
    # 遍历list,比较根节点大小
    for i in range(k,len(li)):
        if heap[0]<li[i]:
            heap[0]=li[i]
            sift(heap,0,k-1)
     # 出数
    for i in range(k-1,-1,-1):
        heap[0],heap[i] = heap[i],heap[0]
        sift(heap, 0, i-1)
    return heap
3. 归并排序( O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( n ) O(n) O(n)

list分段有序,输出有序数列

def merge(li,low,mid,high):
    i = low
    j = mid + 1
    tmp = []
    while i<=mid and j <= high:# 只要左右两边都有数,就比大小
        if li[i]<li[j]:
            tmp.append(li[i])
            i+=1
        else:
            tmp.append(li[j])
            j+=1
    # while 执行完,肯定有一部分没数
    while i<=mid: 
        tmp.append(li[i])
        i+=1
    while j<=high:
        tmp.append(li[j])
        j+=1
    li[low:high+1] = tmp
    
# 利用递归,将list分为单元素有序
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= [2,7,3,7,6,9,11]
merge_sort(li,0,len(li)-1)
print(li)
其他
1. 希尔排序(时间复杂度与gap有关)
# 希尔排序
def insert_sort_gap(li,gap):
    for i in range(gap,len(li)):# 摸到牌的下标
        tmp = li[i]
        j = i - gap # 手里最后一张牌的下标
        while li[j]>tmp and j>=0:
            li[j+gap]=li[j]     
            j -= gap
        li[j+gap] = tmp
    return li

def shell_sort(li):
    d = len(li)//2
    while d>=1:
        insert_sort_gap(li,d)
        d //= 2
                
li= [2,7,3,7,6,9,11]
shell_sort(li)
2. 计数排序

已知列表数范围在0到100之间,设计时间复杂度为 O ( n ) O(n) O(n)的算法。

def count_sort(li, max_count = 20):
    count = [0 for _ in range(max_count+1)]
    for val in li:
        count[val] +=1
    li.clear()
    for ind, val in enumerate(count):# 根据count结果生成list,ind为值,val为频次
        for i in range(val):
            li.append(ind)
                        
import random
li = [random.randint(0,10) for _ in range(20)]
print(li)
count_sort(li)

桶排序,如果列表数范围在亿级别。将不同元素分在不同桶中,再堆每个桶中的元素排序。

def bucket_sort(li,n=5,max_num=50):
    buckets = [[] for _ in range(n)] # 创建n个桶
    for val in li:  #将数列值分配进桶
        i = min(val // (max_num // n), n-1) # i 表示val放到几号桶里
        buckets[i].append(val) # 元素放进桶里
    # 思路一:对每个桶单独排序
    # 思路二:插入元素进桶的时候就排序
        for j in range(len(buckets[i])-1,0,-1): # 保持桶内顺序
            if buckets[i][j]<buckets[i][j-1]:
                buckets[i][j],buckets[i][j-1]=buckets[i][j-1],buckets[i][j]
            else:
                break
    sorted_li =[]
    for buc in buckets:
        sorted_li.extend(buc)
    return sorted_li

import random
li = [random.randint(0,50) for _ in range(50)]
print(li)
print(bucket_sort(li))
3. 基数排序( O ( k n ) O(kn) O(kn)
  • 多关键字排序
def radix_sort(li):
    max_num = max(li) #最大值 9—>1, 99->2, 999—>3...
    it = 0   # 0-个位数、2-十位数、3-百位数
    while 10 ** it <= max_num: #判断列表最大位数
        buckets = [[] for _ in range(10)]
        for val in li:  # 将元素放进桶里
            # 987 it=1 987//10=98 98%10 = 8 ; it=2 987//100 = 9 9%10 = 9
            digit = (val // 10 ** it) % 10
            buckets[digit].append(val)
        # 分桶完成
        li.clear()
        for bucket in buckets:
            li.extend(bucket)
        # 把数重新写回li
        it+=1

import random
li = [random.randint(0,100) for _ in range(100)]
print(li)
print(radix_sort(li))
print(li)
查找排序习题

在这里插入图片描述
解题:

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值