栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python数据结构与算法分析(第2版) —— 第4章 递归 and 第5章 搜索和排序

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python数据结构与算法分析(第2版) —— 第4章 递归 and 第5章 搜索和排序

Python数据结构与算法分析(第2版) —— 第4章 递归 and 第5章 搜索和排序

文章目录
    • Python数据结构与算法分析(第2版) —— 第4章 递归 and 第5章 搜索和排序
      • 第4章 递归
        • 4.2.2 递归三原则
        • 4.2.3 将整数转换成任意进制的字符串
      • 4.4 递归可视化
      • 4.5 复杂的递归问题
      • 4.6 探索迷宫
      • 4.7 动态规划
        • 题解
      • 第5章 搜索和排序
        • 5.2.1 无序列表的顺序搜索(时间复杂度O(n))
        • 5.2.2 二分搜索
        • 5.2.3 散列
          • 1. 散列函数
          • 2. 处理冲突
          • 3.实现映射抽象数据类型
          • 4.分析散列搜索算法
      • 5.3 排序
        • 5.3.1 冒泡排序
        • 5.3.2 选择排序
        • 5.3.3 插入排序
        • 5.3.4 希尔排序
        • 5.3.5 归并排序
        • 5.3.6 快速排序

第4章 递归 4.2.2 递归三原则
  1. 递归算法必须有基本情况
  2. 递归算法必须改变其状态并向基本情况靠近
  3. 递归算法必须递归地调用自己
4.2.3 将整数转换成任意进制的字符串
def toStr(n , base):
    convertString = "0123456789ABCDEF"

    if n < base:
        return convertString[n]
    else:
        return toStr(n // base,base) + convertString[n % base]

x = 12
print(toStr(x,2))
4.4 递归可视化
  • 使用turtle模块递归地绘制螺旋线
from turtle import *

myTurtle = Turtle()
myWin = myTurtle.getscreen()

def drawSpiral(myTurtle,lineLen):
    if lineLen > 0:
        myTurtle.forward(lineLen)
        myTurtle.right(90)
        drawSpiral(myTurtle,lineLen - 5)

drawSpiral(myTurtle,100)
myWin.exitemclick()
  • 绘制分形树
from turtle import *

myTurtle = Turtle()
myWin = myTurtle.getscreen()

def tree(branchLen,t):
    if branchLen > 5:
        myTurtle.forward(branchLen)
        myTurtle.right(20)
        tree(branchLen - 15,myTurtle)
        myTurtle.left(40)
        tree(branchLen - 10,myTurtle)
        myTurtle.right(20)
        myTurtle.backward(branchLen)

myTurtle.left(90)
myTurtle.up()
myTurtle.backward(300)
myTurtle.down()

myTurtle.color("green")
tree(110,myTurtle)
myWin.exitonclick()
  • 绘制谢尔平斯基三角形
from turtle import *

def drawTriangle(points,color,myTurtle):
    myTurtle.fillcolor(color)
    myTurtle.up()
    myTurtle.goto(points[0])
    myTurtle.down()
    myTurtle.begin_fill()
    myTurtle.goto(points[1])
    myTurtle.goto(points[2])
    myTurtle.goto(points[0])
    myTurtle.end_fill()

def getMid(p1,p2):
    return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)

def sierpinski(points , degree , myTurtle):
    colormap = ["blue","red","green","white","yellow","violet","orange"]

    drawTriangle(points,colormap[degree],myTurtle)
    if degree > 0:
        sierpinski([points[0],
                   getMid(points[0],points[1]),
                   getMid(points[0],points[2],)],
                   degree - 1,
                   myTurtle
                   )
        sierpinski([points[1],
                    getMid(points[0], points[1]),
                    getMid(points[1], points[2], )],
                   degree - 1,
                   myTurtle
                   )
        sierpinski([points[2],
                    getMid(points[2], points[1]),
                    getMid(points[0], points[2], )],
                   degree - 1,
                   myTurtle
                   )
