Golang学习 Day_18 数据结构02

双向链表

单向链表的缺点

  1. 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找

  1. 单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除。

案例

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)有序列表

应用场景

  1. 子程序的调用:再跳往子程序前,会先将下个指令的地址存到栈中,知道子程序执行完后再将地址去除,已回到原来的程序中。

  1. 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量的数据存入栈中

  1. 表达式的转换与求职

  1. 二叉树的遍历

  1. 图形的深度优先(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)
}

递归

原则:

  1. 执行一个函数时,就创建一个新的受保护的独立空间

  1. 函数的局部变量是独立的,不会相互影响

  1. 递归必须向退出递归的条件逼近,否则就是无限递归

  1. 当一个函数执行完毕,或者遇到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()
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值