第一章:数据结构
一、稀疏数组
1、思想
把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
2、示例
使用稀疏数组存放二维数组
package mainimport ( fmt os bufio _ io)type ValNode struct { row int col int val int}func main() { //创建原始数组 var chessMap [11][11]int chessMap[1][2] = 1 chessMap[2][3] = 2 //输出原始数组 fmt.Println(原始数组为:) for _, v := range chessMap { for _, k := range v { fmt.Printf(%dt, k) } fmt.Println() } //将原始数组转换为稀疏数组 //遍历原始数组,发现不为零的元素,就将该节点放入一个node结构体,并将其放入对应的切片中 var sparseArr []ValNode //标准的稀疏数组应含有记录元素的二维数组的规模(行、列、默认值) valNode := ValNode{ row: 11, col: 11, val: 0, } sparseArr = append(sparseArr, valNode) for i, v := range chessMap { for j, k := range v { if k != 0 { valNode := ValNode{ row: i, col: j, val: k, } sparseArr = append(sparseArr, valNode) } } } //输出稀疏数组 fmt.Println(转换后的稀疏数组为:) for i, v := range sparseArr { fmt.Printf(%d: %dt%dt%dn, i, v.row, v.col, v.val) } //将稀疏数组写入文件 filePath := d:/test/sparse.txt file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0644) if err != nil { fmt.Println(file create err, err) return } defer file.Close() for _, v := range sparseArr { str := fmt.Sprintf(%dt%dt%dn, v.row, v.col, v.val) writer := bufio.NewWriter(file) writer.WriteString(str) writer.Flush() } //读取稀疏数组 //先创建一个原始数组 var chessMap2 [11][11]int for i, v := range sparseArr { if i != 0 { chessMap2[v.row][v.col] =v.val } } //查看恢复后的原始数组 fmt.Println(恢复后的数组) for _, v := range chessMap2 { for _, v2 := range v { fmt.Printf(%dt, v2) } fmt.Println() }}二、队列(queue)
1、介绍
- 队列是一个有序列表,可以用数组或是链表来实现
- 遵循先入先 出的原则:即先存入队列的数据,要先取出,后存入的数据要后取出
2、使用数组模拟队列
代码
package mainimport ( fmt errors os)//定义结构体,管理队列type Queue struct { maxSize int //队列最大值 array [5]int front int //队首指针,不包含队首元素 rear int //队尾指针,包含队尾元素}//向队列中插入元素的方法func (this *Queue) Add(val int) (err error) { //判断队列是否已满 if this.rear == this.maxSize -1 { return errors.New(queue full) } //指针后移 this.rear++ //插入数据 this.array[this.rear] = val return}//显示当前队列方法func (this *Queue) show() { fmt.Println(当前队列情况是:) //找到队首,遍历到队尾 for i := this.front + 1; i <= this.rear; i++ { fmt.Printf(arr[%d] = %dn, i, this.array[i]) }}//取出队列中元素的方法func (this *Queue) Get() (val int, err error){ //队首指针等于队尾指针时,队列为空 if this.front == this.rear { return -1, errors.New(队列为空) } this.front++ val = this.array[this.front] return val, err}func main() { queue := Queue{ maxSize: 5, array: [5]int{}, front: -1, rear: -1, } //定义变量 val 接收 要放入队列中的元素,key接收控制台输入 var val int var key string for true { fmt.Println(1. 输入add 表示向队列中添加元素) fmt.Println(2. 输入get 表示从队列中取出元素) fmt.Println(3. 输入show 表示显示当前队列) fmt.Println(4. 输入exit 表示退出) fmt.Scanln(&key) switch key { case add: fmt.Println(请输入要添加的数:) fmt.Scanln(&val) err := queue.Add(val) if err != nil { fmt.Println(add error, err.Error()) } case get: fmt.Println(取出数据) val, err := queue.Get() if err != nil { fmt.Println(get error, err) } else { fmt.Println(取出数据成功:, val) } case show: //fmt.Println(当前队列为:) queue.show() case exit: os.Exit(0) } }}3、使用数组模拟环形队列
思路:
- ( 尾指针 + 1 )% 最大容量 = 头指针 时队列满
- 尾指针 = 头指针 时队列空
- 初始化时 尾指针 = 头指针 = 0
- ( 尾指针 + 最大容量 - 头指针 ) % 最大容量 = 队列中有几个元素
代码
package mainimport ( fmt errors os)//定义结构体管理队列type CircelQueue struct { maxSize int //队列最大值 array [5]int head int //队首指针,不包含队首元素 tail int //队尾指针,包含队尾元素}//入队列方法func (this *CircelQueue) PushQueue(val int) (err error){ //判断队列是否已满 if this.IsFull() { return errors.New(queue full) } //队尾指针在队尾元素后一位(不包含队尾元素) this.array[this.tail] = val this.tail = (this.tail + 1) % this.maxSize return}//出队列方法func (this *CircelQueue) PopQueue() (val int, err error) { //判断队列是否为空 if this.IsEmpty() { return 0, errors.New(queue empty) } //队首指针指向队首元素(包含队首元素) val = this.array[this.head] this.head = (this.head + 1) % this.maxSize return val, err}//显示当前队列中的元素方法func (this *CircelQueue) ShowQueue() { //判断队列是否为空 if this.IsEmpty() { fmt.Println(队列为空) } //创建辅助变量,指向队首 tempHead := this.head for i := 0; i < this.Size(); i++ { fmt.Printf(arr[%d] = %dt, tempHead, this.array[tempHead]) tempHead = (tempHead + 1) % this.maxSize } fmt.Println()}//判断队列满方法func (this *CircelQueue) IsFull() bool { return (this.tail + 1) % this.maxSize == this.head}//判断队列空方法func (this *CircelQueue) IsEmpty() bool { return this.head == this.tail}//获取队列中元素个数func (this *CircelQueue) Size() int { return (this.tail + this.maxSize - this.head) % this.maxSize}func main() { queue := CircelQueue{ maxSize: 5, array: [5]int{}, head: 0, tail: 0, } //定义变量 val 接收 要放入队列中的元素,key接收控制台输入 var val int var key string for true { fmt.Println(1. 输入add 表示向队列中添加元素) fmt.Println(2. 输入get 表示从队列中取出元素) fmt.Println(3. 输入show 表示显示当前队列) fmt.Println(4. 输入exit 表示退出) fmt.Scanln(&key) switch key { case add: fmt.Println(请输入要添加的数:) fmt.Scanln(&val) err := queue.PushQueue(val) if err != nil { fmt.Println(add error, err.Error()) } case get: fmt.Println(取出数据) val, err :=queue.PopQueue() if err != nil { fmt.Println(get error, err) } else { fmt.Println(取出数据成功:, val) } case show: fmt.Println(当前队列为:) queue.ShowQueue() case exit: os.Exit(0) } }}三、链表
1、介绍
链表是有序的列表,在内存的形式如下
2、单链表
案例
用单链表实现人员名单管理,添加人员是在链表尾 部增加
package mainimport fmttype HeroNode struct { id int name string aliasName string next *HeroNode //指针,指向下个节点}//项链表插入节点func InsertHeroNode(head *HeroNode, newHeroNode *HeroNode) { //找到链表的尾部 //创建辅助指针 temp := head for { if temp.next == nil { //链表为空 break } temp = temp.next //指针后移 } //将新节点加入到链表 temp.next = newHeroNode}//显示链表func ListHeroNode(head *HeroNode) { //判断链表是否为空 temp := head if temp.next == nil { fmt.Println(链表为空) } //遍历链表,输出链表信息 for { fmt.Printf([%d, %s, %s]==>, temp.next.id, temp.next.name, temp.next.aliasName) //判断列表是否为最后 temp = temp.next if temp.next == nil { break } }}func main() { //创建头结点 head := &HeroNode{} hero1 := &HeroNode{ id: 1, name: 松江, aliasName: 及时雨, next: nil, } hero2 := &HeroNode{ id: 2, name: 卢俊义, aliasName: 玉麒麟, next: nil, } InsertHeroNode(head, hero2) InsertHeroNode(head, hero1) ListHeroNode(head)}用单链表实现人员名单管理,根据id顺序插入人员
package mainimport fmttype HeroNode struct { id int name string aliasName string next *HeroNode //指针,指向下个节点}//项链表插入节点func InsertHeroNode(head *HeroNode, newHeroNode *HeroNode) { //找到链表的尾部 //创建辅助指针 temp := head for { if temp.next == nil { //链表为空 break } temp = temp.next //指针后移 } //将新节点加入到链表 temp.next = newHeroNode}func InsertHeroNode2(head *HeroNode, newHeroNode *HeroNode) { //找到链表的尾部 //创建辅助指针 temp := head flag := true //判断新元素要插入的位置 for { if temp.next == nil { //temp指向链表最后一个元素 break } else if temp.next.id > newHeroNode.id { //新元素插入到temp后 break } else if temp.next.id == newHeroNode.id { //要插入元素的id与现有元素id冲突 flag = false break } temp = temp.next } if !flag { fmt.Println(id已存在, newHeroNode.id) return } else { newHeroNode.next = temp.next temp.next= newHeroNode }}//显示链表func ListHeroNode(head *HeroNode) { //判断链表是否为空 temp := head if temp.next == nil { fmt.Println(链表为空) } //遍历链表,输出链表信息 for { fmt.Printf([%d, %s, %s]==>, temp.next.id, temp.next.name, temp.next.aliasName) //判断列表是否为最后 temp = temp.next if temp.next == nil { break } }}//删除节点func DeleteHeroNode(head *HeroNode, id int) { temp := head flag := false //判断是否有该节点 for { if temp.next == nil { //temp指向链表最后一个元素 break } else if temp.next.id == id { //要插入元素的id与现有元素id冲突 flag = true break } temp = temp.next } if flag { temp.next = temp.next.next } else { fmt.Println(要删除的id不存在) }}func main() { //创建头结点 head := &HeroNode{} hero1 := &HeroNode{ id: 1, name: 宋江, aliasName: 及时雨, next: nil, } hero2 := &HeroNode{ id: 2, name: 卢俊义, aliasName: 玉麒麟, next: nil, } hero3 := &HeroNode{ id: 3, name: 林冲, aliasName: 豹子头, next: nil, } hero4 := &HeroNode{ id: 4, name: 吴用, aliasName: 智多星, next: nil, } InsertHeroNode2(head, hero3) InsertHeroNode2(head, hero1) InsertHeroNode2(head, hero2) InsertHeroNode2(head, hero4) ListHeroNode(head) fmt.Println() DeleteHeroNode(head, 3) DeleteHeroNode(head, 1) ListHeroNode(head)}3、双向链表
package mainimport fmttype HeroNode struct { id int name string aliasName string next *HeroNode //指针,指向下个节点 pre *HeroNode //指向前一个节点}//项链表插入节点func InsertHeroNode(head *HeroNode, newHeroNode *HeroNode) { //找到链表的尾部 //创建辅助指针 temp := head for { if temp.next == nil { //链表为空 break } temp = temp.next //指针后移 } //将新节点加入到链表 temp.next = newHeroNode newHeroNode.pre = temp}func ListHeroNode(head *HeroNode) { //判断链表是否为空 temp := head if temp.next == nil { fmt.Println(链表为空) return } //遍历链表,输出链表信息 for { fmt.Printf([%d, %s, %s]==>, temp.next.id, temp.next.name, temp.next.aliasName) //判断列表是否为最后 temp = temp.next if temp.next == nil { break } }}//逆序遍历双向链表func ListHeroNode2(head *HeroNode) { //判断链表是否为空 temp := head if temp.next == nil { fmt.Println(链表为空) return } //让temp指向双向链表最后一个节点 for { if temp.next == nil { break } temp = temp.next } //遍历链表,输出链表信息 for { fmt.Printf([%d, %s, %s]==>, temp.id, temp.name, temp.aliasName) //判断列表是否为表头 temp = temp.pre if temp.pre == nil { break } }}func InsertHeroNode2(head *HeroNode, newHeroNode *HeroNode) { //找到链表的尾部 //创建辅助指针 temp := head flag := true //判断新元素要插入的位置 for { if temp.next == nil { //temp指向链表最后一个元素 break } else if temp.next.id > newHeroNode.id { //新元素插入到temp后 break } else if temp.next.id == newHeroNode.id { //要插入元素的id与现有元素id冲突 flag = false break } temp = temp.next } if !flag { fmt.Println(id已存在, newHeroNode.id) return } else { newHeroNode.next = temp.next temp.next= newHeroNode if temp.next != nil { temp.next.pre = newHeroNode } temp.next = newHeroNode }}//删除节点func DeleteHeroNode(head *HeroNode, id int) { temp := head flag := false //判断是否有该节点 for { if temp.next == nil { //temp指向链表最后一个元素 break } else if temp.next.id == id { //要插入元素的id与现有元素id冲突 flag = true break } temp = temp.next } if flag { temp.next = temp.next.next if temp.next != nil { temp.next.pre = temp } } else { fmt.Println(要删除的id不存在) }}func main() { //创建头结点 head := &HeroNode{} hero1 := &HeroNode{ id: 1, name: 宋江, aliasName: 及时雨, next: nil, } hero2 := &HeroNode{ id: 2, name: 卢俊义, aliasName: 玉麒麟, next: nil, } hero3 := &HeroNode{ id: 3, name: 林冲, aliasName: 豹子头, next: nil, } hero4 := &HeroNode{ id: 4, name: 吴用, aliasName: 智多星, next: nil, } InsertHeroNode2(head, hero1) InsertHeroNode2(head, hero4) InsertHeroNode2(head, hero3) InsertHeroNode2(head, hero2) ListHeroNode(head) fmt.Println(正向遍历) //ListHeroNode2(head) //fmt.Println(反向遍历) DeleteHeroNode(head, 3) ListHeroNode(head)}4、环形链表
package mainimport ( fmt)//创建结构体type CatNode struct { id int name string next *CatNode}//向环形列表添加节点func InsertNode(head *CatNode, NewCatNode *CatNode) { //判断要加入的节点是否是第一个节点 if head.next == nil { head.id = NewCatNode.id head.name = NewCatNode.name head.next = head fmt.Println(NewCatNode, 加入到环形列表) return } //定义一个临时变量 temp := head for { //temp 指向了头节点 if temp.next == head { break } temp = temp.next } //加入到环境列表 temp.next = NewCatNode NewCatNode.next = head}//输出环形列表func ListCircleSingleLink(head *CatNode) { fmt.Println(环形列表情况如下) temp := head if temp.next == nil { fmt.Println(环形列表为空) return } //遍历环形列表 for { fmt.Printf(猫的信息为:[id = %d, name = %s] -->, temp.id, temp.name) if temp.next == head { break } temp = temp.next }}//删除一个节点func DelCatNode(head *CatNode, id int) *CatNode { //创建两个辅助节点 temp := head helper := head //空链表 if temp.next == nil { fmt.Println(链表为空) return head } //链表中只有一个节点 if temp.next == head { if temp.id == id { temp.next = nil } return head } //将helper指向链表最后 for { if helper.next == head { break } helper = helper.next } //链表中有两个及两个以上节点 flag := true for { if temp.next == head { break } if temp.id == id { if temp == head { //要删除的节点为头节点 head = head.next } helper.next = temp.next fmt.Printf(节点id = %d 被删除, id) flag = false break } temp = temp.next helper = helper.next } if flag { if temp.id == id { helper.next = temp.next fmt.Printf(节点id = %d 被删除, id) } else { fmt.Println(没有该节点) } } return head}func main() { head := &CatNode{} cat1 := &CatNode{ id: 1, name: tom, next: nil, } cat2 := &CatNode{ id: 2, name: jack, next: nil, } cat3 := &CatNode{ id: 3, name: monkey, next: nil, } cat4 := &CatNode{ id: 4, name: dog, next: nil, } InsertNode(head, cat1) InsertNode(head, cat2) //InsertNode(head, cat3) ListCircleSingleLink(head) fmt.Println() head = DelCatNode(head, 1) fmt.Println(删除后) ListCircleSingleLink(head) InsertNode(head, cat4) ListCircleSingleLink(head)}5、josephus 问题
设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依此类推,直到所有人出列为止,由此产生一个出队编号的序列
package mainimport fmttype Node struct { id int next *Node}//num 表示节点个数func AddBoy(num int) *Node { //初始化头节点(空) first := &Node{} // 因为 first 指针固定指向第一个节点,因此 first 指针不能移动,需要一个辅助指针 // 辅助指针 curNode ,指向当前的节点 curNode := &Node{} //节点数必须大于等于1 if num < 1 { fmt.Println(节点数不得小于1) return first } //构建循环列表 for i := 0; i <= num; i++ { node := &Node{ id: i, next: nil, } if i == 1 { //列表中只有一个节点 first = node curNode = node //自己指向自己 curNode.next = first } else { //列表中有两个及以上节点 curNode.next = node curNode = node //最后一个节点指向头节点,形成循环 curNode.next = first } } return first}//显示环形链表中所有节点func ShowBoy(first *Node) { // 单独处理空链表 if first.next == nil { fmt.Println(empty link list) return } // 创建一个辅助指针,帮助遍历 curNode := first for { fmt.Printf(节点编号 : %d -> , curNode.id) // 退出循环的条件 // 单向环形链表的最后一个节点指向头指针 if curNode.next == first { break } curNode = curNode.next } fmt.Println()}//统计节点数量func countNode(first *Node) int { //空链表 if first.next == nil { fmt.Println(链表为空) return 0 } //创建辅助指针及计数的变量 tail := first count := 0 //循环次数就是节点数量 for { count++ //最后一个节点指向头节点,退出循环 if tail.next == first { break } tail = tail.next } return count}//解决约瑟夫问题算法// startNode: 起始节点 countNum:累加数func PlayGame(first *Node, startNode int, countNum int) { //空链表 if first.next == nil { fmt.Println(链表为空) return } //开始节点不可以大于节点总数 if startNode > countNode(first) { fmt.Println(开始节点不可以大于节点总数) return } //定义辅助指针,指向最后一个节点 tail := first for { if tail.next == first { // tail 指向了最后一个节点 break } tail = tail.next } //让 first 移动到 startNode(要出列的节点是first指向的节点) for i := 1; i <= startNode - 1; i++ { first = first.next tail = tail.next } //开始数countNum,然后将first指向的节点删除 for { for i := 1; i <= countNum - 1; i++ { first = first.next tail = tail.next } fmt.Printf(节点 %d 出列n, first.id) //移除first指向的节点 first = first.next tail.next = first //如果tail = first ,说明列表中只剩一个节点 if tail == first { break } } fmt.Printf(节点 %d 出列n, first.id)}func main() { first := AddBoy(50) ShowBoy(first) PlayGame(first, 20,3)}4、排序
排序是将一组数据,已制定的顺序进行排列的过程
4.1、冒泡排序
4.2、选择排序
package mainimport fmtfunc SelectSort(arr []int) { //交换次数 for i := 0; i < len(arr) - 1; i++ { //假设切片第一个元素为最大值 max := arr[i] maxIndex := i //循环与后面的数比较 for j := i + 1; j < len(arr); j++ { //假设不成立,找到了真正的最大值 if max < arr[j] { max = arr[j] maxIndex = j } } //交换 if maxIndex != i { arr[i], arr[maxIndex] = arr[maxIndex], arr[i] } fmt.Printf(第%d次,%vn, i + 1, arr) }}func main() { arr := []int{50,5,21,35,11,84} fmt.Println(排序前:, arr) SelectSort(arr) fmt.Println(排序后:, arr)}4.3、插入排序
插入排序属于内部排序法,是对于预排序的元素以插入的方式找寻该元素的适当位置
排序法思想
把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表无素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
package mainimport ( fmt)func InsertSort(arr []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 } fmt.Printf(第%d次后,数组为:%vn, i, arr) }}func main() { arr := []int{2,32,12,-3,4,9,0} fmt.Println(排序前数组:, arr) fmt.Println() InsertSort(arr) fmt.Println(排序后数组:, arr)}4.4、快速排序
package mainimport ( fmt)//left:表示数组左边的下标,right:表示数组右边的下标func QuickSort(left int, right int, arr []int) { l := left r := right //创建变量,存储数组中间的值(以数组中间的值,将数组分开) pivot := arr[(l + r) / 2] //将比 pivot 大的数放在 pivot 右边, //将比 pivot 小的数放在 pivot 左边 for l < r { //从 pivot 的左边找到大于等于 pivot 的值 for arr[l] < pivot { l++ } //从 pivot 的右边找到小于于等于 pivot 的值 for arr[r] > pivot { r-- } //left >= right 分组完成 if l >= r { break } //交换 arr[l], arr[r] = arr[r], arr[l] if arr[l] == pivot { r-- } if arr[r] == pivot { l++ } } //如果相等,指针再移动下 if l == r { l++ r-- } //向左递归 if left < r { QuickSort(left, r, arr) } //向右递归 if right > l { QuickSort(l, right, arr) }}func main() { arr := []int{-9,78,0,23,-567,70} fmt.Println(排序前:, arr) QuickSort(0, len(arr) - 1, arr) fmt.Println(排序后:, arr)}第二章:栈(stack)
一、栈的介绍
栈是一个先入后出 的有序列表
栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,成为栈顶(top),另一端为固定的一端,称为栈底(bottom)
二、数组模拟栈
package mainimport ( fmt errors)type Stack struct { MaxTop int //栈最大容量 Top int //栈顶指针 arr [5]int}//向栈中插入元素func (this *Stack) Push(val int) (err error) { //栈满 if this.Top == this.MaxTop - 1 { fmt.Println(stack full) return errors.New(stack full) } //先移动指针,再填入数据 this.Top++ this.arr[this.Top] = val return}//出栈func (this *Stack) Pop() (val int, err error) { //栈空 if this.Top == -1 { //fmt.Println() return 0, errors.New(stack empty) } //先弹出数据,在移动指针 val = this.arr[this.Top] this.Top-- return val, nil}//遍历栈func (this *Stack) Show() { //栈空 if this.Top == -1 { fmt.Println(stack empty) return } for i := this.Top; i >= 0; i-- { fmt.Printf(arr[%d] = %vn, i, this.arr[i]) }}func main() { stack := &Stack{ MaxTop: 5, Top: -1, arr: [5]int{}, } stack.Push(1) stack.Push(2) stack.Push(3) stack.Push(4) stack.Push(5) //stack.Push(6) fmt.Println() stack.Show() val, err := stack.Pop() if err != nil { fmt.Println(stack pop err, err) } fmt.Println(pop val, val)}第三章:二叉树遍历
package mainimport ( fmt)type BinaryTree struct { id int name string left *BinaryTree right *BinaryTree}//前序遍历func Preorder(node *BinaryTree) { if node != nil { fmt.Printf(id = %d, name = %s n, node.id, node.name) Preorder(node.left) Preorder(node.right) }}//中序遍历func InfixOrder(node *BinaryTree) { if node != nil { Preorder(node.left) fmt.Printf(id = %d, name = %s n, node.id, node.name) Preorder(node.right) }}//后序遍历func PostOrder(node *BinaryTree) { if node != nil { Preorder(node.left) Preorder(node.right) fmt.Printf(id = %d, name = %s n, node.id, node.name) }}func main() { //构建二叉树 root := &BinaryTree{ id: 1, name: 宋江, left: nil, right: nil, } left1 := &BinaryTree{ id: 2, name: 吴用, left: nil, right: nil, } right1 := &BinaryTree{ id: 3, name: 卢俊义, left: nil, right: nil, } right2 := &BinaryTree{ id: 4, name: 林冲, left: nil, right: nil, } root.left = left1 root.right = right1 right1.right = right2 fmt.Println(前序遍历) Preorder(root) fmt.Println(中序遍历) InfixOrder(root) fmt.Println(后序遍历) PostOrder(root)}