《算法很美》3.分治思想 (python实现) 【3】堆,二叉树,排序

二叉树简介
在这里插入图片描述
用数组表示一棵树,第一个元素是根节点,某一节点位置为i,其左子节点为2i+1,右子节点2i+2,父节点为(i-1)//2
树的遍历顺序:前序遍历,中序遍历,后序遍历(前中后对应的根的位置,前序遍历为根左右,中序遍历为左根右,后序遍历为左右根)

#树的三种遍历顺序
def preorder(alist,i):
    if i>len(alist)-1:
        return
    if i<=len(alist)-1:
        print(alist[i])               #根
        preorder(alist,2*i+1)         #左
        preorder(alist,2*i+2)         #右
def inorder(alist,i):
    if i>len(alist)-1:
        return
    if i<=len(alist)-1:
        preorder(alist,2*i+1)
        print(alist[i])
        preorder(alist,2*i+2)
def postorder(alist,i):
    if i>len(alist)-1:
        return
    if i<=len(alist)-1:
        preorder(alist,2*i+1)
        preorder(alist,2*i+2)
        print(alist[i])
textlist=[1,2,3,4,5,6,7]

堆排序
在这里插入图片描述
大顶堆:
在这里插入图片描述
对应到数组中:
在这里插入图片描述
思路:
将待排序序列构造成一个小顶堆,此时,整个序列的最小值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最小值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

#小顶堆,最后利用堆排序是倒序数组
def makeminheap(alist):
    for index in range(len(alist)//2-1,-1,-1):
        heapadjust(alist,index,len(alist)-1)
def heapadjust(alist,index,n):
    #找到左右子树
    left=2*index+1
    right=2*index+2
    #左子树已经越界,index对应的是叶子节点
    if left>n:
        return
    min=left
    if right>n:
        min=left
    else:
        if alist[right]<alist[left]:
            min=right

    #min指向了左右子树中较小的那个
    #如果A【i】比两个子树都要小,不用调整
    if alist[index]<=alist[min]:
        return
    #否则,找到两个子树中较小,进行交换
    else:
        alist[index],alist[min]=alist[min],alist[index]
    #较小子树位置对应的值发生了变化,index变更到那个位置,递归调整
    heapadjust(alist,min,n)

def heapsort(alist):
    makeminheap(alist)
    for i in range(len(alist)-1,0,-1):
        alist[0],alist[i]=alist[i],alist[0]
        heapadjust(alist,0,i-1)

计数排序
牺牲空间换时间
思路:找出原数组中元素值最大的,创建一个新全零数组,其长度是最大值加1,
遍历原数组中的元素,以原数组中的元素作为新全零数组的索引,以原数组中的元素出现次数作为新数组的对应索引的元素值。
遍历处理完的新t数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填到原数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素,返回原数组

textlist=[3,1,10,29,13,10]
maxnum=max(textlist)
helper=[0]*(maxnum+1)
for i in textlist:
    helper[i]+=1
n=0
while n<=len(textlist)-1:
    for i in range(len(helper)):
        while helper[i]>0:
            helper[i]-=1
            textlist[n]=i
            n+=1
print(textlist)

练习题:
例题1 排序数组找和的因子
在这里插入图片描述
如果只是简单想将所有情况组合看和的结果,那么复杂度为n!没有利用到排序数组这一条件
思路:(双指针)
确定两个指针指向数组两端,左指针向右移动,右指针向左移动,因为是排好顺序的,那么左右指针对应值相加。如果大于目标值,说明应该减小,那么,右指针左移;如果小于目标值,说明应该增大,那么,左指针右移;等于目标值则返回。

def solution(alist, target):
    left = 0
    right = len(alist) - 1
    res=[]
    while left < right:
        if alist[left] + alist[right] < target:     #如果小于目标值,左指针右移
            left += 1
        elif alist[left] + alist[right] > target:   #如果大于目标值,右指针左移
            right -= 1
        else:
            res.append(alist[left])
            res.append(alist[right])
            left+=1

    return res


textlist = [-8,-4,-3,0,2,4,5,8,9,10]
target = 10
print(solution(textlist, target))

例题2 需要排序数组的长度
在这里插入图片描述
思路:(双指针)
从左向右遍历,遍历的过程记录左侧出现过的数的最大值。记为maxnum。假设当前数为alist[i],如果alist[i]<maxnum,说明不是持续上升,开始下降,需要排序,maxnum的值需要移到alist[i]的右边。用变量end记录需要调整数组的右边界。
从右向左遍历,遍历的过程记录右侧出现过的数的最小值,记为minnum。假设当前数为alist[i],如果alsit[i]>minnum,minnum值需要排序,需要移到alisti]的左边。用变量start记录需要调整数组的左边界。
如果遍历完成后,end值依然为len(alist)-1,start的值依然为-1,说明从右向左始终不升序,原数组本来就有序,直接返回0,即完全不需要排序。
遍历完后,alist[start…end]是真正需要排序的部分。返回它的长度即可。

