Golang sort -- sorting algorithm

Posted by aminnuto on Sun, 16 Jan 2022 20:48:44 +0100

1. Overview

Sort package implements four basic sorting algorithms: insert sort, merge sort, heap sort and quick sort. However, these four sorting methods are not public, and they are only used inside the sort package. Therefore, it is not necessary to consider which sort method should be selected when sorting data sets, as long as sort is implemented The interface defines three methods: Len() method to obtain the length of the data set, Less() method to compare the sizes of two elements, and Swap() method to exchange the positions of two elements, so that the data set can be sorted smoothly. The sort package will automatically select an efficient sorting algorithm according to the actual data. In addition, in order to facilitate the operation of common data types, the sort package provides complete support for [] int slice, [] float64 slice and [] string slice, mainly including:

  • Sorting support for basic data type slices
  • Basic data element lookup
  • Judge whether the basic data type slices have been sorted
  • Reverse the ordered data set

2. Data set sorting

As mentioned earlier, sorting data sets (including sets of user-defined data types) requires the implementation of sort For the three methods of the interface, let's look at the following definition of the interface:

type Interface interface {
		// Get the number of data collection elements
		Len() int
		// If the data of i index is less than the data of j index, it returns true and will not be called
		// The following Swap(), that is, the data is sorted in ascending order.
		Less(i, j int) bool
		// Swap the positions of the two elements of the i and j indexes
        Swap(i, j int)
}

After the data set implements these three methods, you can call the sort () method of the package to sort. The Sort() method is defined as follows:

  func Sort(data Interface)

The only parameter to the Sort() method is the data set to be sorted.

The package also provides a method to judge whether the data sets have been arranged in order. The internal implementation of this method depends on our own Len() and Less() methods:

func IsSorted(data Interface) bool {
    n := data.Len()
    for i := n - 1; i > 0; i-- {
        if data.Less(i, i-1) {
            return false
        }
    }
    return true
}

The following is an example of sorting students' grades using the sort package:

package main

import (
	"fmt"
	"sort"
)

//Student achievement structure
type StuScore struct {
     //full name
    name  string
    //achievement
    score int
}

type StuScores []StuScore

//Len()
func (s StuScores) Len() int {
	return len(s)
}

//Less(): grades will be sorted from low to high
func (s StuScores) Less(i, j int) bool {
	return s[i].score < s[j].score
}

//Swap()
func (s StuScores) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func main() {
    stus := StuScores{
                {"alan", 95},
                {"hikerell", 91},
                {"acmfly", 96},
                {"leao", 90}}

    fmt.Println("Default:")
    //Original order
    for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }
    fmt.Println()
    //StuScores has implemented sort Interface interface
    sort.Sort(stus)
    
    fmt.Println("Sorted:")
     //Ordered structure
    for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }

    //Judge whether the order has been arranged, and true will be printed
    fmt.Println("IS Sorted?", sort.IsSorted(stus))
}

The custom type StuScores of the sample program implements sort Interface interface, so its object can be used as sort Sort() and sort The parameter of issorted() was passed in. Operation results:

======Default======
alan : 95
hikerell : 91
acmfly : 96
leao : 90

======Sorted=======
leao : 90
hikerell : 91
alan : 95
acmfly : 96
IS Sorted? true

This example implements ascending sorting. If you want to get descending sorting results, you just need to modify the Less() function:

//Less(): the grades are sorted in descending order. Only the less than sign is modified to the greater than sign
func (s StuScores) Less(i, j int) bool {
	return s[i].score > s[j].score
}

In addition, the sort package provides the Reverse() method, which allows the data to be sorted in reverse order according to the sorting method defined by Less(), without modifying the Less() code. The method is defined as follows:

func Reverse(data Interface) Interface

We can see a sort returned by reverse () Interface interface type. The internal implementation of Reverse() is interesting:

   //Defines a reverse structure type, which is embedded in the Interface
    type reverse struct {
        Interface
    }

    //The Less() method of the reverse struct type has the opposite behavior of the embedded Less() method
    //The Len() and Swap() methods maintain the method behavior of the embedded type
    func (r reverse) Less(i, j int) bool {
        return r.Interface.Less(j, i)
    }

    //Returns the new data type that implements the Interface
    func Reverse(data Interface) Interface {
        return &reverse{data}
    }

After understanding the internal principle, you can use Reverse() in the student grade sorting example to realize the ascending sorting of grades:

 sort.Sort(sort.Reverse(stus))
     for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }

The last method: Search()

func Search(n int, f func(int) bool) int

The official document describes the method as follows:

The Search() method uses the binary search algorithm to search for a specified slice [0:n], and returns the minimum i (0 < = i < n) value that can make f(i)=true. It will assume that if f(i)=true, f(i+1)=true, that is, for slice [0:n],
The slice elements before i will make the f () function return false, and the elements after i and i will make the f () function return true. However, when f(i)=true i cannot be found in the slice (at this time, none of the slice elements can make the f() function return true), the Search() method will return n.

A common way to use the Search() function is to search whether the element x is in the slice s that has been arranged in ascending order:

  x := 11
    s := []int{3, 6, 8, 11, 45} //Note that it has been sorted in ascending order
    pos := sort.Search(len(s), func(i int) bool { return s[i] >= x })
    if pos < len(s) && s[pos] == x {
        fmt.Println(x, "stay s The location in is:", pos)
    } else {
        fmt.Println("s Contains no elements", x)
    }

The official document also provides a small program for guessing numbers:

func GuessingGame() {
	var s string
	fmt.Printf("Pick an integer from 0 to 100.\n")
	answer := sort.Search(100, func(i int) bool {
		fmt.Printf("Is your number <= %d? ", i)
		fmt.Scanf("%s", &s)
		return s != "" && s[0] == 'y'
	})
	fmt.Printf("Your number is %d.\n", answer)
}

3. sort package already supports internal data type sorting

As mentioned earlier, the sort package natively supports the sorting operations of three built-in data type slices of [] int, [] float64 and [] string, that is, we do not need to implement the relevant Len(), Less() and Swap () method.

1. IntSlice type and [] int sort

Since the internal implementation and usage of [] int slice sorting are similar to [] float64 and [] string, only this part is described in detail.

The sort package defines an IntSlice type and implements sort Interface:

    type IntSlice []int
    func (p IntSlice) Len() int           { return len(p) }
    func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
    func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    //The IntSlice type defines the sort () method and wraps the sort Sort() function
    func (p IntSlice) Sort() { Sort(p) }
    //The IntSlice type defines the SearchInts() method and wraps the SearchInts() function
    func (p IntSlice) Search(x int) int { return SearchInts(p, x) }

And provide sort The IntSlice type is used by the ints() method:

 func Ints(a []int) { Sort(IntSlice(a)) }

Therefore, sort is more often used to sort [] int slices Ints() instead of using IntSlice type directly:

    s := []int{5, 2, 6, 3, 1, 4} // Unordered slice data
    sort.Ints(s)
    fmt.Println(s) //[1 2 3 4 5 6] will be output

If you want to use descending sorting, you obviously need to use the Reverse() method mentioned earlier:

    s := []int{5, 2, 6, 3, 1, 4} // Unordered slice data
    sort.Sort(sort.Reverse(sort.IntSlice(s)))
    fmt.Println(s) //[6 5 4 3 2 1] will be output

If you want to find the position of the integer x in slice a, the sort package provides SearchInts() relative to the Search() method mentioned earlier:

 func SearchInts(a []int, x int) int

Note that the use condition of SearchInts() is that slice a has been sorted in ascending order

    s := []int{5, 2, 6, 3, 1, 4} // Unordered slice data
    sort.Ints(s) //The sorted s is [1 2 3 4 5 6]
    fmt.Println(sort.SearchInts(s, 3)) //2 will be output

2. Float64Slice type and []float64 sorting

The implementation is similar to Ints. Just look at its internal implementation:

    type Float64Slice []float64

    func (p Float64Slice) Len() int           { return len(p) }
    func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }
    func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    func (p Float64Slice) Sort() { Sort(p) }
    func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }

Three methods corresponding to Sort(), IsSorted(), and Search():

func Float64s(a []float64)  
func Float64sAreSorted(a []float64) bool
func SearchFloat64s(a []float64, x float64) int

To illustrate, in the Less method defined by the Float64Slice type above, there is an internal function isNaN(). The implementation of IsNaN () is exactly the same as that of IsNaN () in the math package. The reason why the sort package does not use math IsNaN () is completely based on package dependency. It should be noted that the implementation of sort package does not depend on any other package.

3. StringSlice type and [] string sorting

The size comparison between two string objects is based on "dictionary order".

The implementation is similar to Ints. Just look at its internal implementation:

    type StringSlice []string
    
    func (p StringSlice) Len() int           { return len(p) }
    func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
    func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    func (p StringSlice) Sort() { Sort(p) }
    func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }

Three methods corresponding to Sort(), IsSorted(), and Search():

func Strings(a []string)
func StringsAreSorted(a []string) bool
func SearchStrings(a []string, x string) int

Transferred from: https://blog.csdn.net/gongpulin/article/details/80843921

Topics: Go