Go简单学习笔记

楔子

go语言学习笔记。

Go语言圣经

https://docs.hacknode.org/gopl-zh/ch1/ch1-01.html
PDF蓝奏云下载https://www.lanzous.com/i8n12od

其他书籍

Go语言高级编程
《Effective Go》中英双语版

Go语言数据类型

Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。基础类型:数字、字符串、和布尔型。复合数据类型:数组、结构体(通过组合简单类型,来表达更加复发的数据结构)。引用类型包括:指针、切片、字典、函数、通道。

序列化与反序列化

package main

import (
	"encoding/json"
	"fmt"
)

//结构体和json
func main() {

	p1 := person{
		Name: "go学习",
		Age:  1,
	}
	fmt.Printf("%#v\n", p1)
	//1 把go语言中结构体-->json| 序列化
	//2 反序列化  |json-->go结构体
	marshal, err := json.Marshal(p1)
	if err != nil {
		fmt.Println("marsh1 failed")
		return
	}
	fmt.Printf("%#v\n", string(marshal))
	// 反序列化
	var p2 = new(person)
	json.Unmarshal([]byte(string(marshal)), p2) //传入指针,以便修改结构体的值
	fmt.Println(*p2)
}

type person struct {
	Name string `json:"name""`
	Age  int    `json:"age"`
}

控制台|小例子

map增删查改学生管理系统

package main

import (
	"fmt"
	"os"
)

// 学生管理系统
type student struct {
	id   int64
	name string
}

func NewStudent(id int64, name string) {

}

type stuMgr struct {
	//保存所有 学生
	allStudent map[int64]student
}

var (
	allStudent map[int64]student
)

func (s stuMgr) addStu() {
	var (
		id        int64
		nameinput string
	)
	fmt.Println("输入学生ID")
	fmt.Scanln(&id)
	fmt.Println("输入学生姓名")
	fmt.Scanln(&nameinput)
	p := student{id: id, name: nameinput}
	s.allStudent[id] = p

}

//查看学生
func (s stuMgr) showAllStudent() {
	for _, v := range s.allStudent {
		fmt.Println("|学号", v.id, " |姓名", v.name)
	}
}
func (s stuMgr) delStudent() {
	fmt.Println("输入要删除的ID")
	var delId int64
	fmt.Scanln(&delId)
	delete(s.allStudent, delId)
	fmt.Println("-----删除后的结果----")
	s.showAllStudent()

}
func (s stuMgr) modify() {
	var (
		id        int64
		nameinput string
	)
	fmt.Println("输入要修改学生ID")
	fmt.Scanln(&id)
	fmt.Println("输入修改后的学生姓名")
	fmt.Scanln(&nameinput)
	p := student{id: id, name: nameinput}
	s.allStudent[id] = p
}

// 管理者
var smr stuMgr

//查看学生
// 删除 学生
// 新增
func main() {
	smr = stuMgr{allStudent: make(map[int64]student, 100)}
	for {
		//菜单
		showMenu()
		//等待输入
		fmt.Print("输入选项:")
		var choice int
		fmt.Scanln(&choice)
		fmt.Println("输入的是", choice)
		switch choice {
		case 5:
			os.Exit(1)
		case 1:
			smr.showAllStudent()
		case 2:
			smr.addStu()
		case 3:
			smr.modify()
		case 4:
			smr.delStudent()
		default:
			fmt.Println("输入有误")
		}
	}
}
func showMenu() {
	fmt.Println("welcole sms!")
	fmt.Println(`
	1查看所有
	2添加
	3 修改
	4 删除
	5 退出
	`)
}

接口

import "fmt"

//接口 是一种类型

type cat struct{}
type dog struct{}
type animal interface {
	speak()
}

func (c cat) speak() {
	fmt.Println("喵喵喵")
}
func (d dog) speak() {
	fmt.Println("汪汪汪")
}
func da(a animal) {
	fmt.Println("方法执行")
	a.speak()
}
func main() {
	var (
		cat = cat{}
		dog = dog{}
	)
	da(cat)
	da(dog)
}

