[go学习笔记.第十八章.数据结构] 2.约瑟夫问题,排序,栈,递归,哈希表,二叉树的三种遍历方式

news2024/9/22 11:29:00

一.约瑟夫问题

josephu 问题:

设编号为1, 2 ,... n 的n个人围坐一圈, 约定编号为 k (1<=k<=n )的人从 1 开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列

提示:

用一个不带头结点的循环链表来处理josephu问题,先构成一个有 n 个结点的单循环链表,然后由 k 结点起从1开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除,算法结束

  代码如下:

package main

import (
    "fmt"
)

//定义一个结构体
type Boy struct {
    no int //编号
    next *Boy // 指向下一个小孩的指针
}

//编写一个函数,构建一个单向环形链表
//num:表示要构建的小孩个数
//*Boy:返回环形链表的第一个小孩的指针
func AddBoy(num int) *Boy  {
    first := &Boy{} // 空指针
    curBoy := &Boy{} // 空指针: 由逻辑分析得出,要构成一个循环单向链表,需要一个辅助指针
    //判断num是否符合要求
    if num < 1 {
        fmt.Println("num不正确")
        return first
    }
    //循环构建环形链表
    for i := 1; i <= num; i++ {
        boy := &Boy{
            no: i,
        }
        //1.第一个小孩比较特殊
        if i == 1 {
            first = boy
            curBoy = boy
            curBoy.next = first // 形成一个环形链表
        } else {
            curBoy.next = boy
            curBoy = boy
            curBoy.next = first  // 形成一个环形链表
        }
    }
    return first
}

//显示单向环形链表
func ShowBoy(first *Boy)  {
    //处理环形链表为空的情况
    if first.next == nil {
        fmt.Println("链表为空")
        return
    }
    //创建一个辅助指针,帮助遍历
    curBoy := first
    for {
        fmt.Printf("小孩编号:%d\n", curBoy.no)
        //退出条件: curBoy.next == first
        if curBoy.next == first {
            break
        }
        //下一个
        curBoy = curBoy.next
    }
}

//获取小孩总的个数
func GetNum(first *Boy) (num int) {
    //处理环形链表为空的情况
    if first.next == nil {
        fmt.Println("链表为空")
        return num
    }
    //创建一个辅助指针,帮助遍历
    curBoy := first
    for {
        //退出条件: curBoy.next == first
        if curBoy.next == first {
            break
        }
        //下一个
        curBoy = curBoy.next
        num++
    }
    return num
}

/*
编号为1,2,...,n个小孩围坐在一圈,约定编号为k(1<=k<=n)的人从1开始报数,
数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,
直到所有人出列为止,由此产生一个出队编号的序列
*/
//分析:
//1.编写一个函数PlayGame(first *Boy, startNo int, countNum int)
//2.最后使用一个算法,按照要求,在环形链表中留下最后一个人
func  PlayGame(first *Boy, startNo int, countNum int)  {
    //1.空的链表处理
    if first.next == nil {
        fmt.Println("空链表")
        return
    }
    //2.判断startNo <= 小孩个数
    num := GetNum(first)
    if startNo > num {
        fmt.Println("开始小孩个数不正确")
        return
    }
    //定义一个辅助指针,帮助删除
    tail := first
    //3.让tail指向最后一个小孩,这个tail非常重要,因为在删除小孩的时候需要用到
    for {
        if tail.next == first { // 说明tail到了最后一个小孩了
            break
        }
        tail = tail.next
    }
    //3.让first移动到startNo(后面删除时,就以first为准)
    for i := 1; i <= startNo - 1; i++ {
        first = first.next
        tail = tail.next
    }
    //5.开始数countNum,然后删除first指向的小孩
    for {
        //开始数countNum -1 次
        for i := 1; i <= countNum - 1; i++ {
            first = first.next
            tail = tail.next
        }
        fmt.Printf("小孩编号为%d 出圈 -> \n", first.no)
        //删除first执行的小孩
        first = first.next
        tail.next = first
        //判断 如果 tail == first 说明只有一个小孩了
        if tail == first {
            break
        }
    }
    fmt.Printf("小孩编号为%d 最后出圈 -> \n", first.no)
}

