1. 程式人生 > >堆排序及GOLANG程式碼實現

堆排序及GOLANG程式碼實現

堆排序及GOLANG程式碼實現

 

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/mofiu/article/details/83620743

一、什麼是堆排序

堆排序是利用堆這種資料結構而設計的一種排序演算法,堆排序是一種選擇排序,它的最壞,最好,平均時間複雜度均為O(nlogn),它也是不穩定排序。

排序的過程主要是由構建初始堆交換堆頂元素和末尾元素並重建堆兩部分組成。其中構建初始堆經推導複雜度為O(n),在交換並重建堆的過程中,需交換n-1次,而重建堆的過程中,根據完全二叉樹的性質,[log2(n-1),log2(n-2)…1]逐步遞減,近似為nlogn。所以堆排序時間複雜度一般認為就是O(nlogn)級。

升序使用大頂堆,每次將末尾值交換,沒構建一次大頂堆,整體陣列減1。這樣就完成了升序排序,降序排序的話使用小頂堆。

二、堆的特性

大頂堆:每個結點的值都大於或等於其左右孩子結點的值
小頂堆:每個結點的值都小於或等於其左右孩子結點的值

根據對的特性來形成公式就是,節點為i的話
大頂堆: arr[i]>=arr[2i+1] && arr[i]>=arr[2i+2]
小頂堆:arr[i]<=arr[2i+1] && arr[i]<=arr[2i+2]

構建堆都是從堆的最後一個root節點開始也就是(len(arr)-1)/2

三、例項

例如我們對陣列{2,28,6,15,26,22,9,10,18,233,8}進行降序排列
1、首先構建一個小頂堆,陣列預設的圖如下
圖1

第一個需要比較的root節點為(len(arr)-1)/2,也就是5節點,因為只有一個子節點,所以只需要比較(2*5+1)節點
在這裡插入圖片描述
然後比較(4,9,10)節點比較,先比較9和10節點,選擇較小的節點與4節點比較,結果如下圖
在這裡插入圖片描述
然後選擇3節點,(3,7,8)節點比較,先比較7和8節點,選擇較小的節點與3節點比較,結果如下圖
在這裡插入圖片描述
接下來比較root節點為2,child為5和6的幾個幾點。選擇5和6中較小的節點與5(root節點)比較,2和5進行交換,交換後不確定5為root(11為子節點)的子節點是否都大於5的值,所以也進行比較一次。
在這裡插入圖片描述


接下來對(1,3,4節點比較)較小的節點4與父節點1進行交換
在這裡插入圖片描述
1和4交換後後,4、9、10已經不符合小頂堆的特性了,所以需要重新比較,此時4節點就變成了9和10的root節點
在這裡插入圖片描述
小頂堆構建完成
在這裡插入圖片描述
2、排序過程
首先將堆頂的最小值與最後一個值交換(陣列中的0和11位置交換),交換後重新構建堆(11位置已經是最小的值,不參與堆的構建,所以重新構建堆的陣列要減去最後的值),如此往返,排序就完成了。
在這裡插入圖片描述

三、程式碼

func minHeap(root int, end int, c []int)  {
   for {
      var child = 2*root + 1
      //判斷是否存在child節點
      if child > end {
         break
      }
      //判斷右child是否存在,如果存在則和另外一個同級節點進行比較
      if child+1 <= end && c[child] > c[child+1] {
         child += 1
      }
      if c[root] > c[child] {
         c[root], c[child] = c[child], c[root]
         root = child
      } else {
         break
      }
   }
}
//降序排序
func HeapSort(c []int)  {
   var n = len(c)-1
   for root := n / 2; root >= 0; root-- {
      minHeap(root, n, c)
   }
   fmt.Println("堆構建完成")
   for end := n; end >=0; end-- {
      if c[0]<c[end]{
         c[0], c[end] = c[end], c[0]
         minHeap(0, end-1, c)
      }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

四、擴充套件問題

如果需要找出100w個數值中100個最大的數怎麼找出呢,?
思路1: 將所有值構建大頂堆,然後排序,排序只排出100個大的值即可
思路2: 因為找出100個最大的值,不需要排序,所以可以只建一個100的數的小頂堆,然後將陣列的後邊的數逐一和堆頂最小值比較,如果大於堆頂的值就進行交換並重新構建堆**,如果小於頂堆的話就進行下一次比較(不重建堆)**

//在c陣列中找出num個最大值
func HeapSort(c []int,num int) []int {
   m:=len(c)-1
   createHeap(c[:num],num-1)
   fmt.Println("堆構建完成")
   for i := num; i <=m; i++ {
      if c[0]<c[i]{
         c[0],c[i] = c[i],c[0]
         createHeap(c[:num],num-1)
      }
   }
   fmt.Println(c[:num])
   return c
}
func createHeap(arr []int,end int){
   for start := end / 2; start >= 0; start-- {
      minHeap(start, end, arr)
   }
}
//隨機陣列生成
func generateRandomNumber(start int, end int, count int) []int {
	//範圍檢查
	if end < start || (end-start) < count {
		return nil
	}
	//存放結果的slice
	nums := make([]int, 0)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for len(nums) < count {
		num := r.Intn((end - start))
		exist := false
		for _, v := range nums {
			if v == num {
				exist = true
				break
			}
		}
		if !exist {
			nums = append(nums, num)
		}
	}
	return nums
}