如果是使用值接受者与使用指针接受者实现接口的区别

  • 使用值接受者实现接口,结构体和结构体指针类型的变量都能存
  • 使用指针接受者实现接口,只能存结构体指针类型

在这里插入图片描述

类型断言


//类型断言
func main() {
	assign(12)
	assin2(true)
}
func assin2(a interface{}) {
	fmt.Printf("%T\n", a)

	switch a.(type) {
	case string:
		fmt.Println("string is :", a.(string))
	case bool:
		fmt.Println(" bool ", a)
	default:
		fmt.Println("default")
	}
}
func assign(a interface{}) {
	fmt.Printf("%T", a)
	s, ok := a.(string)
	if ok {
		fmt.Println("this is string ", s)
	} else {
		fmt.Println("其他类型")
	}
}

rand

func randDemo() {
	rand.Seed(time.Now().Unix())
	for i := 1; i < 6; i++ {
		r1 := rand.Int()
		r2 := rand.Intn(10) //0<=x<10
		fmt.Println(r1, r2)
	}
}

2.3.1 元组赋值

元祖赋值是另一种形式的赋值语句,它允许同时更新多个变量的值,在赋值之前,赋值语句右边的所有表达式将会先进行求值,然后在统一更新左边对应变量的值。这对于处理有些同时出现在元组赋值语句左右两边的变量很有帮助,例如可以交换两个变量的值

x, y = y, x

a[i], a[j] = a[j], a[i]

计算两个整数的最大公约数(GCD)

func gcd(x, y int) int {
    for y != 0 {
        x, y = y, x%y
    }
    return x
}

计算斐波那契数列(Fibonacci)的第N个数:

func fib(n int) int {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        x, y = y, x+y
    }
    return x
}

元组赋值也可以使一系列琐碎赋值更加紧凑。

i, j, k = 2, 3, 5

基于指针对象的方法

当调用一个函数时,会对其每一个参数值值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够避免这种默认的拷贝,这种情况我们就需要用到了指针。对应到我们这里用来更新接收器的对象方法,当这个接受者变量本身比较大时。我们就可以用其指针而不是对象来声明方法。

func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor
}

这个方法的名字是(*Point).ScaleBy 。这里的括号必须的;没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)

在现实的程序里,一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有的Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数。

要想调用指针型方法(*Point).ScaleBy,只要提供一个Point类型的指针即可,如下

r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"

或者

p := Point{1, 2}
pptr := &p
pptr.ScaleBy(2)
fmt.Println(p) // "{2, 4}"

或者

p := Point{1, 2}
(&p).ScaleBy(2)
fmt.Println(p) // "{2, 4}"

相比第一种,后两种方法比较笨拙,幸运的是,go语言本身会帮我们,如果接收器p是一个Point类型的变量,并且其方法需要一个Point指针作为接收器,可以用下面这种简短的写法:

p.ScaleBy(2)

编译器会隐式地帮我们用&p去调用ScaleBy这个方法。这种简写方法只适用于变量,包括struct里的字段以及array和slice内的元素。

是否使用指针对象方法

  1. 不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换。
  2. 在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面的因素,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。熟悉C或者C++的人这里应该很快能明白。

下载网络图片

func downPic() {
	client := &http.Client{}
	url := "https://i5.mmzztt.com/2019/06/20a03.jpg"
	request, err := http.NewRequest("GET", url, nil)

	request.Header.Add("referer", "https://www.mzitu.com/190776")
	request.Header.Add("accept", "image/webp,image/apng,image/*,*/*;q=0.8")

	if err != nil {
		fmt.Println("请求失败")
		panic(err)
	}
	//处理请求结果
	response, err := client.Do(request)
	defer response.Body.Close()
	all, err := ioutil.ReadAll(response.Body)
	ioutil.WriteFile("d:/"+path.Base(url), all, 0644)

}

完整示例

package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery" //类似于 jquery
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
)

