一、heapinsert
1.问题描述
数组本质就是完全二叉树,因此完全二叉树只是一种逻辑结构,实际结构其实是数组。那么给定一个数组,如何将其变成大根堆?这就是一个heapinsert过程。
方法:
父结点----(i-1)/2
左孩子-----2i+1
右孩子------2i+2
每从数组里拿出一个元素,就找该元素下标对应的父结点。比较大小,如果大于父结点,则和父结点互换,否则不动。
2.python实现
def turnBigHeap(arr):
'''
问题一:将一个完全二叉树(实际结构是数组)转换成大根堆
'''
for i in range(0,len(arr)):
arr = heapInsert(arr, i)
return arr
def heapInsert(arr, index):
while arr[index] > arr[int((index-1)/2)]:
arr[index], arr[int ((index-1)/2)] = arr[int((index-1)/2)], arr[index]
index = int((index-1)/2)
return arr
arr = [1,21515,3,1255]
fianlArr = turBigHeap(arr)
print(fianlArr)
二、heapipy
1.问题描述
一个之前是大根堆的完全二叉树,因为某种变化导致第一个结点的变小了,那么要对这样一个完全二叉树重新变成大根堆,这个过程我们称之为heapipy。
2.python实现
def heapipy(arr, index, heapsize):
'''
heapipy过程:将一个原本是大根堆的完全二叉树重新变成大根堆的过程。
方法:index和它的左右孩子比较,如果小于它的左右孩子那么就往下沉;否则不动
'''
left = 2 * index + 1
right = 2 * index + 2 #其实可以用left + 1表示右孩子,但是这里为了方便以后复习就引入一个右孩子变量
while left <= heapsize: #这里用heapsize是因为增加程序的鲁棒性;heapsize小于等于数组的大小(也就是表示0~heapsize区间是个大根堆);
large = right if (arr[right] > arr[left] and right < heapsize) else left
large = large if (arr[large] > arr[index]) else index #python的三目运算符,说白了就是从三者中取一个最大的,然后返回对应的索引
if large == index:
break
arr[large], arr[index] = arr[index], arr[large]
index = large
left = 2 * index + 1
return arr
arr = [2,6,3,2,1]
fianlArr = heapipy(arr,0,4)
print(fianlArr)
三、堆排序
1.问题描述
利用堆排序将一个数组按照从小到大的顺序排列。
2.思想
- 先将数组变成大根堆 (heapinsert)
- 大根堆堆顶(全局最大值)与最后一个数交换,然后heapsize-1(0到heapsize范围表示待排序的堆结构,heapsize以外的表示排好的),再对新的0~heapsize作heapipy过程,再次得到新的大根堆
- 以此反复,最后得到从小到大的数组
3.python实现
def heapSort(arr, R):
'''
问题三:堆排序
step1:先将数组变成大根堆
step2:堆顶的数与最后一个数交换;然后heapsize减1(也就是把最大的放在最后位置不再动)
step3:对新的堆调用heapipy 将其再变成一个大根堆
step4:以此反复,最后得到一个从小到大的数组
'''
for i in range(0,len(arr)):
arr = heapInsert(arr, i)
heapsize = R
while heapsize > 0:
arr[heapsize], arr[0] = arr[0], arr[heapsize]
heapsize -= 1
heapipy(arr, 0, heapsize)
return arr
def heapInsert(arr, index):
'''
step1 函数
'''
while arr[index] > arr[int((index-1)/2)]:
arr[index], arr[int ((index-1)/2)] = arr[int((index-1)/2)], arr[index]
index = int((index-1)/2)
return arr
def heapipy(arr, index, heapsize):
'''
step3 函数
将原先是大根堆的再次变成大根堆(改变的只有堆的第一个结点)
'''
left = 2 * index + 1
right = 2 * index + 2 #其实可以用left + 1表示右孩子,但是这里为了方便以后复习就引入一个右孩子变量
while left <= heapsize: #这里用heapsize是因为增加程序的鲁棒性;heapsize小于等于数组的大小(也就是表示0~heapsize区间是个大根堆);
large = right if (arr[right] > arr[left] and right < heapsize) else left
large = large if (arr[large] > arr[index]) else index #python的三目运算符,说白了就是从三者中取一个最大的,然后返回对应的索引
if large == index:
break
arr[large], arr[index] = arr[index], arr[large]
index = large
left = 2 * index + 1
return arr
arr = [1000,2,6,3,2,1,888,4]
fianlArr = heapSort(arr,len(arr) - 1)
print(fianlArr)
四、比较器
比较器的实现部分在博客的上一篇文章详细的解释了实现过程,这里不再赘述。
五、求相邻两数的最大差值
1.问题描述
给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序。
2.思想
题目要求时间复杂度O(N),我们借用桶的思想,但不用桶排序。
- 遍历数组,数组的最小值放在第0号桶,数组的最大值放在第N号桶
- 将N个数按照一定规则等分在N+1个桶中
- 相邻两数的最大差值一定出现在两个相邻的非空桶之间
3.python实现
import sys
def MaxGap(arr):
'''
问题五:求相邻两数的最大差值(要求时间复杂度O(N))
给定一个数组,求如果排序之后,相邻两数的最大差值,要求时 间复杂度O(N),且要求不能用非基于比较的排序。
借用桶的思想,但是没有用桶排序
'''
length = len(arr)
minNum = sys.maxsize #因为要取最小值,故这里赋值要赋值为系统最大值,这样才不会因为赋值不好导致取不出来数组最小值
maxNum = -sys.maxsize - 1 #因为要取最大值,故这里赋值要赋值为系统最小值,这样才不会因为赋值不好导致取不出来数组最大值
for i in range(0, len(arr)):
minNum = min(minNum, arr[i])
maxNum = max(maxNum, arr[i]) #17行-21行是遍历列表,取出全局最小值和最大值分别放到0号桶和N+1号桶
if minNum == maxNum:
return 0
boolList = [0 for i in range(length+1)]
maxList = [-sys.maxsize - 1 for i in range(length+1)]
minList = [sys.maxsize for i in range(length+1)]
for i in range(0, len(arr)):
bit = bucket(arr[i], length, minNum, maxNum)
boolList[bit] = 1
maxList[bit] = max(maxList[bit], arr[i])
minList[bit] = min(minList[bit], arr[i]) #上下两种写法都对
# maxList[bit] = max(maxList[bit], arr[i]) if (boolList[bit]) else arr[i]
# minList[bit] = min(minList[bit], arr[i]) if (boolList[bit]) else arr[i]
# boolList[bit] = 1
print('maxList', maxList, 'minList', minList, 'boolList',boolList)
res = maxList[1] - minList[0]
# for i in range(1, len(maxList)):
# if boolList[i] and not boolList[i-1]: #这种错误写法在于:并不是说一定只有一个空桶,因为可能会有很多重复的,那么就会有很多空桶;如果这么去找最近的非空桶,则仅对 只有一个空桶的情况有效
# res = max(res, maxList[i] - minList[i-2])
# else:
# res = max(res, maxList[i] - minList[i-1])
res = 0
lastMax = maxList[0]
for i in range(1, length + 1):
if boolList[i]:
res = max(res, minList[i] - lastMax)
lastMax = maxList[i] #lastMax代表着上一次(最近一次)的最大值,时刻更新着,作为一个临时变量存在
return res
def bucket(num, length, minNum, maxNum):
'''
分桶函数:
功能是把所有的数按照一定数理逻辑分到不同范围的桶内
'''
bid = ((num - minNum)*length)//(maxNum - minNum) #函数目的是把当前数组中的第i个数放在对应的范围桶中
print ('bucket', bid)
return bid
array = [1,3,6,3,7,9,15,1,5]
print (MaxGap(array))
注意
- 导入sys库,为了获取系统最大值和系统最小值;
- 要注意如何实现第i个桶可以找到最近的第i-1个非空的桶,方法就是定义一个临时变量,随时随地被赋值即可。