def findsegement1(alist,n):
    start=n-1
    end=0
    maxnum=alist[0]
    minnum=alist[-1]
    for index in range(n):        #从左向右遍历
        if alist[index]>=maxnum:     #更新最大值
            maxnum=alist[index]
        if alist[index]<maxnum:       #alist[i]<maxnum,
        							  #maxnum的值需要移到alist[i]的右边
            end=index     			  #用变量end记录需要调整数组的右边界
    for index2 in range(n-1,-1,-1):    #从右向左遍历
        if alist[index2]<=minnum:      #更新最小值
            minnum=alist[index2]
        if alist[index2]>minnum:      #alist[i]>minnum
        							  #minnum的值需要移到alist[i]的左边
            start=index2			# 用变量start记录需要调整数组的左边界
    if start==n-1 and end==0:      #原数组本来就有序,返回0
        return 0
    else:
        return end-start+1

题目没说排序最后排成什么顺序,也许排从小到大困难,但是排成从大到小简单
因此需要比较这两种排序哪种更短

def findsegement2(alist,n):
    start=n-1
    end=0
    minnum=alist[0]
    maxnum=alist[-1]
    for index in range(n):
        if alist[index]<=minnum:
            minnum=alist[index]
        if alist[index]>minnum:
            end=index
    for index2 in range(n-1,-1,-1):
        if alist[index2]>=maxnum:
            maxnum=alist[index2]
        if alist[index2]<maxnum:
            start=index2
    if start==n-1 and end==0:
        return 0
    else:
        return end-start+1

比较两种排序,返回其中较小值

例题3 topK问题
在这里插入图片描述
思路:海量数据无法直接储存,那就只储存k个数,这k个数按堆排列(大顶堆),当输入新数据,和数组第一个元素(根节点)比较,他是数组中最大的元素,如果比他小,将新加入的数据设为根节点值,在向下调整;如果比他大,则说明他无需加入。

k=int(input())     #正序排序后前k个数
numlist=[]
num=int(input())
while num!=-1:            #输入为-1时停止
    if len(numlist)<k:          #数组长度小于k
        numlist.append(num)     #继续添加数字
        makemaxheap(numlist)    #进行堆调整      (大顶堆)
    elif len(numlist)==k:           #数字长度等于k
        if num<numlist[0]:          #如果堆顶数字(堆顶数字代表的是最小的数字)小于待加入数字
            numlist[0]=num          #更改堆顶数字
            makemaxheap(numlist)    #进行堆调整
    num=int(input())
print(numlist)

例题4.员工年龄排序问题
在这里插入图片描述
思路:桶排序
所有员工的年龄都在一个范围内,例如0到99,初始化一个大小为100的数组alist(数组中所有元素初始为0),遍历所有员工年龄,将员工年龄的人数放到数组的对应位置上alist[0],将员工年龄为1的人数放到数组的第1个位置alist[1]…将员工年龄为99的人数放到数组的第99个位置alsit[99],遍历这个数组alist,将员工年龄(数组alist下标)重新编写到年龄数组中。代码参考桶排序