var finished = make(chan struct{})

// goquery 学习使用 下载图片
func main() {
	//
	strUrl := "https://www.mzitu.com/179574"
	res, err := http.Get(strUrl)
	if err != nil {
		log.Println(err)
		return
	}
	defer res.Body.Close()

	//解析获取的 内容 为Dom
	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	picUrl, exists := doc.Find(".main-image").Find("img").Attr("src")
	//fmt.Println("图片路径-------", picUrl, exists)//https://i5.mmzztt.com/2019/06/20a01.jpg
	if !exists { //返回true表示获取到属性
		log.Fatalln("获取图片路径出现错误", picUrl)
		return
	}

	//从  下一页 上一页位置获取 总页码
	find := doc.Find(".pagenavi").Find("span")
	node := goquery.NewDocumentFromNode(find.Get(find.Size() - 2))
	picCountNumInt, err := strconv.Atoi(node.Text())
	if err != nil {
		log.Println("总页数 字符串转换 数字出错")
		return
	}
	//url前缀 和文件名
	picUrlPrefix, fileName := filepath.Split(picUrl)
	log.Println(picUrlPrefix, "| 总页数", picCountNumInt)

	for i := 1; i <= picCountNumInt; i++ {
		newFileName := strings.Replace(fileName, "01", fmt.Sprintf("%02d", i), 1)
		downPic(picUrlPrefix+newFileName, picUrl)
	}
	fmt.Println(picCountNumInt)

}

//下载图片
func downPic(name string, refererurl string) {
	//|创建存储路径
	savePath := "d:/pic/ps/179574"
	os.MkdirAll(savePath, 0666)

	client := &http.Client{}
	request, err := http.NewRequest("GET", name, nil)
	request.Header.Add("referer", refererurl) //需要设置 referer 才能访问到
	request.Header.Add("accept", "image/webp,image/apng,image/*,*/*;q=0.8")

	if err != nil {
		fmt.Println("请求失败")
		panic(err)
	}
	//处理请求结果
	response, err := client.Do(request)
	defer response.Body.Close()
	all, err := ioutil.ReadAll(response.Body)
	ioutil.WriteFile(savePath+"/"+path.Base(name), all, 0644)
	log.Println(name)
}

//输出2位整数,不够2位补0
func intToStr() {
	n := 32
	sInt := fmt.Sprintf("%02d", n)
	log.Println(sInt, "===========")
}

正则表达式

package main

import (
	"fmt"
	"log"
	"regexp"
)

const text = `my email is [email protected]
[email protected]
[email protected]
em2 is [email protected]
`

func main() {

	/********************************************
	.|表示匹配任意字符
	+表示匹配一个或多个
	* 表示匹配零个或多个
	*********************************************/
	//compile := regexp.MustCompile(`([a-zA-Z0-9]+)@[a-zA-Z0-9]+\.[a-zA-Z0-9]+`) //\在其他中需要用双斜杆
	//compile := regexp.MustCompile(`[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z0-9.]+`) //与上面哪行相比是为了匹配出  [email protected](因为其中包含2个点)

	//3 使用正则表达式的提取功能
	compile := regexp.MustCompile(`([a-zA-Z0-9]+)@([a-zA-Z0-9]+)\.([a-zA-Z0-9.]+)`) //与上面哪行相比是为了匹配出  [email protected](因为其中包含2个点)
	findString := compile.FindString(text)
	log.Println(findString)
	//	查找所有的
	allString := compile.FindAllString(text, -1)
	log.Println(allString)
	log.Println(`3	使用正则表达式的提取功能|`)
	//3	使用正则表达式的提取功能|
	submatch := compile.FindAllStringSubmatch(text, -1) // 返回的是 [][]string

	for _, m := range submatch {
		for _, ma := range m {
			fmt.Printf("%s ", ma) //[email protected] qqs qq com
		}
		fmt.Println()
	}

}

命令行参数的解析

Go语言内置的flag包实现了命令行参数的解析
flag.Type()
基本格式如下:

flag.Type(flag名, 默认值, 帮助信息)*Type例如我们要定义姓名、年龄、婚否三个命令行参数,我们可以按如下方式定义:

package main

import (
	"flag"
	"log"
)

func main() {
	log.Println("flag 示例")
	name := flag.String("name", "张三", "姓名")
	age := flag.Int("age", 18, "年龄")
	married := flag.Bool("married", false, "婚否")
	flag.Parse()
	log.Println(name,age,married)//表示地址
	log.Println(*name,*age,*married)

}

在这里插入图片描述
flag.TypeVar()
基本格式如下: flag.TypeVar(Type指针, flag名, 默认值, 帮助信息) 例如我们要定义姓名、年龄、婚否三个命令行参数,我们可以按如下方式定义:

var name string
var age int
var married bool
flag.StringVar(&name, "name", "张三", "姓名")
flag.IntVar(&age, "age", 18, "年龄")
flag.BoolVar(&married, "married", false, "婚否")

flag.Parse()
上述定义好参数后,需要调用flag.Parse()来对命令行参数进行解析。
支持的命令行参数格式有以下几种:

  • -flag xxx|使用空格,一个-符号
  • --flag xxx|使用空格,两个-符号
  • -flag=xxx|使用等号,一个-符号
  • --flag xxx|使用等号,2个-符号

广度优先搜索走迷宫

package main

import (
	"fmt"
	"os"
)

/********************************************
 --------------------
	广度优先搜索 走迷宫
*********************************************/

func main() {
	//step 1  从文件加载地图
	maze := readMaze("Maze/maze.txt")

	showMap(maze)
	//左上角 和 右下角 作为 起点和终点 |
	steps := walk(maze, point{0, 0}, point{len(maze) - 1, len(maze[0]) - 1})
	num := stepNum(steps, point{len(maze) - 1, len(maze[0]) - 1})
	fmt.Printf("arrival terminal point: %d", num)

	fmt.Println("\n走过的路线是")
	showMap(steps)
}

type point struct {
	i, j int
}

// 方向 i是行,j是列|上左 下右
var dirs = [4]point{
	{-1, 0}, {0, -1}, {1, 0}, {0, 1},
}

//
func (p point) add(r point) point {
	return point{p.i + r.i, p.j + r.j}
}

//价差某个点是否越界,并返回该点的值
func (p point) at(grid [][]int) (int, bool) {

	//	检查行是否越界
	if p.i < 0 || p.i >= len(grid) {
		return 0, false
	}

	//检查列是否越界
	if p.j < 0 || p.j >= len(grid[p.i]) {
		return 0, false
	}
	return grid[p.i][p.j], true
}

// 走迷宫|广度优先搜索
func walk(maze [][]int, start, end point) [][]int {
	steps := make([][]int, len(maze))
	for i := range steps {
		steps[i] = make([]int, len(maze[i]))
	}
	//作为队列使用,存放走得通的点,当某个点要被搜索便把他出队列
	Q := []point{start}
	for len(Q) > 0 {
		//探索开始
		cur := Q[0]
		Q = Q[1:]
		if cur == end { //终点
			break
		}
		//	依次 上 左 下 右 走一遍
		for _, dir := range dirs {
			// next 是0
			// steps 点的值为0 (有值表示走过了)
			// next 不能等于start
			next := cur.add(dir)
			val, ok := next.at(maze)
			if !ok || val == 1 { //遇到了墙或者越界了
				continue
			}
			//	判断是否越界或者已经走过了
			val, ok = next.at(steps)
			if !ok || val != 0 {
				continue
			}
			if next == start {
				continue
			}
			//当前的步 数
			curStep, _ := cur.at(steps)
			steps[next.i][next.j] = curStep + 1
			//	next作为 下一个要被探索的点放进队列
			Q = append(Q, next)
		}
	}
	return steps
}

//查看到达某个点所走的步数
func stepNum(steps [][]int, end point) int {
	return steps[end.i][end.j]
}

