├── .gitignore ├── LICENSE ├── README.md ├── animation └── animation.go ├── gif.go ├── gifs ├── bubble.gif ├── heap.gif ├── insertion.gif ├── merge.gif ├── quick.gif ├── radix.gif ├── selection.gif └── shell.gif ├── main.go ├── palette └── palette.go └── sort ├── bubble.go ├── heap.go ├── insertion.go ├── merge.go ├── quick.go ├── radix.go ├── selection.go └── shell.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 invzhi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sorting-visualization 2 | 3 | [![GoDoc](https://godoc.org/github.com/invzhi/sorting-visualization?status.svg)](https://godoc.org/github.com/invzhi/sorting-visualization) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/invzhi/sorting-visualization)](https://goreportcard.com/report/github.com/invzhi/sorting-visualization) 5 | [![license](https://img.shields.io/github/license/invzhi/sorting-visualization.svg)](LICENSE) 6 | 7 | Inspired by [FishyMcFishFace](https://imgur.com/t/rainbow/RM3wl) on [Hacker News](https://news.ycombinator.com/item?id=15423202). 8 | 9 | More: 10 | 11 | - [Sorting Algorithms Visualized](https://imgur.com/a/voutF) 12 | - [Sorting Algorithms Revisualized](https://imgur.com/gallery/GD5gi) 13 | - [How I Visualized Sorting Algorithms and Brought Them to Life with Sound 14 | ](https://medium.freecodecamp.org/how-i-visualized-the-sorting-algorithms-and-brought-them-to-life-with-sound-ce7c5c6cb6ef) 15 | 16 | ## Usage 17 | ``` 18 | Usage of ./sorting-visualization: 19 | -delay int 20 | successive delay times, one per frame, in 100ths of a second (default 10) 21 | -filename string 22 | GIF's filename (default: sorting name) 23 | -height int 24 | GIF's height (default 256) 25 | -sorting string 26 | selection, insertion, shell, merge, quick, bubble, radix, all 27 | -weight int 28 | GIF's weight (default 256) 29 | ``` 30 | 31 | ## GIFs 32 | 33 | ### Selection Sort 34 | ![Selection Sort GIF](gifs/selection.gif) 35 | 36 | ### Bubble Sort 37 | ![Bubble Sort GIF](gifs/bubble.gif) 38 | 39 | ### Insertion Sort 40 | ![Insertion Sort GIF](gifs/insertion.gif) 41 | 42 | ### Shell Sort 43 | ![Shell Sort GIF](gifs/shell.gif) 44 | 45 | ### Merge Sort 46 | ![Merge Sort GIF](gifs/merge.gif) 47 | 48 | ### Quick Sort 49 | ![Quick Sort GIF](gifs/quick.gif) 50 | 51 | ### Heap Sort 52 | ![Heap Sort GIF](gifs/heap.gif) 53 | 54 | ### Radix Sort 55 | ![Radix Sort GIF](gifs/radix.gif) 56 | -------------------------------------------------------------------------------- /animation/animation.go: -------------------------------------------------------------------------------- 1 | package animation 2 | 3 | import ( 4 | "image" 5 | "image/gif" 6 | "log" 7 | "math/rand" 8 | "os" 9 | "sync" 10 | 11 | "github.com/invzhi/sorting-visualization/palette" 12 | ) 13 | 14 | // GIF allow only 256 colors 15 | const limit = 256 16 | 17 | var ( 18 | pal = palette.GetPalette(limit) 19 | m sync.Mutex 20 | ) 21 | 22 | func newFrame(g *gif.GIF) *image.Paletted { 23 | w, h := g.Config.Width, g.Config.Height 24 | r := image.Rect(0, 0, w, h) 25 | pix := make([]uint8, w*h) 26 | 27 | // make hue ordered finally 28 | x, a, b := 0, w/limit, w%limit 29 | for i := 0; i < limit; i++ { 30 | for j := 0; j < a; j++ { 31 | pix[x] = uint8(i) 32 | x++ 33 | } 34 | if i < b { 35 | pix[x] = uint8(i) 36 | x++ 37 | } 38 | } 39 | for i := 1; i < h; i++ { 40 | for j := 0; j < w; j++ { 41 | pix[i*w+j] = pix[j] 42 | } 43 | } 44 | 45 | img := &image.Paletted{ 46 | Pix: pix, 47 | Stride: 1 * w, 48 | Rect: r, 49 | Palette: pal, 50 | } 51 | g.Image = append(g.Image, img) 52 | return img 53 | } 54 | 55 | // NewRandGIF return a new GIF with one random frame and its color indexs. 56 | func NewRandGIF(w, h int) (*gif.GIF, [][]uint8) { 57 | g := &gif.GIF{ 58 | Image: make([]*image.Paletted, 0), 59 | Config: image.Config{ 60 | ColorModel: pal, 61 | Width: w, 62 | Height: h, 63 | }, 64 | } 65 | img := newFrame(g) 66 | cis := make([][]uint8, h) 67 | 68 | for y := 0; y < h; y++ { 69 | cis[y] = make([]uint8, w) 70 | // if ci > 255, color index will overflow :P 71 | for x, ci := range rand.Perm(w) { 72 | cis[y][x] = uint8(ci) 73 | img.Pix[y*w+x] = uint8(ci) 74 | } 75 | } 76 | return g, cis 77 | } 78 | 79 | // SetLine will set a line's color index to GIF on a specific frame. 80 | func SetLine(g *gif.GIF, y, frame int, line []uint8) { 81 | var img *image.Paletted 82 | 83 | m.Lock() 84 | if frame >= len(g.Image) { 85 | img = newFrame(g) 86 | } else { 87 | img = g.Image[frame] 88 | } 89 | m.Unlock() 90 | 91 | // left begin pixel 92 | l := img.PixOffset(0, y) 93 | for x, ci := range line { 94 | img.Pix[l+x] = ci 95 | } 96 | } 97 | 98 | func setDelay(g *gif.GIF, delay int) { 99 | g.Delay = make([]int, len(g.Image)) 100 | 101 | for i := range g.Image { 102 | g.Delay[i] = delay 103 | } 104 | } 105 | 106 | // EncodeGIF set delay time and encode to specific filename. 107 | func EncodeGIF(g *gif.GIF, fn string, delay int) { 108 | f, err := os.Create(fn) 109 | if err != nil { 110 | log.Fatal(err) 111 | } 112 | 113 | setDelay(g, delay) 114 | 115 | if err := gif.EncodeAll(f, g); err != nil { 116 | f.Close() 117 | log.Fatal(err) 118 | } 119 | 120 | if err := f.Close(); err != nil { 121 | log.Fatal(err) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gif.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | 7 | "github.com/invzhi/sorting-visualization/animation" 8 | ) 9 | 10 | func newGIF(sortf sorting, fn string, weight, height, delay int) { 11 | var wg sync.WaitGroup 12 | 13 | sem := make(chan struct{}, numCPU) 14 | 15 | g, cis := animation.NewRandGIF(weight, height) 16 | 17 | wg.Add(height) 18 | log.Println(fn, "start generate") 19 | 20 | for y := 0; y < height; y++ { 21 | sem <- struct{}{} 22 | go func(y int) { 23 | defer wg.Done() 24 | defer func() { <-sem }() 25 | sortf(cis[y], y, g) 26 | }(y) 27 | } 28 | 29 | wg.Wait() 30 | animation.EncodeGIF(g, fn, delay) 31 | log.Printf("%s generate %v frames\n", fn, len(g.Delay)) 32 | } 33 | -------------------------------------------------------------------------------- /gifs/bubble.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/bubble.gif -------------------------------------------------------------------------------- /gifs/heap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/heap.gif -------------------------------------------------------------------------------- /gifs/insertion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/insertion.gif -------------------------------------------------------------------------------- /gifs/merge.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/merge.gif -------------------------------------------------------------------------------- /gifs/quick.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/quick.gif -------------------------------------------------------------------------------- /gifs/radix.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/radix.gif -------------------------------------------------------------------------------- /gifs/selection.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/selection.gif -------------------------------------------------------------------------------- /gifs/shell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invzhi/sorting-visualization/848e4c541936ae81f7250e9e783a9c51563d3a2e/gifs/shell.gif -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "image/gif" 6 | "log" 7 | "runtime" 8 | "strings" 9 | 10 | "github.com/invzhi/sorting-visualization/sort" 11 | ) 12 | 13 | type sorting func([]uint8, int, *gif.GIF) 14 | 15 | const ( 16 | minWeight = 5 17 | ) 18 | 19 | var ( 20 | numCPU int 21 | 22 | sortName, filename string 23 | weight, height, delay int 24 | 25 | m = map[string]sorting{ 26 | "selection": sort.SelectionSort, 27 | "insertion": sort.InsertionSort, 28 | "shell": sort.ShellSort, 29 | "merge": sort.MergeSort, 30 | "quick": sort.QuickSort, 31 | "heap": sort.HeapSort, 32 | "bubble": sort.BubbleSort, 33 | "radix": sort.RadixSort, 34 | } 35 | ) 36 | 37 | func init() { 38 | numCPU = runtime.NumCPU() 39 | 40 | const ( 41 | defaultWeight = 256 42 | defaultHeight = 256 43 | defaultDelay = 10 44 | 45 | sortUsage = "selection, insertion, shell, merge, quick, bubble, radix, all" 46 | delayUsage = "successive delay times, one per frame, in 100ths of a second" 47 | ) 48 | flag.StringVar(&sortName, "sorting", "", sortUsage) 49 | flag.StringVar(&filename, "filename", "", "GIF's filename (default: sorting name)") 50 | 51 | flag.IntVar(&weight, "weight", defaultWeight, "GIF's weight") 52 | flag.IntVar(&height, "height", defaultHeight, "GIF's height") 53 | flag.IntVar(&delay, "delay", defaultDelay, delayUsage) 54 | } 55 | 56 | func main() { 57 | flag.Parse() 58 | 59 | sortf, isexist := m[sortName] 60 | if isexist == false && sortName != "all" { 61 | log.Fatalln("sorting is not existed:", sortName) 62 | } 63 | 64 | // filename 65 | if filename == "" { 66 | filename = sortName 67 | } 68 | if strings.HasSuffix(filename, ".gif") == false { 69 | filename += ".gif" 70 | } 71 | 72 | // weight, height 73 | if weight < minWeight { 74 | log.Fatalln("weight can not less than", minWeight) 75 | } 76 | if height <= 0 { 77 | log.Fatalln("height can not less than 1") 78 | } 79 | 80 | // delay 81 | if delay < 0 { 82 | log.Fatalln("delay can not less than 0") 83 | } 84 | 85 | // if sorting name is "all", generate all GIF 86 | if sortName != "all" { 87 | newGIF(sortf, filename, weight, height, delay) 88 | } else { 89 | // no matter about filename 90 | for name, sortf := range m { 91 | newGIF(sortf, name+".gif", weight, height, delay) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /palette/palette.go: -------------------------------------------------------------------------------- 1 | package palette 2 | 3 | import "image/color" 4 | 5 | const round = 360 6 | 7 | var pal = make(color.Palette, round) 8 | 9 | func init() { 10 | for hue := 0; hue < round; hue++ { 11 | pal[hue] = hueToRGBA(hue) 12 | } 13 | } 14 | 15 | // GetPalette return palette, hue is between 0 to n-1. 16 | func GetPalette(n int) color.Palette { 17 | var colors color.Palette 18 | if n > round { 19 | colors = pal[:] 20 | } else { 21 | colors = pal[:n] 22 | } 23 | return colors 24 | } 25 | 26 | func hueToRGBA(h int) color.RGBA { 27 | var c color.RGBA 28 | 29 | hi := h / 60 30 | f := float64(h)/60 - float64(hi) 31 | q := 1 - f 32 | 33 | switch hi { 34 | case 0: 35 | c.R = 0xFF 36 | c.G = uint8(f * 0xFF) 37 | c.B = 0x00 38 | case 1: 39 | c.R = uint8(q * 0xFF) 40 | c.G = 0xFF 41 | c.B = 0x00 42 | case 2: 43 | c.R = 0x00 44 | c.G = 0xFF 45 | c.B = uint8(f * 0xFF) 46 | case 3: 47 | c.R = 0x00 48 | c.G = uint8(q * 0xFF) 49 | c.B = 0xFF 50 | case 4: 51 | c.R = uint8(f * 0xFF) 52 | c.G = 0x00 53 | c.B = 0xFF 54 | case 5: 55 | c.R = 0xFF 56 | c.G = 0x00 57 | c.B = uint8(q * 0xFF) 58 | } 59 | c.A = 0xFF 60 | return c 61 | } 62 | -------------------------------------------------------------------------------- /sort/bubble.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | // BubbleSort will record a frame for every bubble operation. 10 | func BubbleSort(a []uint8, y int, g *gif.GIF) { 11 | frame := 1 12 | l := len(a) 13 | 14 | for i := 0; i < l-1; i++ { 15 | for j := l - 1; j > i; j-- { 16 | if a[j] < a[j-1] { 17 | a[j], a[j-1] = a[j-1], a[j] 18 | } 19 | } 20 | animation.SetLine(g, y, frame, a) 21 | frame++ 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sort/heap.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | func sink(a []uint8, f, b int) { 10 | for 2*f+1 < b { 11 | c := 2*f + 1 12 | if c+1 < b && a[c] < a[c+1] { 13 | c++ 14 | } 15 | if a[f] >= a[c] { 16 | break 17 | } 18 | a[f], a[c] = a[c], a[f] 19 | f = c 20 | } 21 | } 22 | 23 | // HeapSort will record a frame for every sink operation. 24 | func HeapSort(a []uint8, y int, g *gif.GIF) { 25 | frame := 1 26 | l := len(a) 27 | 28 | for i := l/2 - 1; i >= 0; i-- { 29 | sink(a, i, l) 30 | 31 | animation.SetLine(g, y, frame, a) 32 | frame++ 33 | } 34 | for i := l - 1; i > 0; i-- { 35 | a[0], a[i] = a[i], a[0] 36 | sink(a, 0, i) 37 | 38 | animation.SetLine(g, y, frame, a) 39 | frame++ 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sort/insertion.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | // InsertionSort will record a frame every insertion. 10 | func InsertionSort(a []uint8, y int, g *gif.GIF) { 11 | frame := 1 12 | l := len(a) 13 | 14 | for i := 1; i < l; i++ { 15 | t := a[i] 16 | j := i 17 | for ; j > 0 && a[j-1] > t; j-- { 18 | a[j] = a[j-1] 19 | } 20 | a[j] = t 21 | 22 | animation.SetLine(g, y, frame, a) 23 | frame++ 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sort/merge.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | func quickMerge(a, t []uint8, mid int) { 10 | l := len(a) 11 | 12 | for i := 0; i <= mid; i++ { 13 | t[i] = a[i] 14 | } 15 | for i := l - 1; i > mid; i-- { 16 | t[i] = a[mid+l-i] // mid+l-i = mid+1 + l-1-i 17 | } 18 | 19 | i, j := 0, l-1 20 | for k := 0; k < l; k++ { 21 | if t[i] < t[j] { 22 | a[k] = t[i] 23 | i++ 24 | } else { 25 | a[k] = t[j] 26 | j-- 27 | } 28 | } 29 | } 30 | 31 | // MergeSort will record a frame for every merge operation. 32 | func MergeSort(a []uint8, y int, g *gif.GIF) { 33 | frame := 1 34 | l := len(a) 35 | 36 | t := make([]uint8, l) 37 | // bottom up 38 | for blk := 1; blk < l; blk += blk { 39 | for lo := 0; lo < l-blk; lo += blk + blk { 40 | mid := lo + blk - 1 41 | hi := mid + blk 42 | if l-1 < hi { 43 | hi = l - 1 44 | } 45 | if a[mid] > a[mid+1] { 46 | quickMerge(a[lo:hi+1], t[lo:hi+1], mid-lo) 47 | } 48 | } 49 | 50 | animation.SetLine(g, y, frame, a) 51 | frame++ 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sort/quick.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | func partition(a []uint8) int { 10 | l := len(a) 11 | pivot := a[0] 12 | i, j := 0, l 13 | 14 | for { 15 | for a[i+1] < pivot { 16 | i++ 17 | if i+1 == l { 18 | break 19 | } 20 | } 21 | for ; a[j-1] > pivot; j-- { 22 | } 23 | i++ 24 | j-- 25 | if i >= j { 26 | break 27 | } 28 | a[i], a[j] = a[j], a[i] 29 | } 30 | a[0], a[j] = a[j], a[0] 31 | return j 32 | } 33 | 34 | func quickSort(a, data []uint8, y int, frame *int, g *gif.GIF) { 35 | if len(a) <= 1 { 36 | return 37 | } 38 | p := partition(a) 39 | 40 | animation.SetLine(g, y, *frame, data) 41 | *frame = *frame + 1 42 | 43 | quickSort(a[:p], data, y, frame, g) 44 | quickSort(a[p+1:], data, y, frame, g) 45 | } 46 | 47 | // QuickSort will record a frame for every partition. 48 | func QuickSort(a []uint8, y int, g *gif.GIF) { 49 | data := a 50 | frame := 1 51 | quickSort(a, data, y, &frame, g) 52 | } 53 | -------------------------------------------------------------------------------- /sort/radix.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | // RadixSort will record a frame for every radix sort. 10 | func RadixSort(a []uint8, y int, g *gif.GIF) { 11 | frame := 1 12 | l := len(a) 13 | 14 | const BASE = 10 15 | b := make([]uint8, l) 16 | max := int(a[0]) 17 | exp := 1 18 | 19 | for i := 1; i < l; i++ { 20 | if int(a[i]) > max { 21 | max = int(a[i]) 22 | } 23 | } 24 | 25 | for max/exp > 0 { 26 | bucket := make([]int, BASE) 27 | for i := 0; i < l; i++ { 28 | radix := int(a[i]) / exp % BASE 29 | bucket[radix]++ 30 | } 31 | for i := 1; i < BASE; i++ { 32 | bucket[i] += bucket[i-1] 33 | } 34 | for i := l - 1; i >= 0; i-- { 35 | radix := int(a[i]) / exp % BASE 36 | bucket[radix]-- 37 | b[bucket[radix]] = a[i] 38 | } 39 | for i := 0; i < l; i++ { 40 | a[i] = b[i] 41 | } 42 | exp *= BASE 43 | 44 | animation.SetLine(g, y, frame, a) 45 | frame++ 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sort/selection.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | // SelectionSort will record a frame for every selection. 10 | func SelectionSort(a []uint8, y int, g *gif.GIF) { 11 | frame := 1 12 | l := len(a) 13 | 14 | for i := 0; i < l-1; i++ { 15 | minIndex := i 16 | for j := i + 1; j < l; j++ { 17 | if a[j] < a[minIndex] { 18 | minIndex = j 19 | } 20 | } 21 | a[minIndex], a[i] = a[i], a[minIndex] 22 | 23 | animation.SetLine(g, y, frame, a) 24 | frame++ 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sort/shell.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import ( 4 | "image/gif" 5 | 6 | "github.com/invzhi/sorting-visualization/animation" 7 | ) 8 | 9 | // ShellSort will record a frame for every h-insertion sort. 10 | func ShellSort(a []uint8, y int, g *gif.GIF) { 11 | frame := 1 12 | l := len(a) 13 | 14 | h := 1 15 | for h < l/3 { 16 | h = 3*h + 1 17 | } 18 | for h >= 1 { 19 | for i := h; i < l; i++ { 20 | t := a[i] 21 | j := i 22 | for ; j >= h && a[j-h] > t; j -= h { 23 | a[j] = a[j-h] 24 | } 25 | a[j] = t 26 | } 27 | h /= 3 28 | 29 | animation.SetLine(g, y, frame, a) 30 | frame++ 31 | } 32 | } 33 | --------------------------------------------------------------------------------