myTurtle = Turtle()
myWin = myTurtle.getscreen()
myPoints = [(-500,-250),(0,500),(500,-250)]
sierpinski(myPoints,5,myTurtle)
myWin.exitonclick()

4.5 复杂的递归问题
  • 汉诺塔

用Python解决汉诺塔问题

def moveDisk(fromPole, toPole):
    print("Moving disk from %d to %d" % (fromPole,toPole))

def moveTower(height , fromPole , toPole , withPole):
    if height >= 1:
        moveTower(height - 1,fromPole,withPole,toPole)
        moveDisk(fromPole,toPole)
        moveTower(height - 1,withPole,toPole,fromPole)

if __name__ == '__main__':
    plate = 5
    moveTower(5,1,3,2)
4.6 探索迷宫

递归方法解决

4.7 动态规划

经典问题:换硬币

假设某个自动售货机制造商希望在每笔交易中给出最少的硬币。

一个顾客使用一张一美元的纸币购买了价值37美分的物品,问最少需要找给顾客多少硬币?

答案:6枚【25美分的2枚,10美分的1枚,1美分的3枚】

贪婪算法:从面值最大的硬币(25美分)开始,使用尽可能多的硬币,然后尽可能多地使用面值第二大的硬币 → 试图最大程度地解决问题。

  • 使用递归解决找零问题
def recMC(coinValueList,change):
    minCoins = change
    if change in coinValueList:
        return 1
    else:
        for i in [c for c in coinValueList if c <= change]:
            numCoins = 1 + recMC(coinValueList,change - i)
            if numCoins < minCoins:
                minCoins = numCoins
    return minCoins

# 硬币种类
coins = [1,5,10,25]
# 需要凑出
target = 63
print(recMC(coins,target))

67716925次递归调用才能找到最优解

  • 使用动态规划解决找零问题
def dpMakeChange(coinValueList , change , minCoins):
    
    for cents in range(change - 1):
        coinCount = cents
        
        for j in [c for c in coinValueList if c <= cents]:
            if minCoins[cents - j] + 1 < coinCount:
                coinCount = minCoins[cents - j] + 1
        minCoins[cents] = coinCount
    return minCoins[change]
  • 力扣322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

题解
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1)
        # dp[i] 代表凑出i块钱,最少需要的硬币个数
        dp[0] = 0 # 凑0块,一个硬币也不要

        # 最后一步的思想
        for coin in coins:# 遍历所有硬币种类

            for i in range(coin , amount + 1): # 从当前硬币种类开始,i代表要凑的金额数
                # dp[金额数 - 当前coin]代表除去coin后,凑齐[ ]金额还需要的硬币个数,再和dp[i]求最小值
                dp[i] = min(dp[i] , dp[i - coin] + 1)
        if dp[amount] != float("inf"):
            return dp[amount]
        else:
            return - 1
第5章 搜索和排序 5.2.1 无序列表的顺序搜索(时间复杂度O(n))
def sequentialSearch(alist,item):
    pos = 0
    found = False

    while pos < len(alist) and not found:
        if alist[pos] == item:
            found = True
        else:
            pos = pos + 1
    return found

myList = [1,7,4,5,2]
target = 4
print(sequentialSearch(myList,target))

无序列表的顺序搜索,略

5.2.2 二分搜索
  • 递归版本(时间复杂度为对数阶)
def binnarySearch(alist,item):
    if len(alist) == 0:
        return False

    else:
        midpoint = len(alist) // 2
        if alist[midpoint] == item:
            return True
        else:
            if item < alist[midpoint]:
                return binnarySearch(alist[:midpoint],item)
            else:
                return binnarySearch(alist[midpoint:],item)
            
myList = [1,2,3,4,5]
target = 4
print(binnarySearch(myList,target))
5.2.3 散列

通过散列可以构建一个查找目标元素时间复杂度为O(1)的数据结构

