排序算法-quicksort

59 阅读1分钟

intro

Quicksort is popular because it is not difficult to implement, works well for a variety of different kinds of input data, and is substantially faster than any other sorting method in typical applications.

advantages:

  • it is in-place (uses only a small auxiliary stack)
  • time proportional to N log N on the average to sort an array of length N
  • quicksort has a shorter inner loop than most other sorting algorithms

drawback:

  • must avoid worst case
func shuffle[T comparable](sli []T) []T {
   rand.Seed(time.Now().UnixNano())
   len := len(sli)
   if len <= 1 {
      return sli
   }

   for i := len - 1; i > 0; i-- {
      randNum := rand.Intn(i)
      sli[i], sli[randNum] = sli[randNum], sli[i]
   }
   return sli
}
func partition(nums *[]int, lo int, hi int) int {
   i := lo + 1
   j := hi
   v := (*nums)[lo]
   for true {
      // 如果用 (*nums)[i] < v, 当数组里面有其他项也等于v的时候,会出现无限循环
      // i如果=hi, 就会出现越界
      for ; i < hi && (*nums)[i] <= v; i++ {
      }
      for ; j > lo && (*nums)[j] >= v; j-- {
      }
      if i >= j {
         break
      }
      swap(nums, i, j)
   }
   swap(nums, lo, j)
   return j
}
func QuickSort(nums []int) {
   shuffle(nums)
   quickSort(nums, 0, len(nums)-1)
}

func quickSort(nums []int, lo int, hi int) {
   if hi <= lo {
      return
   }
   j := partition(&nums, lo, hi)

   quickSort(nums, lo, j-1)
   quickSort(nums, j+1, hi)
}

Algorithmic improvements

Cutoff to insertion sort

The optimum value of the cutoff length M is system-dependent, but any value between 5 and 15 is likely to work well in most situations

Median-of-three partitioning(取随机3位数的中位数作为partitioning item)

use the median of a small sample of items taken from the subarray as the partitioning item. Doing so will give a slightly better partition, but at the cost of computing the median.

It turns out that most of the available improvement comes from choosing a sample of size 3 and then partitioning on the middle item.

3-way partitioning

improve performance for arrays with large numbers of duplicate keys

For example, a subarray that consists solely of items that are equal (just one key value) does not need to be processed further, but our implementation keeps partitioning down to small subarrays.

One straightforward idea is to partition the array into three parts, one each for items with keys smaller than, equal to, and larger than the partitioning item’s key.

image.png

func quickSort(nums []int, lo int, hi int) {
   if hi <= lo {
      return
   }
   lt := lo
   i := lo + 1
   gt := hi
   v := nums[lo]

   for i <= gt {
      if nums[i] < v {
         swap(nums, lt, i)
         lt++
         i++
      } else if nums[i] > v {
         swap(nums, i, gt)
         gt--
      } else {
         i++
      }
   }

   quickSort(nums, lo, lt-1)
   quickSort(nums, gt+1, hi)
}