// 显示迷宫地图
func showMap(maze [][]int) {
	for _, row := range maze {
		for _, col := range row {
			fmt.Printf("%2d ", col)
		}
		fmt.Println()
	}
}

// step 1  从文件加载地图
func readMaze(filename string) [][]int {
	file, err := os.Open(filename)
	if err != nil {
		panic("文件不存在")
	}
	defer file.Close()

	var row, col int

	fmt.Fscanf(file, "%d %d", &row, &col)

	maze := make([][]int, row)

	for i := range maze {
		maze[i] = make([]int, col)
		for j := range maze[i] {
			//fmt.Fscanf()遇到换行返回值为0,换成fmt.Fscan|https://blog.csdn.net/hp_cpp/article/details/100834645
			//fmt.Fscanf(file, "%d",&maze[i][j])|这种会把换行识别为0 https://segmentfault.com/q/1010000015629978?sort=created
			fmt.Fscan(file, &maze[i][j])
		}
	}

	return maze
}

地图

6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 0 0 0

go 数据结构

稀疏数组

例如,五子棋存盘

package main

import (
	"fmt"
	"github.com/huandu/xstrings"
)

type valNode struct {
	row, col, val int
}

//稀疏数组
func main() {
	//1 创建原始数组
	var chessMap [11][11]int
	chessMap[1][2] = 1 //黑
	chessMap[2][3] = 2 //蓝

	showMap(chessMap)

	//	转为稀疏数组
	// 设计结构体|
	var sparseArr []valNode
	//表示规模的节点|记录二位数组的行 列
	sparseArr = append(sparseArr, valNode{len(chessMap), len(chessMap[0]), 0}, )
	for i, v := range chessMap {
		for j, v2 := range v {
			if v2 != 0 {
				//创建值节点
				node := valNode{row: i, col: j, val: v2,}
				sparseArr = append(sparseArr, node)
			}
		}
	}
	//稀疏数组
	fmt.Println(xstrings.Center("稀疏数组", 20, "_"))
	for i, node := range sparseArr {
		fmt.Printf("%d: %-2d %-2d %2d\n", i, node.row, node.col, node.val)
	}

	//	恢复原始二位数组

	var chessMap2 [11][11]int
	for i, node := range sparseArr {
		if i == 0 { //第一个节点是规模节点
			continue
		}
		chessMap2[node.row][node.col] = node.val
	}
	showMap(chessMap2)
}

func showMap(chessMap [11][11]int) {
	fmt.Println(xstrings.Center("showMap", 30, "_"))
	for _, v := range chessMap {
		for _, v2 := range v {
			fmt.Printf("%d\t", v2)
		}
		fmt.Println()
	}
	fmt.Println()
}

单向队列

package main

import (
	"errors"
	"fmt"
	"github.com/huandu/xstrings"
	"os"
)

//使用front 和rear记录队列前后端的下标
/**********************************
使用数组实现队列的思路
1创建一个数组array,作为队列的一个字段
2 front初始化为-1
3real,表示队列尾部,初始化为-1
4 完成队列的基本查找
 Addququq 加入数据到队列
 Getququ
 ShowQuquq
**********************************/
type Queue struct {
	maxSize int
	array   [4]int //数组
	front   int    //队首
	Real    int    //队尾
}

func (this *Queue) AddQueue(val int) (err error) {
	//判断是否已满
	if this.Real == this.maxSize-1 { //指向了最后
		return errors.New("queue full")
	}
	this.Real++ //后移
	this.array[this.Real] = val
	return
}
func (this *Queue) ShowQueue() {
	//this.Front 不包含队首的元素
	//找到队首遍历到队尾|front队首,不包含第一个元素
	fmt.Println(xstrings.Center("队列当前情况", 20, " "))
	for i := this.front + 1; i <= this.Real; i++ {
		fmt.Printf("arry=[%d]=%d\t ", i, this.array[i])
	}
	fmt.Println()
}