散列表:一个元素集合,其中的元素以一种便于查找的方式存储,散列表中的每个位置通常被称为槽,其中可以存储一个元素。从0开始整数标记每一个槽。

散列函数将散列表中的元素与其所属位置对应起来。

对散列表中的任一元素,散列函数返回一个介于0 ~ m-1【m为元素个数】之间的整数

  • 取余函数:使用余数作为散列值:即用元素 % 表的大小

    槽的占用率称作载荷因子 λ = 元素个数 / 散列表大小

    搜索目标函数时,仅需使用散列函数计算出槽编号,并查看对应的槽中是否有值。

    有时候,散列函数会将两个元素放入同一个槽 → 冲突(碰撞)

1. 散列函数

给定一个元素集合,能将每个元素映射到不同的槽,这种散列函数称作完美散列函数。

构建完美散列函数的一个方法是增大散列表,使之能容纳每一个元素,这样就能保证每个元素都有属于自己的槽【可能空间浪费】

目标:创建这样一个散列函数:冲突数最少,计算方便,元素均匀分布于散列表中。

→ 扩展取余函数

  • 折叠法
  • 平方取中法

!散列函数一定要高效,以免它成为存储和搜索过程的负担。如果散列函数过于复杂,计算槽编号的工作量可能比在进行顺序搜索或二分搜索时的更大,这不是散列的初衷。

2. 处理冲突

当两个元素被分到同一个槽时,必须通过一种系统化方法在散列表中安置第二个元素

  • 开放定址法:尝试在散列表中寻找下一个空槽或地址→又叫线性探测

    缺点:聚集现象。如果一个槽发生太多冲突,线性探测会填满其附近的槽。

  • 扩展线性探测

    再散列:泛指发生冲突后,寻找另一个槽的过程。

  • 平方探测

    不采用固定的跨步,通过再散列函数递增散列值

  • 链接法:允许散列表同一个位置上存在多个元素。发生冲突,仍进行插入【链条状】

3.实现映射抽象数据类型

Python使用两个列表实现HashTable类