func main() {
    first := AddBoy(5)
    ShowBoy(first)
    PlayGame(first, 2, 3)
}

二.排序

1.排序的基本介绍

排序是将一组数据,依照指定的顺序进行排列的过程,常见的排序有:

(1).冒泡排序

(2).选择排序

(3).插入排序

(4).快速排序

2.冒泡排序

见[go学习笔记.第八章.排序和查找] 1.排序,查找的基本介绍

3.选择排序

 基本介绍

        选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,经过和其他元素重整,再依原则交换位置后达到排序的目的

选择排序思想

        选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从 R[0]~R [ n - 1] 中选取最小值,与 R[0]交换,第二次从 R[1]~R[n - 1]中选取最小值,与 R[1]交换,第三次从 R[2]~R[n - 1]中选取最小值,与 R[2]交换, … ,第 i 次从 R[i - 1] ~ R [ n - 1]中选取最小值,与 R[i - 1]交换, … 第 n - 1 次从 R[n - 2]~ R[n ~ 1中选取最小值,与 R [ n - 2 ]交换,总共通过 n - 1 次,得到一个按排序码从小到大排列的有序序列

代码如下: 

package main

import(
    "fmt"
)

//编写一个函数SelectSort(arr *[5]int) 完成排序
func SelectSort(arr *[5]int)  {
    for j := 0; j < len(arr) - 1; j++ {
        //1.假设arr[0]为最大值
        max := arr[j]   //最大值
        maxIndex := j   //最大值的下标
        //2.遍历后面的1~len(arr) - 1
        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]
        }
        fmt.Printf("第%d次交换后,arr = %v\n", j + 1, *arr)
    }
}

func main()  {
    arr := [5]int{80, 12, 43, 24, 89}
    SelectSort(&arr)
    fmt.Println(arr)
}

4.插入排序

基本介绍:

        插入式排序属于内部排序法,是对干欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的

插入排序法思想:

        插入排序(Insertion Sorting)的基本思想是:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 n - 1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表

package main

import(
    "fmt"
)

//编写一个方法,实现插入排序
func insertSort(arr *[5]int) {
    for i := 1; i < len(arr); i++ { //定义要比较的次数 
        //给第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次插入后数据:%v\n", i, *arr)
    }
}

func main()  {
    arr := [5]int{11,79,45,7,90}
    fmt.Println(arr)
    insertSort(&arr)
    fmt.Println(arr)
}

5.快速排序

基本介绍

        快速排序(Quicksort)是对冒泡排序的一种改进,基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列 

案例要求:

        对[-9 , 78 , 0 , 23 ,-567 , 70]进行从小到大的排序,要求使用快速排序        

        说明:

                (1).如果取消左右递归,结果是 -9 -567 0 23 78 70

                (2).如果取消右递归,结果是  -567 -9 0 23 78 70

                (3).如果取消左递归,结果是  -9 -567 0 23 70 78

package main

import(
    "fmt"
)

//left: 表示数组左边的下标
//right: 表示数组右边的下标
//array: 表示要排序的数组
func QuickSort(left int, right int, array *[6]int) {
    l := left
    r := right
    //pivot 是中轴,支点
    pivot := array[(left + right) / 2]
    //for循环目的是将比pivot小的数放到左边,比pivot大的数放到右边
    for ;l < r; {
        //从pivot的左边找到大于等于pivot的值
        for ; array[l] < pivot; {
            l++
        }
        //从pivot的右边找到小于等于pivot的值
        for ; array[r] > pivot; {
            r--
        }
        //说明找到了: 本次分解任务完成
        if l >= r { 
            break
        }
        //交换
        array[l], array[r] = array[r], array[l]
        //优化
        if array[l] == pivot {
            l++
        }
        if array[r] == pivot {
            r--
        }
    }
    //如果l == r, 则再移动一下
    if l == r {
        l++
        r--
    }
    //向左递归
    if left < r {
        QuickSort(left, r, array)
    }
    //向右递归
    if right > l {
        QuickSort(l, right, array)
    }
}

func main() {
    arr := [6]int{-8, 66, 0, 12, -37, 80}
    fmt.Println(arr)
    QuickSort(0, len(arr) - 1, &arr)
    fmt.Println(arr)
}