//取出队列
func (this *Queue) GetQueue() (val int, err error) {
	//判断是否为空
	if this.front == this.Real {
		return -1, errors.New("queue empty")
	}
	this.front++
	return this.array[this.front], nil
}
func main() {
	queue := &Queue{
		maxSize: 4,
		front:   -1,
		Real:    -1,
	}
	var (
		key string
		val int
	)
	for {
		fmt.Println("1 输入add添加数据到队列")
		fmt.Println("2 输入get  获取队列数据")
		fmt.Println("3 输入show显示队列")
		fmt.Println("4 exit")
		fmt.Println()
		fmt.Scanln(&key)
		fmt.Println("输入的是", key)
		switch key {
		case "add":
			fmt.Println("输入要入队列的数据")
			fmt.Scanln(&val)
			err := queue.AddQueue(val)
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("加入队列OK")
			}
		case "get":
			getQueue, err := queue.GetQueue()
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println("取到的数据:", getQueue)
			}
		case "show":
			queue.ShowQueue()
		case "exit":
			os.Exit(1)
		}

	}
}

/**********************************
**********************************/

/**********************************
入队列称为addqueue ,addqueue处理需要两个步骤
1 将尾指针往后移,rear+1 front==rear 空
2 如果 尾指针rear小于等于队列的最大下标 Maxsize-1,则将数据存入rea所指的数组元素中,否则无法存入
**********************************/

环形队列

package main

import (
	"errors"
	"fmt"
	"github.com/huandu/xstrings"
	"os"
)

//使用结构体还礼队列
type CircleQueue struct {
	maxSize int
	array   [5]int
	head    int //队首
	tail    int //队列尾
}

// 入对垒
func (this *CircleQueue) Push(val int) (err error) {
	//判断是否已满
	if this.IsFull() {
		return errors.New(" is full")
	}
	this.array[this.tail] = val
	this.tail = (this.tail + 1) % this.maxSize //后移
	return
}

//取出队列
func (this *CircleQueue) Pop() (val int, err error) {
	//判断是否为空
	if this.IsEmpty() {
		return 0, errors.New(" is empty")
	}
	//head指向队首,包含队首元素
	val = this.array[this.head]
	this.head = (this.head + 1) % this.maxSize
	return
}
func (this *CircleQueue) ShowCircleQueue() {
	//this.Front 不包含队首的元素
	fmt.Println(xstrings.Center("环形队列当前情况", 20, " "))
	size := this.Size()
	if size == 0 {
		fmt.Println("队列为空")
		return
	}
	//设计一个辅助变量,指向head
	tempHead := this.head
	for i := 0; i < size; i++ {
		fmt.Printf("array[%d]=%d\t", tempHead, this.array[tempHead])
		tempHead = (tempHead + 1) % this.maxSize
	}
	fmt.Println()
}

//判断是不是满
func (this *CircleQueue) IsFull() bool {
	return (this.tail+1)%this.maxSize == this.head
}

//判断是否为空
func (this *CircleQueue) IsEmpty() bool {
	return this.tail == this.head
}
func (this *CircleQueue) Size() int {
	return (this.tail + this.maxSize - this.head) % this.maxSize
}
func main() {
	CircleQueue := &CircleQueue{
		maxSize: 5,
		tail:    0,
		head:    0,
	}
	var (
		key string
		val int
	)
	for {
		fmt.Println("1 输入add添加数据到队列")
		fmt.Println("2 输入get  获取队列数据")
		fmt.Println("3 输入show显示队列")
		fmt.Println("4 exit")
		fmt.Println()
		fmt.Scanln(&key)
		fmt.Println("输入的是", key)
		switch key {
		case "add":
			fmt.Println("输入要入队列的数据")
			fmt.Scanln(&val)
			err := CircleQueue.Push(val)
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("加入队列OK")
			}
		case "get":
			getCircleQueue, err := CircleQueue.Pop()
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println("取到的数据:", getCircleQueue)
			}
		case "show":
			CircleQueue.ShowCircleQueue()
		case "exit":
			os.Exit(1)
		}
	}
}