例题5.数组能排成的最小数

题解参考leetcode题解
排序判断规则: 设 nums 任意两数字的字符串格式 x和 y ,则
若拼接字符串 x + y > y + x,x+y>y+x ,则 m > n,m>n ;
反之,若 x + y < y + x,x+y<y+x ,则 n < m,n<m ;
根据以上规则,套用任何排序方法对 nums 执行排序即可

算法流程:
初始化: 字符串列表 strsstrs ,保存各数字的字符串格式;
列表排序: 应用以上 “排序判断规则” ,对 strs 执行排序;
返回值: 拼接 strs中的所有字符串,并返回。
此题求拼接起来的 “最小数字” ,本质上是一个排序问题。

为什么要按上述排序?
以下是证明:
根据算法,如果a < b,那么a排在b前面,否则b排在a前面。可利用反证法,假设排成的最小数字为xxxxxx,并且至少存在一对字符串满足这个关系:a > b,但是在组成的数字中a排在b前面。根据a和b出现的位置,分三种情况考虑:

(1)xxxxab,用ba代替ab可以得到xxxxba,这个数字是小于xxxxab,与假设矛盾。因此排成的最小数字中,不存在上述假设的关系。

(2)abxxxx,用ba代替ab可以得到baxxxx,这个数字是小于abxxxx,与假设矛盾。因此排成的最小数字中,不存在上述假设的关系。

(3)axxxxb,这一步证明麻烦了一点。可以将中间部分看成一个整体ayb,则有ay < ya,yb < by成立。将ay和by表示成10进制数字形式,则有下述关系式,这里a,y,b的位数分别为n,m,k。

关系1: ay < ya => a * 10^m + y < y * 10^n + a => a * 10^m - a < y * 10^n - y => a( 10^m - 1)/( 10^n - 1) < y

关系2: yb < by => y * 10^k + b < b * 10^m + y => y * 10^k - y < b * 10^m - b => y < b( 10^m -1)/( 10^k -1) 

关系3: a( 10^m - 1)/( 10^n - 1) < y < b( 10^m -1)/( 10^k -1)  => a/( 10^n - 1)< b/( 10^k -1) => a*10^k - a < b * 10^n - b =>a*10^k + b < b * 10^n + a => a < b

这与假设a > b矛盾。因此排成的最小数字中,不存在上述假设的关系。

综上所述,得出假设不成立,从而得出结论:对于排成的最小数字,不存在满足下述关系的一对字符串:a > b,但是在组成的数字a出现在b的前面。从而得出算法是正确的

此题求拼接起来的 “最小数字” ,本质上是一个排序问题。
排序判断规则: 设 numsnums 任意两数字的字符串格式 xx 和 yy ,则
若拼接字符串 x + y > y + x,x+y>y+x ,则 m > n,m>n ;
反之,若 x + y < y + x,x+y<y+x ,则 n < m,n<m ;
根据以上规则,套用任何排序方法对 numsnums 执行排序即可

def minNumber(self, nums: List[int]) -> str:
	def fast_sort(l , r):
    	if l >= r: 
    		return
        i, j = l, r
        while i < j:
        	while strs[j] + strs[l] >= strs[l] + strs[j] and i < j:
            	j -= 1
            while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: 
            	i += 1
            strs[i], strs[j] = strs[j], strs[i]
        	strs[i], strs[l] = strs[l], strs[i]
            fast_sort(l, i - 1)
            fast_sort(i + 1, r)
        
    strs = [str(num) for num in nums]
    fast_sort(0, len(strs) - 1)
    return ''.join(strs)

作者:jyd
链接:https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/solution/mian-shi-ti-45-ba-shu-zu-pai-cheng-zui-xiao-de-s-4/
来源:力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值