双向链表
单向链表的缺点
单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找
单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除。
案例
package main
import "fmt"
type LinkNode struct {
no int
name string
nickname string
pre *LinkNode //表示指向前一个结点
next *LinkNode
}
// 给双链表插入一个节点
// 编写第一种插入方法,在单链表的最后加入
func InsertHeroNode(head *LinkNode, newHeroNode *LinkNode) {
// 1.先找到该链表的最后这个节点
// 2.创建一个辅助节点
temp := head
for {
if temp.next == nil {
break
}
temp = temp.next
}
temp.next = newHeroNode
newHeroNode.pre = temp
}
func InsertHeroNode2(head *LinkNode, newHeroNode *LinkNode) {
temp := head
flag := true
for {
if temp.next == nil {
break
} else if temp.next.no > newHeroNode.no {
break
} else if temp.next.no == newHeroNode.no {
flag = false
break
}
temp = temp.next
}
if !flag {
fmt.Println("对不起,已经存在no=", newHeroNode.no)
return
} else {
newHeroNode.next = temp.next
temp.next = newHeroNode
}
}
// 仍然使用单向的节点
func ListHeroNode(head *LinkNode) {
temp := head
if temp.next == nil {
fmt.Println("空空如也。。。")
return
}
for {
fmt.Printf("[%d , %s, %s]==>", temp.next.no, temp.next.name, temp.next.nickname)
temp = temp.next
if temp.next == nil {
break
}
}
fmt.Println()
}
// 仍然使用单向的节点
func ListHeroNode2(head *LinkNode) {
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.no, temp.name, temp.nickname)
// 判断是否链表头
temp = temp.pre
if temp.pre == nil {
break
}
}
}
// 双向链表删除节点
func DelHerNode(head *LinkNode, id int) {
temp := head
flag := false
// 找到要删除的节点
for {
if temp.next == nil {
break
} else if temp.next.no == 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("要删除的结点不存在")
}
}
func main() {
// 创建一个头结点
head := &LinkNode{}
hero1 := &LinkNode{
no: 1,
name: "宋江",
nickname: "及时雨",
}
hero2 := &LinkNode{
no: 2,
name: "卢俊义",
nickname: "玉麒麟",
}
hero3 := &LinkNode{
no: 3,
name: "林冲",
nickname: "豹子头",
}
InsertHeroNode(head, hero1)
InsertHeroNode(head, hero2)
InsertHeroNode(head, hero3)
ListHeroNode(head)
fmt.Println("逆序打印")
ListHeroNode2(head)
DelHerNode(head, 3)
ListHeroNode(head)
fmt.Println("逆序打印")
ListHeroNode2(head)
}
环形单向链表
package main
import (
"fmt"
)
type CatNode struct {
no int
name string
next *CatNode
}
func InsertCatNode(head *CatNode, newCatNode *CatNode) {
if head.next == nil {
head.no = newCatNode.no
head.name = newCatNode.name
head.next = head // 构成一直环形
fmt.Println(newCatNode, "加入到环形的链表")
return
}
temp := head
for {
if temp.next == head {
break
}
temp = temp.next
}
temp.next = newCatNode
newCatNode.next = head
}
func ListCircleLink(head *CatNode) {
// 环形列表的情况如下
temp := head
if temp.next == nil {
fmt.Println("环形链表是空的。。。。")
return
}
for {
fmt.Printf("猫的信息为=[id = %v name = %v]->\n", temp.no, temp.name)
if temp.next == head {
break
}
temp = temp.next
}
}
// 删除一个节点
func DelCat(head *CatNode, id int) *CatNode {
temp := head
helper := head
if temp.next == nil {
fmt.Println("这是一个空的环形链表,不能删除")
return head
}
if temp.next == head {
temp.next = nil
return head
}
for {
if helper.next == head {
break
}
helper = helper.next
}
flag := true
for {
if temp.next == head {
break
}
if temp.no == id {
if temp == head {
head = head.next
}
helper.next = temp.next
fmt.Println("猫=", id)
flag = false
}
temp = temp.next
helper = helper.next // 一旦找到要删除的节点,help
}
if flag {
if temp.no == id {
helper.next = temp.next
fmt.Println("猫=", id)
} else {
fmt.Println("没有", id)
}
}
return head
}
func main() {
head := &CatNode{}
cat1 := &CatNode{
no: 1,
name: "tom",
}
cat2 := &CatNode{
no: 2,
name: "tom2",
}
cat3 := &CatNode{
no: 3,
name: "tom3",
}
InsertCatNode(head, cat1)
InsertCatNode(head, cat2)
InsertCatNode(head, cat3)
ListCircleLink(head)
head = DelCat(head, 3)
ListCircleLink(head)
}
约瑟夫问题(Josephu)
设编号为1、2、3.... n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列
package main
import "fmt"
type Boy struct {
No int
Next *Boy // 指向下一个结构体
}
// 编写函数,构成单向环形链表
// num : 表示小孩的个数
// *Boy : 返回该环形的链表的第一个小孩的指针
func AddBoy(num int) *Boy {
frist := &Boy{}
curBoy := &Boy{}
if num < 1 {
fmt.Println("num的值不对")
return frist
}
for i := 1; i <= num; i++ {
boy := &Boy{
No: i,
}
// 分析构成环形链表,需要一个辅助指针
if i == 1 {
frist = boy
curBoy = boy
curBoy.Next = frist
} else {
curBoy.Next = boy
curBoy = boy
curBoy.Next = frist
}
}
return frist
}
// 显示链表
func showBoy(frist *Boy) {
// 处理一下如果环形链表为空
if frist.Next == nil {
fmt.Println("链表为空,没有小孩")
return
}
// 创建指针帮助遍历
curBoy := frist
for {
fmt.Printf("小孩的编号=%v\n", curBoy.No)
if curBoy.Next == frist {
break
}
curBoy = curBoy.Next
}
}
func PlayGame(frist *Boy, startNo int, countNum int) {
//空链表单独处理
if frist.Next == nil {
fmt.Println("空的链表,没有小孩")
return
}
// 需要定义辅助指针,帮助我们删除小孩
tail := frist
// 让tail指向环形链表的最后一个小孩
for {
if tail.Next == frist {
break
}
tail = tail.Next
}
// 让frist 移动到starNo
for i := 0; i < startNo-1; i++ {
frist = frist.Next
tail = tail.Next
}
// 开始数countNum,然后就删除frist 指向的小孩
for {
for i := 1; i <= countNum-1; i++ {
frist = frist.Next
tail = tail.Next
}
fmt.Printf("小孩的编号为%d 出圈", frist.No)
fmt.Println()
frist = frist.Next
tail.Next = frist
// 判断如果 tail == frist,圈子中只有一个小孩
if tail == frist {
break
}
}
fmt.Printf("小孩编号为%d 出圈", frist.No)
}
func main() {
frist := AddBoy(5)
showBoy(frist)
PlayGame(frist, 2, 3)
}
排序
选择排序
package main
import "fmt"
func SelectSort(arr *[5]int) {
for i := 0; i < len(arr)-1; i++ {
max := arr[i]
maxIndex := i
for j := i; j < len(arr); j++ {
if max < arr[j] {
max = arr[j]
maxIndex = j
}
}
if maxIndex != i {
arr[i], arr[maxIndex] = arr[maxIndex], arr[i]
}
}
}
func main() {
arr := [5]int{10, 34, 19, 100, 80}
fmt.Println(arr)
SelectSort(&arr)
fmt.Println(arr)
}
插入排序
插入排序属于内部排序发,是对与排序的元素已插入的方式找寻该元素的适当位置,以达到排序的目的
package main
import "fmt"
func InsertSort(arr *[5]int) {
for i := 1; i < len(arr); i++ {
inserVal := arr[i]
insertIndex := i - 1
for insertIndex >= 0 && arr[insertIndex] < inserVal {
arr[insertIndex+1] = arr[insertIndex]
insertIndex--
}
if insertIndex+1 != i {
arr[insertIndex+1] = inserVal
}
fmt.Println(*arr)
}
}
func main() {
arr := [5]int{23, 0, 12, 56, 34}
fmt.Println(arr)
InsertSort(&arr)
fmt.Println("main 函数")
fmt.Println(arr)
}
快速排序(Quicksort)
快速排序是对冒泡排序的一种改进,基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分是所有数据都比里一部分的所有数据都要小,然后再按此方法对这两部分数据分表进行快速排序,整个排序过程可以递归进行。
package main
import "fmt"
func QuickSort(left int, right int, array *[6]int) {
l := left
r := right
// pivot 是中轴
pivot := array[(left+right)/2]
temp := 0
for l < r {
for array[l] < pivot {
l++
}
for array[r] > pivot {
r--
}
if l >= r {
break
}
temp = array[l]
array[l] = array[r]
array[r] = temp
if array[l] == pivot {
r--
}
if array[r] == pivot {
l++
}
}
if l == r {
l++
r--
}
if left < r {
QuickSort(left, r, array)
}
if right > l {
QuickSort(l, right, array)
}
}
func main() {
arr := [6]int{-9, 78, 0, 23, -567, 70}
fmt.Println(arr)
QuickSort(0, len(arr)-1, &arr)
fmt.Println(arr)
}
栈(Stack)
先入后出(FILO)有序列表
应用场景
子程序的调用:再跳往子程序前,会先将下个指令的地址存到栈中,知道子程序执行完后再将地址去除,已回到原来的程序中。
处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量的数据存入栈中
表达式的转换与求职
二叉树的遍历
图形的深度优先(depth->frist)搜索法
数组模拟栈
package main
import (
"errors"
"fmt"
)
type Stack struct {
MaxTop int
Top int
arr [5]int
}
func (st *Stack) Pop() (val int, err error) {
if st.Top == -1 {
fmt.Println("stack empty!")
return 0, errors.New("stack empty")
}
val = st.arr[st.Top]
st.Top--
return val, nil
}
func (st *Stack) Push(val int) (err error) {
// 先判断栈是否已经满了
if st.Top == st.MaxTop-1 {
fmt.Println("stack full")
return errors.New("stack full")
}
st.Top++
st.arr[st.Top] = val
return
}
func (st *Stack) List() {
// 先判断栈是否为空
if st.Top == -1 {
fmt.Println("stack empty")
return
}
for i := st.Top; i >= 0; i-- {
fmt.Printf("arr[%d]=%d\n", i, st.arr[i])
}
}
func main() {
Stack := &Stack{
MaxTop: 5,
Top: -1, // 当栈顶为-1,表示栈空
}
Stack.Push(1)
Stack.Push(2)
Stack.Push(3)
Stack.Push(4)
Stack.Push(5)
Stack.List()
Stack.Pop()
Stack.Pop()
Stack.Pop()
fmt.Println("出栈后")
Stack.List()
}
栈的计算表达式
没有考虑括号以及多位数
package main
import (
"errors"
"fmt"
"strconv"
)
type Stack struct {
MaxTop int
Top int
arr [20]int
}
func (st *Stack) Pop() (val int, err error) {
if st.Top == -1 {
fmt.Println("stack empty!")
return 0, errors.New("stack empty")
}
val = st.arr[st.Top]
st.Top--
return val, nil
}
func (st *Stack) Push(val int) (err error) {
// 先判断栈是否已经满了
if st.Top == st.MaxTop-1 {
fmt.Println("stack full")
return errors.New("stack full")
}
st.Top++
st.arr[st.Top] = val
return
}
func (st *Stack) List() {
// 先判断栈是否为空
if st.Top == -1 {
fmt.Println("stack empty")
return
}
for i := st.Top; i >= 0; i-- {
fmt.Printf("arr[%d]=%d\n", i, st.arr[i])
}
}
func (st *Stack) IsOper(val int) bool {
if val == 42 || val == 43 || val == 45 || val == 47 {
return true
} else {
return false
}
}
func (st *Stack) Cal(num1 int, num2 int, oper int) int {
res := 0
switch oper {
case 42:
res = num2 * num1
case 43:
res = num2 + num1
case 45:
res = num2 - num1
case 47:
res = num2 / num1
default:
fmt.Println("运算符错误")
}
return res
}
// 编写一个方法,返回某个运算符的优先级
func (st *Stack) Priorty(oper int) int {
res := 0
if oper == 42 || oper == 47 {
return 1
} else if oper == 43 || oper == 45 {
return 0
}
return res
}
func main() {
numStack := &Stack{
MaxTop: 20,
Top: -1,
}
operStack := &Stack{
MaxTop: 20,
Top: -1,
}
exp := "3+2*6-2"
index := 0
num1, num2 := 0, 0
oper := 0
result := 0
keepNum := ""
for {
ch := exp[index : index+1]
temp := int([]byte(ch)[0]) // 字符对应的ASCII值
if operStack.IsOper(temp) {
if operStack.Top == -1 {
operStack.Push(temp)
} else {
if operStack.Priorty(operStack.arr[operStack.Top]) >= operStack.Priorty(temp) {
num1, _ = numStack.Pop()
num2, _ = numStack.Pop()
oper, _ = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算的结果重新入栈
numStack.Push(result)
operStack.Push(temp)
} else {
operStack.Push(temp)
}
}
} else {
// 处理多位数
keepNum += ch
if index == len(exp)-1 {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
} else {
if operStack.IsOper(int([]byte(exp[index+1 : index+2])[0])) {
val, _ := strconv.ParseInt(keepNum, 10, 64)
numStack.Push(int(val))
keepNum = ""
}
}
// val, _ := strconv.ParseInt(ch, 10, 64)
// numStack.Push(int(val))
}
// 先判断index是否已经扫描搭配计算表达式的最后
if index+1 == len(exp) {
break
}
index++
}
for {
if operStack.Top == -1 {
break
}
num1, _ = numStack.Pop()
num2, _ = numStack.Pop()
oper, _ = operStack.Pop()
result = operStack.Cal(num1, num2, oper)
// 将计算的结果重新入栈
numStack.Push(result)
}
res, _ := numStack.Pop()
fmt.Printf("表达式%s = %v", exp, res)
}
递归
原则:
执行一个函数时,就创建一个新的受保护的独立空间
函数的局部变量是独立的,不会相互影响
递归必须向退出递归的条件逼近,否则就是无限递归
当一个函数执行完毕,或者遇到return,就会返回,遵守设调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁
迷宫回溯
package main
import "fmt"
func SetMap(myMap *[8][7]int, i int, j int) bool {
if myMap[6][5] == 2 {
return true
} else {
if myMap[i][j] == 0 {
myMap[i][j] = 2
if SetMap(myMap, i+1, j) {
return true
} else if SetMap(myMap, i, j+1) {
return true
} else if SetMap(myMap, i-1, j) {
return true
} else if SetMap(myMap, i, j-1) {
return true
} else {
myMap[i][j] = 3
return false
}
} else {
return false
}
}
}
func main() {
// 二维数组模拟迷宫
// 规则
// 如果元素的值为1,就是墙
// 如果元素的值为0,就是未走过的路
// 如果元素的值为2,是一个通路
// 如果元素的值为3,时走过的点,但是走不通
var myMap [8][7]int
for i := 0; i < 7; i++ {
myMap[0][i] = 1
myMap[7][i] = 1
}
for i := 0; i < 8; i++ {
myMap[i][0] = 1
myMap[i][6] = 1
}
myMap[3][1] = 1
myMap[3][2] = 1
for i := 0; i < 8; i++ {
for j := 0; j < 7; j++ {
fmt.Print(myMap[i][j], " ")
}
fmt.Println()
}
SetMap(&myMap, 1, 1)
fmt.Println("探测完毕...")
for i := 0; i < 8; i++ {
for j := 0; j < 7; j++ {
fmt.Print(myMap[i][j], " ")
}
fmt.Println()
}
}