排序

快速排序法

//快速排序
func QuickSort(left int, right int, array *[10]int) {
	l := left
	r := right
	// pivot 是中轴, 支点
	pivot := array[(left+right)/2]
	//temp := 0

	//for 循环的目标是将比 pivot 小的数放到 左边
	//  比 pivot 大的数放到 右边
	for ; l < r; {
		//从  pivot 的左边找到大于等于pivot的值
		for ; array[l] < pivot; {
			l++
		}
		//从  pivot 的右边边找到小于等于pivot的值
		for ; array[r] > pivot; {
			r--
		}
		// 1 >= r 表明本次分解任务完成, break
		if l >= r {
			break
		}
		//交换
		temp:= array[l]
		array[l] = array[r]
		array[r] = temp
		//优化
		if array[l] == pivot {
			r--
		}
		if array[r] == pivot {
			l++
		}
	}
	// 如果  1== r, 再移动下
	if l == r {
		l++
		r--
	}
	// 向左递归
	if left < r {
		QuickSort(left, r, array)
	}
	// 向右递归
	if right > l {
		QuickSort(l, right, array)
	}
}

插入排序

func insertSorte(arr *[5]int) {
	//完成第一次,给第二个元素找打合适的位置插入
	for i := 1; i < len(arr); i++ {
		insertVal := arr[i]
		insertIndex := i - 1 //待插入的前一个元素下标
		for insertIndex >= 0 && arr[insertIndex] < insertVal {
			arr[insertIndex+1] = arr[insertIndex] //数据后移
			insertIndex--
		}
		//	插入
		if insertIndex+1 != i {
			arr[insertIndex+1] = insertVal
		}
	}
}

选择排序

//选择排序|每次选择最大的 和前面的 做交换
func SelectSort(arr *[5]int) {
	for j := 0; j < len(arr)-1; j++ {
		max := arr[j]
		maxIndex := j
		for i := j + 1; i < len(arr); i++ {
			if max < arr[i] {
				max = arr[i]
				maxIndex = i
			}
		}
		if maxIndex != j {
			arr[j], arr[maxIndex] = arr[maxIndex], arr[j]
		}
	}
}

go双向链表


import (
	"container/list"
	"fmt"
)

func main() {
	l := list.New()
	fmt.Println(l)
	//向双向链表添加元素
	l.PushFront("a")
	l.PushBack("b")               //向最后添加元素[a,b]
	l.PushBack("c")               //[a,b,c]
	l.InsertBefore("d", l.Back()) //back取出最后一个|[a,b,d,c]

	//遍历
	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value)
	}

	fmt.Println("双向链表头:", l.Front().Value)
	fmt.Println("双向链表尾:", l.Back().Value)
	//	移动元素顺序
	l.MoveBefore(l.Front(), l.Back()) //把第一个移动到最后一个前面
	listList(l)
	l.MoveToFront(l.Back()) //把最后一个移动到最前面
}

func listList(l *list.List) {
	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Print(e.Value, " ")
	}
}

内置包

sort

排序切片

//sort对切片排序
func SoerDemo() {
	num := []int{3, 7, 1, 9}
	sort.Ints(num) //升序

	sort.Sort(sort.IntSlice(num)) //也是升序排列

	fmt.Println("升序排序后:", num)
	sort.Sort(sort.Reverse(sort.IntSlice(num))) //降序排列
	fmt.Println(num)

	//	string
	str := []string{"a", "c", "中国"}
	sort.Sort(sort.Reverse(sort.StringSlice(str)))
	sort.Strings(str)
	fmt.Println(str)
	//	该方法要求str,如果包含返回在切片的索引,如果不存在,返回该值应该在切片中哪个位置
	n := sort.SearchStrings(str, "中国")
	fmt.Println("中国在前片中位置", n)
}
发布了308 篇原创文章 · 获赞 70 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/u012848709/article/details/103942375
今日推荐