class HashTable:
    def __init__(self):
        #指定散列表初始大小
        self.size = 11
        self.slots = [None] * self.size
        self.data = [None] * self.size

    def put(self,key,data):
        hashvalue = self.hashfunction(key,len(self.slots))
        if self.slots[hashvalue] == None:
            self.slots[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data
            else:
                nextslot = self.rehash(hashvalue,len(self.slots))
                while self.slots[nextslot] != None and self.slots[nextslot] != key:
                    nextslot = self.rehash(nextslot,len(self.slots))
                    
                    if self.slots[nextslot] == None:
                        self.slots[nextslot]= key
                        self.data[nextslot] = data
                    else:
                        self.data[nextslot] = data
                        
    def get(self,key):
        startslot = self.hashfunction(key,len(self.slots))
        data = None
        stop = False
        found = False
        
        position = startslot
        while self.slots[position] != None and not found and not stop:
            if self.slots[position] == key:
                found = True
                data = self.data[position]
            else:
                position = self.rehash(position,len(self.slots))
                if position == startslot:
                    stop = True
        return data
    def __getitem__(self, key):
        return self.get(key)
    
    def __setitem__(self, key, data):
        self.put(key,data)
    
    def hashfunction(self,key,size):
        return key % size
    
    def rehash(self,oldhash,size):
        return (oldhash + 1) % size
4.分析散列搜索算法 5.3 排序 5.3.1 冒泡排序

冒泡排序多次遍历列表。它比较相邻的元素,将不合顺序的交换。每一轮遍历都将下一个最大值放到正确的位置上。本质上,每个元素通过“冒泡”找到自己所属的位置。

  • Python实现冒泡排序
def bubbleSort(alist):
    for passnum in range(len(alist) - 1, 0 ,-1):
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp
5.3.2 选择排序

选择排序在冒泡排序的基础上做了改进,每次遍历列表时只做一次交换。要实现这一点,选择排序在每次遍历时寻找最大值,并在遍历完之后将它放到正确位置上。

  • Python实现选择排序
def selectionSort(alist):
    for fillslot in range(len(alist - 1),0,-1):
        positionOfMax = 0
        for location in range(1,fillslot + 1):
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location
        
        temp  = alist[fillslot]
        alist[fillslot] = alist[positionOfMax]
        alist[positionOfMax] = temp
5.3.3 插入排序

在列表较低的一端维护一个有序的子列表,并逐个将每个新元素“插入”这个子列表。图5-16展示了插入排序的过程。

  • Python实现插入排序
def insertionSort(alist):
    for index in range(1,len(alist)):
        
        currentvalue = alist[index]
        position = index
        
        while position > 0 and alist[position -1 ] > currentvalue:
            alist[position] = alist[position - 1]
            position = position - 1
        
        alist[position] = currentvalue
5.3.4 希尔排序

希尔排序也称“递减增量排序”,它对插入排序做了改进,将列表分成数个子列表,并对每一个子列表应用插入排序。如何切分列表是希尔排序的关键——并不是连续切分,而是使用增量i(有时称作步长)选取所有间隔为i的元素组成子列表。

  • 使用Python实现希尔排序
def gapInsertionSort(alist, start, gap):
    for i in range(start + gap,len(alist),gap):
        currentvalue = alist[i]
        position = i
        
        while position >= gap and alist[position - gap] > currentvalue:
            alist[position] = alist[position - gap]
            position = position - gap
        alist[position] = currentvalue

def shellSort(alist):
    sublistcount = len(alist) // 2
    while sublistcount > 0:
        for startposition in range(sublistcount):
            gapInsertionSort(alist,startposition,sublistcount)
        print("After increments of size",sublistcount,"The list is ",alist)
        
        sublistcount = sublistcount // 2
5.3.5 归并排序

递归算法,每次将一个列表一分为二。如果列表为空或只有一个元素,那么从定义上来说它就是有序的(基本情况)。如果列表不止一个元素,就将列表一分为二,并对两部分都递归调用归并排序。当两部分都有序后,就进行归并这一基本操作。归并是指将两个较小的有序列表归并为一个有序列表的过程。

  • 用Python实现归并排序
def mergeSort(alist):
    print("Splitting " , alist)
    if len(alist) > 1:
        mid = len(alist) // 2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthalf)
        mergeSort(righthalf)

        i = 0
        j = 0
        k = 0
        while i < len(lefthalf) and j < len(righthalf):
            if lefthalf[i] < righthalf[j]:
                alist[k] = lefthalf[i]
                i += 1
            else:
                alist[k] = righthalf[j]
                j += 1
            k += 1
        while i < len(lefthalf):
            alist[k] = lefthalf[i]
            i += 1
            k += 1

        while j < len(righthalf):
            alist[k] = righthalf[j]
            j += 1
            k += 1
    print("Merging ",alist)
5.3.6 快速排序

快速排序算法首先选出一个基准值基准值的作用是帮助切分列表。在最终的有序列表中,基准值的位置通常被称作分割点,算法在分割点切分列表,以进行对快速排序的子调用。

  • 用Python实现快排
def partition(alist, first, last):
    pivotvalue = alist[first]
    
    leftmark = first + 1
    rightmark = last
    
    done = False
    while not done:
        while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
            leftmark = leftmark + 1
        while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
            rightmark = rightmark - 1
            
        if rightmark < leftmark:
            done = True
        else:
            temp = alist[leftmark]
            alist[leftmark] = alist[rightmark]
            alist[rightmark] = temp
    temp = alist[first]
    alist[first] = alist[rightmark]
    alist[rightmark] = temp
    
    return rightmark
def quickSortHelper(alist, first, last):
    if first < last:
        splitpoint = partition(alist,first,last)
        quickSortHelper(alist,first,splitpoint - 1)
        quickSortHelper(alist,splitpoint + 1,last)

def quickSort(alist):
    quickSortHelper(alist,0,len(alist) - 1)
暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1016048.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号