三.栈

1.看一个需求

请输入一个表达式计算式: [7 * 2 * 2 - 5 +1 - 5 + 3 - 3 ]点击计算

请问:计算机底层是如何运算得到结果的?注意不是简单的把算式列出运算,因为看这个算式 7 * 2 * 2 - 5 ,但是计算机怎么理解这个算式的 (对计算机而言,它接收到的就是一个字符串

2.栈的介绍

有些程序员也把栈称为堆栈:即栈和堆栈是同一个概念

        (1).栈的英文为( stack)

        (2).栈是一个先入后出(FILO-First In Last Out)的有序列表

        (3).栈(stack)是限制线性表中元素的插入和删除,只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)

        (4).根据堆栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

3.栈的入栈和出栈示意图

4.栈的应用场景

(1).子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中

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

(3).表达式的转换与求值

(4).二叉树的遍历

(5).图形的深度优先(depth-first)搜索法

5.栈的案例

用数组模拟栈的使用,由于堆栈是一种有序列表,当然可以使用数组的结构来储存栈的数据内容,数组模拟栈的出栈,入栈等操作

package main

import(
    "fmt"
    "errors"
)

//使用数组模拟一个栈的作用 
type Stack struct {
    MaxTop int //栈最大可以存放数个数
    Top int //表示栈顶,因为栈顶固定,因此直接使用Top
    arr [5]int //数组模拟栈
}

func (this *Stack) Push(val int) (err error)  {
    //先判断栈是否满了
    if this.Top == this.MaxTop - 1 {
        fmt.Println("栈满了")
        return errors.New("栈满了")
    }
    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("栈空了")
    }
    //先取值,再top--
    val = this.arr[this.Top]
    this.Top--
    return val, nil
}

//遍历栈,注意需要从栈顶开始遍历
func (this *Stack) List()  {
    //先判断栈是否满了
    if this.Top == - 1 {
        fmt.Println("栈空了")
        return
    }
    for i := this.Top; i >= 0; i-- {
        fmt.Printf("arr[%d] = %d\n", i, this.arr[i])
    }
}

func main() {
    //初始化一个栈
    stack := &Stack {
        MaxTop: 5, //表示最多存放5个数到栈中
        Top: -1, //当栈顶为-1,表示栈为空
    }
    //入栈
    stack.Push(1)
    stack.Push(2)
    stack.Push(3)
    stack.Push(4)
    stack.Push(5)
    //遍历
    stack.List()
    //出栈
    val, _ := stack.Pop()
    fmt.Printf("出栈:%d\n", val)
    stack.List()
    //出栈
    val, _ = stack.Pop()
    fmt.Printf("出栈:%d\n", val)
    stack.List()
    //出栈
    val, _ = stack.Pop()
    fmt.Printf("出栈:%d\n", val)
    stack.List()
    //出栈
    val, _ = stack.Pop()
    fmt.Printf("出栈:%d\n", val)
    stack.List()
    //出栈
    val, _ = stack.Pop()
    fmt.Printf("出栈:%d\n", val)
    stack.List()
    //出栈
    val, _ = stack.Pop()
    if val == 0 {
        fmt.Println("栈空了")
    }
}

6.栈实现综合计算器

package main

import(
    "fmt"
    "errors"
    "strconv"
)

//使用数组模拟一个栈的作用 
type Stack struct {
    MaxTop int //栈最大可以存放数个数
    Top int //表示栈顶,因为栈顶固定,因此直接使用Top
    arr [20]int //数组模拟栈
}

func (this *Stack) Push(val int) (err error)  {
    //先判断栈是否满了
    if this.Top == this.MaxTop - 1 {
        fmt.Println("栈满了")
        return errors.New("栈满了")
    }
    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("栈空了")
    }
    //先取值,再top--
    val = this.arr[this.Top]
    this.Top--
    return val, nil
}

//遍历栈,注意需要从栈顶开始遍历
func (this *Stack) List()  {
    //先判断栈是否满了
    if this.Top == - 1 {
        fmt.Println("栈空了")
        return
    }
    for i := this.Top; i >= 0; i-- {
        fmt.Printf("arr[%d] = %d\n", i, this.arr[i])
    }
}

