堆排序(python实现)
堆排序是利用最大最或最小堆,废话不多说:
先给出几个概念:
二叉树:二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”
完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。
满二叉树: 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。
堆:堆是一种数据结构,类似树根结构,如图,但是不一定是二叉树。
二叉堆:二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树),包括最大堆和最小堆。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值,即根节点为最大数字
最小堆:父结点的键值总是大于或等于任何一个子节点的键值,即根节点为最小数字
堆排序步骤:
1.将数据构建成堆,这里的堆指完全二叉树(不一定是满二叉树)
2.将堆调整为最小堆或最大堆
3.此时堆顶已经为最大数或最小数,可以对下面的分支堆再进行调堆即可,此处采用的是将堆顶数取出,再调堆
本人愚钝,网上代码不慎明了,根据自己的思路写了一下,不足之处,请多多指教
1.首先实现将数组按照堆打印
1 def PrintArrayTree(arr): 2 frontRowSum=1 #Number of digits in front of n-1 rows 3 row=1 #row n(start from 1) 4 for i in range(0,len(arr)): 5 if i==frontRowSum: 6 frontRowSum=frontRowSum+2**row #Number of digits in front of n rows 7 print("\n")#the next row 8 row=row+1 9 print (arr[i],end=" ") #print digits 10 print("Over") 11 12 arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] 13 PrintArrayTree(arr)
运行结果如下:
10
9 8
7 6 5 4
3 2 1 234 562 452 23623 565
5 26
print Over
2.构建完了堆,我想实现在堆内任意查找,找到他的子节点和父节点,代码如下:
1 def FindNode(arr,row,cloumn): 2 3 if row<1 or cloumn<1: 4 print("the number of row and column must be greater than 1") 5 return 6 if cloumn>2**(row-1): 7 print("this row just ",2**(row-1),"numbers") 8 return 9 10 frontRowSum=0 11 CurrentRowSum=0 12 for index in range(0,row-1): 13 CurrentRowSum=2**index #the number of digits in current row 14 frontRowSum=frontRowSum+CurrentRowSum #the number of digits of all rows 15 NodeIndex=frontRowSum+cloumn-1 #find the location of the node in the array by row and cloumn 16 17 if NodeIndex>len(arr)-1: 18 print("out of this array") 19 return 20 21 currentNode=arr[NodeIndex] 22 23 childIndex=NodeIndex*2+1 24 25 print("Current Node:",currentNode) 26 27 if row==1: #row 1 have no parent node 28 print("no parent node!") 29 else: #the parent node ofcurrent node 30 parentIndex=int((NodeIndex-1)/2) 31 parentNode=arr[parentIndex] 32 print("Parent Node:",parentNode) 33 34 if childIndex+1>len(arr): #print leftChild node 35 print("no left child node!") 36 else: 37 leftChild=arr[childIndex] 38 print("Left Child Node:",leftChild) 39 40 41 if childIndex+1+1>len(arr): #print rightChild node 42 print("no left right node!") 43 else: 44 rightChild=arr[childIndex+1] 45 print("Right Child Node:",rightChild) 46 47 print("\n") 48 49 arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] 50 FindNode(arr,1,1) 51 FindNode(arr,2,2) 52 FindNode(arr,4,1)
代码运行结果如下:
Current Node: 10
no parent node!
Left Child Node: 9
Right Child Node: 8
Current Node: 8
Parent Node: 10
Left Child Node: 5
Right Child Node: 4
Current Node: 3
Parent Node: 7
Left Child Node: 5
Right Child Node: 26
此代码在堆排序中没有直接用到,但是提供了一些思路
3.按照堆排序步骤,建堆之后需要进行堆调整,接下来进行堆调整,先实现单个叉(某个节点及其子孩子)的进行排序,直接借鉴FindNode里面的代码,将当前节点分别与其左右孩子比较就行了,本文意在实现最小堆,即将小的节点作为父节点
def MinSort(arr,row,cloumn): if row<1 or cloumn<1: print("the number of row and column must be greater than 1") return if cloumn>2**(row-1): print("this row just ",2**(row-1),"numbers") return frontRowSum=0 CurrentRowSum=0 for index in range(0,row-1): CurrentRowSum=2**index #the number of digits in current row frontRowSum=frontRowSum+CurrentRowSum #the number of digits of all rows NodeIndex=frontRowSum+cloumn-1 #find the location of the node in the array by row and cloumn if NodeIndex>len(arr)-1: print("out of this array") return currentNode=arr[NodeIndex] childIndex=NodeIndex*2+1 print("Current Node:",currentNode) if row==1: print("no parent node!") else: parentIndex=int((NodeIndex-1)/2) parentNode=arr[parentIndex] print("Parent Node:",parentNode) if childIndex+1>len(arr): print("no left child node!") else: leftChild=arr[childIndex] print("Left Child Node:",leftChild) if currentNode>leftChild: print("swap currentNode and leftChild") temp=currentNode currentNode=leftChild leftChild=temp arr[childIndex]=leftChild if childIndex+1>=len(arr): print("no right child node!") else: rightChild=arr[childIndex+1] print("Right Chile Node:",rightChild) if currentNode>rightChild: print("swap rightCild and leftChild") temp=rightChild rightChild=currentNode currentNode=temp arr[childIndex+1]=rightChild arr[NodeIndex]=currentNode arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] print("initial array:",arr) MinSort(arr,1,1) print("result array:",arr)
运行结果如下,可以看出对于第一个节点,其自孩子为9,8,已经实现将节点与最小的自孩子进行交换,保证父节点小于任何一个子孩子
initial array: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 234, 562, 452, 23623, 565, 5, 26]
Current Node: 10
no parent node!
Left Child Node: 9
swap currentNode and leftChild
Right Chile Node: 8
swap rightCild and leftChild
result array: [8, 10, 9, 7, 6, 5, 4, 3, 2, 1, 234, 562, 452, 23623, 565, 5, 26]
4.已经实现对单个节点和子孩子进行比较,保证父节点小于孩子,将堆内所有拥有孩子的节点进行排序,即可调整为最小堆,代码如下:
def MinHeap(arr): frontRowSum=1 row=1 for i in range(0,len(arr)): if i==frontRowSum: frontRowSum=frontRowSum+2**row #the number of digits of all rows print("\n") # next row row=row+1 print (arr[i],end=" ") print("row",row) rowIndex=row-1 #the last row have no child node print("rowIndex",rowIndex) column=2**(rowIndex-1) #the number of digits of current row print("column",column) number=len(arr) while rowIndex>0: #sort the nodes that have child nodes from the last number to the first number if number<=2**(rowIndex-1): rowIndex=rowIndex-1 column=2**(rowIndex-1) print("sort",rowIndex,column) MinSort(arr,rowIndex,column) number=number-1 column=column-1 arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] print("initial array:",arr)
PrintArrayTree(arr) MinHeap(arr) print("result array:",arr)
PrintArrayTree(arr)
运行结果如下,可以看到最小数字已经位于顶端,实现最小堆
initial array: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 234, 562, 452, 23623, 565, 5, 26]
10
9 8
7 6 5 4
3 2 1 234 562 452 23623 565
5 26
print Over
.......
result array: [1, 10, 4, 9, 2, 8, 5, 7, 3, 6, 234, 562, 452, 23623, 565, 5, 26]
1
10 4
9 2 8 5
7 3 6 234 562 452 23623 565
5 26
print Over
4.最小值已经到顶端,将最小值依次取出,然后再调整堆,再取出,就完成堆排序。代码如下:
1 def HeapSort(arr): 2 arr2=[] 3 for i in range(0,len(arr)): 4 MinHeap(arr) 5 arr2.append(arr[0]) 6 del arr[0] 7 return arr2 8 9 arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] 10 print("initial array:",arr) 11 PrintArrayTree(arr) 12 resultArr=HeapSort(arr) 13 print("result array:",resultArr) 14 PrintArrayTree(resultArr)
运行结果如下:
initial array: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 234, 562, 452, 23623, 565, 5, 26]
10
9 8
7 6 5 4
3 2 1 234 562 452 23623 565
5 26
print Over
10
9 8
7 6 5 4
3 2 1 234 562 452 23623 565
5 26
.........
result array: [1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 26, 234, 452, 562, 565, 23623]
1
2 3
4 5 5 6
7 8 9 10 26 234 452 562
565 23623
print Over
5.后续工作:
1)代码需要优化
2)感觉堆排序有点类似冒泡排序
3)需要检查代码的健壮性
4)后续需要计算分析代码的复杂度
1)优化之后的程序如下:写法还能再优化,但继续优化会影响可读性
1 def MinSort(arr,start,end): 2 import math 3 arrHeight=0 4 for index in range(0,end-start): 5 if index==2**(arrHeight+1)-1: 6 arrHeight=arrHeight+1 7 8 for NodeIndex in range(2**(arrHeight)-2,-1,-1): 9 currentNode=arr[NodeIndex+start] 10 childIndex=NodeIndex*2+1+start 11 12 if childIndex+1>len(arr): 13 continue 14 else: 15 leftChild=arr[childIndex] 16 17 if currentNode>leftChild: 18 temp=currentNode 19 currentNode=leftChild 20 leftChild=temp 21 arr[childIndex]=leftChild 22 arr[NodeIndex+start]=currentNode 23 24 if childIndex+1>=len(arr): 25 continue 26 else: 27 rightChild=arr[childIndex+1] 28 if currentNode>rightChild: 29 30 temp=rightChild 31 rightChild=currentNode 32 currentNode=temp 33 arr[childIndex+1]=rightChild 34 arr[NodeIndex+start]=currentNode 35 36 37 def HeapSort(arr): 38 for i in range(0,len(arr)-1): 39 MinSort(arr,i,len(arr)) 40 41 42 arr=[10,9,8,7,6,5,4,3,2,1,234,562,452,23623,565,5,26] 43 44 print("Initial array:\n",arr) 45 HeapSort(arr) 46 print("Result array:\n",arr)
运行结果:
Initial array:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 234, 562, 452, 23623, 565, 5, 26]
Result array:
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 26, 234, 452, 562, 565, 23623]