//判断一个字符是不是一个运算符(+,-,*,/),利用ASCII
func (this *Stack) IsOper(val int) bool {
    if val == 42 || val == 43 || val == 45 || val == 47 {
        return true
    } else {
        return false
    }
}

//运算的方法
func (this *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
}

//编写方法,返回某个运算符的优先级[程序员规定]
//*,/ ==> 1, +,- ==> 0
func (this *Stack)  Priority(oper int) int {
    res := 0
    if oper == 42 || oper == 47 {
        res = 1
    } else if oper == 43 || oper == 45 {
        res = 0
    }
    return res
}

func main() {
    //初始化栈
    //数栈
    numStack := &Stack {
        MaxTop: 20, //表示最多存放5个数到栈中
        Top: -1, //当栈顶为-1,表示栈为空
    }
    //符号栈
    operStack := &Stack {
        MaxTop: 20, //表示最多存放5个数到栈中
        Top: -1, //当栈顶为-1,表示栈为空
    }
    exp := "30+20*6-21"
    //定义一个index,帮助扫描
    index := 0
    //为了配合运算,定义需要的变量
    num1 := 0
    num2 := 0
    oper := 0
    result := 0
    keepNum := "" // 处理多位数问题: 目的是用来做拼接的
    for {
        ch := exp[index:index+1] // 字符串
        temp := int([]byte(ch)[0]) // 这个就是字符串对应的ASCII值
        if operStack.IsOper(temp) { // 说明是符号
            //如果operStack是一个空栈,直接入栈
            if operStack.Top == -1 {
                operStack.Push(temp)
            } else {
                //如果发现operStack栈顶的运算符的优先级大于等于当前准备入栈的运算符的优先级,
                //就从符号栈Pop出,并从数栈也Pop两个数,进行运算,运算后的结果再重新入栈到数栈,
                //符号再入符号栈
                if operStack.Priority(operStack.arr[operStack.Top]) >=
                 operStack.Priority(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 { // 说明是数字
            //处理多位数问题,思路:
            //1.定义一个变量keepNum string, 做拼接
            keepNum += ch
            //2.每次要向index的后面字符测试一下,看看是不是运算符,然后处理
            //如果已经到表达式的最后了,则直接把keepNum Push
            if index == len(exp) - 1 {
                val, _ := strconv.ParseInt(keepNum, 10, 64)
                numStack.Push(int(val))
            } else {
                // 向index后面测试看看是不是运算符
                if operStack.IsOper(int([]byte(exp[index + 1: index + 2])[0])) {
                    val, _ := strconv.ParseInt(keepNum, 10, 64)
                    numStack.Push(int(val))
                    keepNum = ""
                }
            }
        }
        //继续扫描
        //先判断index是否扫描到计算表达式的最后
        if index + 1 == len(exp) {
            break
        } else {
            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\n", exp, res)
}

四.递归

1.递归的一个应用场景

迷宫回溯问题

2.递归的概念

递归就是函数/方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变得简洁

3.快速入门

(1).打印问题

(2).阶乘问题

4.递归用于解决什么问题

(1).各种数学问题,如:皇后问题,汉诺塔,阶乘问题,迷宫问题,球和蓝子问题

(2).将用栈解决的问题

5.递归需要遵守的重要原则

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

(2).函数的局部变量是独立的,不会相互影响,如果希望各个函数栈使用同一个数据,使用引用传递

(3).递归必须向退出递归的条件逼近 【 程序员自己必须分析 】 ,否则就是无限递归

(4).当一个函数执行完毕,或者遇到return ,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁

6.案例展示

package main

import(
    "fmt"
)

//编写一个函数,完成找路
//myMap *[8][7]int:地图, 保证是同一个地图,故使用引用
//i, j :表示对地图的哪个点进行测试
func Setway(myMap *[8][7]int, i int, j int) bool  {
    //分析出什么情况下找到出路
    //myMap[6][5]
    if myMap[6][5] == 2 { //说明找到出路了
        return true
    } else { //说明要继续找
        if myMap[i][j] == 0 { //如果这个点是可以探测的
            //假设这个点是可以通的, 但是需要探测, 探测策略:上下左右
            myMap[i][j] = 2
            // if Setway(myMap, i - 1, j) { //上
            //  return true
            // } else if Setway(myMap, i + 1, j) { // 下
            //  return true
            // }else if Setway(myMap, i, j - 1) { //左
            //  return true
            // }else if Setway(myMap, i, j + 1) { //右
            //  return true
            // } else {
            //  myMap[i][j] = 3
            //  return false
            // }
            // 探测策略:下右上左
            if Setway(myMap, i + 1, j) { //下
                return true
            } else if Setway(myMap, i, j + 1) { //右
                return true
            }else if Setway(myMap, i - 1, j) { //上
                return true
            }else if Setway(myMap, i, j - 1) { //左
                return true
            } else {
                myMap[i][j] = 3
                return false
            }
        } else { //说明这个点不能探测, 为1,是墙
            return false
        }
    }
}
func main()  {
    //先创建一个二维数组,模拟迷宫
    //规则: 
    //1.如果元素的值为1,表示一堵墙
    //2.如果元素的值为0,表示没有走过的点
    //3.如果元素的值为2,表示一个通路
    //3.如果元素的值为3,表示走过的点,但是走不通
    var myMap [8][7]int
    //设置墙
    //先把地图的最上和最下设置为1
    for i := 0; i < 7; i++ {
        myMap[0][i] = 1
        myMap[7][i] = 1
    }//先把地图的最左和最右设置为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()
    }
    //探测
    Setway(&myMap, 1, 1)
    fmt.Println("探测完毕后的地图:")
    for i := 0; i < 8; i++ {
        for j := 0; j < 7; j++ {
            fmt.Print(myMap[i][j], " ")
        }
        fmt.Println()
    }
}

五.哈希表(散列)

1.实际需求

        有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址,...),当输入该员工的id时,要求查找到该员工的所有信息

要求:

        不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)

2.基本介绍

散列表( Hash table ,也叫哈希表),是根据关键码值(key value)而直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数,存放记录的数组叫做散列表 

3.使用hash table来实现一个雇员的管理系统[增删改查]

应用实例google公司的一个上机题

        有一个公司,当有新的员工来报道时,要求将该员工的信息加入( id ,性别,年龄,住址,...),当输入该员工的 id 时,要求查找到该员工的所有信息

要求:

        (1).不使用数据库,尽量节省内存速度越快越好=>哈希表(散列)

        (2).添加时,保证按照雇员的 id 从低到高插入

思路分析:

        (1).使用链表来实现哈希表,该链表不带表头, 即:链表的第一个结点就存放雇员信息

        (2).思路分析并画出示意图

        (3).代码实现[增删改查],显示所有员工,按照id查询

package main

import(
    "fmt"
    "os"
)

//定义emp
type Emp struct {
    Id int
    Name string
    Next *Emp
}

//方法
func (this *Emp) ShowMe() {
    fmt.Printf("链表%d对应的雇员编号%d信息为:id=%d,姓名:%v\n", this.Id % 7, this.Id, this.Id, this.Name)
}
//定义EmpLink
//EmpLink 不带表头,即第一个结点就存放雇员
type EmpLink struct {
    Head *Emp
}

//显示链表的数据信息
func (this *EmpLink) ShowLink(no int)  {
    cur := this.Head // 辅助结点
    if cur == nil {
        fmt.Printf("链表%d没有雇员\n", no)
        return
    }
    //遍历当前链表
    fmt.Printf("链表%d对应的雇员信息\n", no)
    for {
        if cur != nil {
            fmt.Printf("雇员信息:id=%d,姓名=%v  => ", cur.Id, cur.Name)
            cur = cur.Next
        } else {
            break
        }
        
    }
    fmt.Println()
}

//添加雇员的方法
//要保证添加时,编号从小到大
func (this *EmpLink) Insert(emp *Emp)  {
    cur := this.Head //辅助指针
    var pre *Emp = nil //辅助指针, 在cur前面
    //如果当前的EmpLink是一个空链表
    if cur == nil {
        this.Head = emp
        return
    }
    //如果不是一个空链表,给emp找到对应的位置并插入
    //思路: 让cur和emp比较,然后让pre保持在cur前面
    for {
        if cur != nil { 
            //比较
            if cur.Id >= emp.Id {
                //找到位置
                break
            }
            pre = cur
            cur = cur.Next
        } else {
            break
        }
    }

    //退出时,是否将emp加入到链表最后还是插入
    pre.Next = emp
    emp.Next = cur
}

//根据雇员id查找雇员
func (this *EmpLink) FindById(id int) *Emp  {
    cur := this.Head
    for {
        if cur != nil && cur.Id == id { // 找到了
            return cur
        } else if cur == nil {
            break
        }
        cur = cur.Next
    }
    return nil
}

//定义HasTable,含有一个链表数组
type HashTable struct {
    LinkArr [7]EmpLink
}

//给HaseTable编写一个ShowAll显示雇员的方法 
func (this *HashTable) ShowAll()  {
    for i := 0; i < len(this.LinkArr); i++ {
        this.LinkArr[i].ShowLink(i)
    }
}
//给HaseTable编写一个Insert雇员的方法
func (this *HashTable) Insert(emp *Emp)  {
    //使用散列函数,确定将该雇员添加到哪条链表
    linkNo := this.HashFun(emp.Id)
    //使用对应的链表添加
    this.LinkArr[linkNo].Insert(emp)
}

//给HaseTable编写一个Find雇员的方法
func (this *HashTable) FindById(id int) *Emp  {
    //使用散列函数,确定将该雇员属于哪条链表
    linkNo := this.HashFun(id)
    //使用对应的链表添加
    return this.LinkArr[linkNo].FindById(id)
}

//编写一个散列方法
func (this *HashTable) HashFun(id int) int {
    return id % 7 //得到一个值,就是对应的链表的下标
}
func main()  {
    key := ""
    id := 0
    name := ""
    var hashtable HashTable
    for {
        fmt.Println("==============雇员菜单=============")
        fmt.Println("==============input 表示添加雇员==============")
        fmt.Println("==============show 表示展示雇员==============")
        fmt.Println("==============find 表示查找雇员==============")
        fmt.Println("==============exit 表示退出==============")
        fmt.Println("请输入你的选择:")
        fmt.Scanln(&key)
        switch key {
        case "input":
            fmt.Println("请输入雇员id:")
            fmt.Scanln(&id)
            fmt.Println("请输入雇员名字:")
            fmt.Scanln(&name)
            emp := &Emp{
                Id: id,
                Name: name,
            }
            hashtable.Insert(emp)
        case "show":
            hashtable.ShowAll()
        case "find":
            fmt.Println("请输入雇员id:")
            fmt.Scanln(&id)
            emp := hashtable.FindById(id)
            if emp != nil {
                //编写一个方法,显示雇员信息
                emp.ShowMe()
            } else {
                fmt.Println("没找到雇员")
            }
        case "exit":
            os.Exit(0)
        default:
            fmt.Println("输入有误")
        }
    }
}

六.二叉树的三种遍历方式

package main

import(
    "fmt"
)

type Hero struct {
    No int
    Name string
    Left *Hero
    Right *Hero
}

//前序遍历: 先输出root根节点,然后输出左子树,然后输出右子树
func PreOrder(node *Hero)  {
    if node != nil {
        fmt.Printf("no=%d,name=%v\n", node.No, node.Name)
        PreOrder(node.Left)
        PreOrder(node.Right)
    }
}

//中序遍历: 先输出root的左子树,然后输出右子树,然后输出root根节点
func InfixOrder(node *Hero)  {
    if node != nil {
        InfixOrder(node.Left)
        fmt.Printf("no=%d,name=%v\n", node.No, node.Name)
        InfixOrder(node.Right)
    }
}

//后序遍历: 先输出root的右子树,然后输出root根节点,然后输出左子树
func PostOrder(node *Hero)  {
    if node != nil {
        InfixOrder(node.Left)
        InfixOrder(node.Right)
        fmt.Printf("no=%d,name=%v\n", node.No, node.Name)
    }
}

func main(){
    //构建二叉树
    root := &Hero {
        No: 1,
        Name: "及时雨",
    }
    left1 := &Hero {
        No: 2,
        Name: "智多星",
    }
    right1 := &Hero {
        No: 3,
        Name: "玉麒麟",
    }
    root.Left = left1
    root.Right = right1

    right2 := &Hero {
        No: 4,
        Name: "豹子头",
    }
    right1.Right = right2

    PreOrder(root)
    InfixOrder(root)
}

[上一节][go学习笔记.第十八章.数据结构] 1.基本介绍,稀疏数组,队列(数组实现),链表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/132744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ActivityManagerService

1 AMS 家族 ActivityManagerService&#xff08;AMS&#xff09;主要负责系统中四大组件的启动、切换、调度以及应用程序的管理和调度工作&#xff0c;其职责与操作系统中的进程管理和调度模块类似。ActivityManagerService 进行初始化的时机很明确&#xff0c;就是在 system_…

计算机组成原理“上分秘籍”——数据的表示和运算

前言 上分地图 目录 前言 上分地图 猜你想问 Q1&#xff1a;为何要研究数据表示问题&#xff1f; Q2&#xff1a;什么叫数据表示&#xff1f;计算机中又有哪些方法&#xff1f; 正文 上分突破口1&#xff1a;进位计数法 例&#xff1a;二进制转为十进制 例&#xff…

路由综合实验

目录需求分析解决方法1&#xff0c;环回配置2&#xff0c;路由接口ip配置3&#xff0c;配置DHCP服务4&#xff0c;配置缺省5&#xff0c;静态路由配置6.浮动路由配置7&#xff0c;nat的配置8&#xff0c;远程服务及端口进行映射需求分析 一&#xff0c;原始需求如图&#xff1…

基于React Native开发的非法App破解记录

目标app YUhSMGNITTZMeTloWm1ZdWJIVnNkWE5wY2k1dFpTOD0 使用jadx反编译&#xff0c;找了一圈没有找到相应代码&#xff0c;看AndroidManifest.xml也不像有加壳的样子。。。 在lib目录下找到libreactnativejni.so文件&#xff0c;看似和react相关&#xff0c;莫非这app是大前端…

C++ Primer Plus 第六版(中文版)第五、六章(重置版)编程练习答案

//本博主所写的代码仅为阅读者提供参考&#xff1b; //若有不足之处请提出&#xff0c;博主会尽所能修改&#xff1b; //附上课后编程练习题目&#xff1b; //若是对您有用的话请点赞或分享提供给它人&#xff1b; //-----------------------------------------------------…

JavaScript奇淫技巧:反调试

JavaScript奇淫技巧&#xff1a;反调试 本文&#xff0c;将分享几种JS代码反调试技巧&#xff0c;目标是&#xff1a;实现防止他人调试、动态分析自己的代码。 检测调试&#xff0c;方法一&#xff1a;用console.log检测 代码&#xff1a; var c new RegExp ("1"…

Cesium 定位到图层(ImageryLayer)报错 DeveloperError: normalized result is not a number

Cesium 定位到图层&#xff08;ImageryLayer&#xff09;报错 DeveloperError: normalized result is not a number错误原因调试定位问题过程问题解决总结在使用 Cesium 封装代码的时候&#xff0c;遇到个奇怪的问题。 使用 viewer.flyTo(ImageryLayer) 报错&#xff1a;Devel…

【2022年度悲报】-回望2022,展望2023

文章目录一、前言-想对大家说的话二、有感而谈2022年-新年开端&#xff08;同销万古愁&#xff09;2022年-前中期&#xff08;再进再困&#xff0c;再熬再奋&#xff09;2022年-年后半段&#xff08;玉骨那愁瘴雾&#xff0c;冰姿自有仙风&#xff09;2022年-年末尾声&#xff…

简单总结:Flink和Kafka是如何做到精准一致性的

Flink CheckPoint机制 CheckPoint本质上就是检查点&#xff0c;类似于玩游戏的时候&#xff0c;需要偶尔存档&#xff0c;怕家里断电导致自己白玩。 Flink也是一样的&#xff0c;机器也是可能宕机&#xff0c;那么Flink如何保证自身不受宕机影响呢&#xff1f; 一般来说&am…

python小案例——采集财经数据

前言 大家早好、午好、晚好吖 ❤ ~ 另我给大家准备了一些资料&#xff0c;包括: 2022最新Python视频教程、Python电子书10个G &#xff08;涵盖基础、爬虫、数据分析、web开发、机器学习、人工智能、面试题&#xff09;、Python学习路线图等等 全部可在文末名片获取哦&…

MATLAB算法实战应用案例精讲-【人工智能】语义分割(补充篇)(附实战应用案例及代码)

前言 语义分割作为计算机视觉领域的关键任务,是实现完整场景理解的必经之路。为了让机器拥有视觉,要经过图像分类、物体检测再到图像分割的过程。其中,图像分割的技术难度最高。 越来越多的应用得益于图像分类分割技术,全场景理解在计算机视觉领域也至关重要。其中一些应…

强大的ANTLR4(3)--算术表达式

下面要构建一个简单的计算器&#xff0c;规则如下&#xff1a; 1&#xff09;可以由一系列语句构成&#xff0c;每条语句由换行符终止 2&#xff09;一条语句可以是表达式、赋值语句或空行 3&#xff09;可以有加减乘除、小括号以及变量出现 例如&#xff0c;文件名t.expr的内…

【Java】PriorityQueue梳理

【Java】PriorityQueue梳理 简介 PriorityQueue是优先队列的意思。优先队列的作用是能保证每次取出的元素都是队列中权值最小的。这里牵涉到了大小关系&#xff0c;元素大小的评判可以通过元素本身的自然顺序&#xff08;natural ordering&#xff09;&#xff0c;也可以通过…

linux的例行性工作

一&#xff0c;单一执行的例行性工作 定时任务&#xff0c;将来的某个时间点执行 使用单一理性工作的命令&#xff1a;at -> atd 命令 服务名 查看atd状态&#xff0c;看有没有这个服务&#xff0c;查看结果为有 我们使用 at 命令来生成所要运行的工作&#xff0c;并将…

Taro+nutui h5使用nut-signature 签名组件的采坑记录

近期在使用Taro&#xff08;“tarojs/taro”: “3.4.0-beta.0”&#xff09; Nutui &#xff08;3.1.16&#xff09;开发H5时,需要一个签名功能结果在小程序上运行正常的 nut-signature组件,在h5上出问题了 首先问题是 : Nutui的 签名组件&#xff08;nut-signature&#xff…

加解密与HTTPS(3)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 除了对称加密算法和非对称加密算法&#xff0c;再就是最后的一种加密算法了&#xff1a;不可逆加密算法。 对称加密算法和非对称加密算法在处理明文的过程中需要…

线程池ThreadPoolExecutor的源码中是如何解决并发问题的?

ThreadPoolExecutor面临哪些线程安全问题 ThreadPoolExecutor俗称线程池&#xff0c;作为java.util.concurrent包对外提供基础实现&#xff0c;以内部线程池的形式对外提供管理任务执行&#xff0c;线程调度&#xff0c;线程池管理等等服务。 然而为高效并发而生ThreadPoolExe…

C++项目实战:职工管理系统

1.管理系统的要求 系统可以管理公司内部所有员工的信息 主要使用c实现一个基于多态的职工管理系统 公司中的职工分为三类&#xff1a;普通员工、经理、老板&#xff0c;显示信息时需要显示职工编号、职工姓名、职工岗位以及职责 普通员工职责&#xff1a;完成经理安排的各项任…

oh my 毕设-人体姿态估计综述

文章目录What is Human Pose Estimation?Classical vs. Deep Learning-based approachesClassical approaches to 2D Human Pose EstimationDeep Learning-based approaches to 2D Human Pose EstimationHuman Pose Estimation using Deep Neural NetworksOpenPoseAlphaPose (…

想要努力赚钱,培养四种基础能力

这四种基础能力分别是&#xff1a;认知力、学习力、执行力、复盘力。我们的认知和思维&#xff0c;很大程度上&#xff0c;都是由所处的环境和圈子决定的。在同一个环境和圈子里面呆久了&#xff0c;你的认知就会被固化了。穷人最根本的枷锁&#xff0c;不是缺乏资金&#xff0…