粗浅理解
- 数据结构: 摆放数据的构造。
- 算法: 处理数据的方法。
栈
实现
// 栈结构
type Stack struct {
Capacity int // 容量
Top int // 顶元素索引,初始值设为 -1。
arr [5]int
}
// 压入栈
func (that *Stack) Push(val int) error {
if that.Top == that.Capacity-1 {
return errors.New("栈 满了")
}
that.Top++
that.arr[that.Top] = val
return nil
}
// 弹出栈
func (that *Stack) Pop() (val int, err error) {
if that.Top == -1 {
return 0, errors.New("栈 空了")
}
val = that.arr[that.Top]
that.Top--
return val, nil
}
// 从 栈顶-> 栈底 遍历栈
func (that Stack) List() {
if that.Top == -1 {
fmt.Println("空栈")
return
}
for i := that.Top; i >= 0; i-- {
fmt.Printf("arr[%d]=%d \n", i, that.arr[i])
}
}
应用
- 计算器
package main
import (
"errors"
"fmt"
"strconv"
)
type Stack struct {
Capacity int //容量
Top int
arr [20]int
}
func (that *Stack) Push(val int) error {
if that.Top == that.Capacity-1 {
return errors.New("栈 满了")
}
that.Top++
that.arr[that.Top] = val
return nil
}
func (that *Stack) Pop() (val int, err error) {
if that.Top == -1 {
return 0, errors.New("栈 空了")
}
val = that.arr[that.Top]
that.Top--
return val, nil
}
func (that Stack) List() {
if that.Top == -1 {
fmt.Println("空栈")
return
}
for i := that.Top; i >= 0; i-- {
fmt.Printf("arr[%d]=%d \n", i, that.arr[i])
}
}
func main() {
// 栈 实现 综合计算器: 10 以内的 加减乘除
// 数 栈
numStack := &Stack{
Capacity: 20,
Top: -1,
}
// 符号 栈
operStack := &Stack{
Capacity: 20,
Top: -1,
}
exp := ""
fmt.Println("请输入10以内的加减乘除:")
fmt.Scanln(&exp)
var num1, num2, oper, result int = 0, 0, 0, 0
// 将 字符 分别 入栈
for _, v := range exp {
// 判断是 数子 还是 运算符号
if isOper(int(v)) {
if operStack.Top == -1 {
// 如果 符号栈 为空,直接入栈
operStack.Push(int(v))
} else {
//如果栈顶 符号 的 优先级 大于 要入栈 的,
// 数栈 弹出两个数,符号栈 弹出一个 ,进行运算, 运算的结果 入展到 数栈,在把要入符号栈的 元素 放入
if Priority(operStack.arr[operStack.Top]) > Priority(int(v)) {
num1, _ = numStack.Pop()
num2, _ = numStack.Pop()
oper, _ = operStack.Pop()
result = cal(num1, num2, oper)
numStack.Push(result)
operStack.Push(int(v))
} else {
operStack.Push(int(v))
}
}
} else {
res := byteToInt(v)
numStack.Push(res) // 放入的是 数字,而不是数字对应的 字节。
}
}
// 入完栈之后 进行 运算
for {
if operStack.Top == -1 {
break
}
num1, _ = numStack.Pop()
num2, _ = numStack.Pop()
oper, _ = operStack.Pop()
result = cal(num1, num2, oper)
numStack.Push(result)
}
a, _ := numStack.Pop()
fmt.Println(exp, "最后的结果是:", a)
}
// 判断一个字符 是不是一个 运算符(通过 ASCII)
func isOper(val int) bool {
if val == 42 || val == 43 || val == 45 || val == 47 {
return true
} else {
return false
}
}
// 运算的方法
func cal(num1, num2, 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 Priority(oper int) int {
res := 0
if oper == 42 || oper == 47 {
res = 1
} else if oper == 43 || oper == 45 {
res = 0
}
return res
}
// 字节 转 int 数字
func byteToInt(a rune) int {
s := string(a)
res, _ := strconv.ParseInt(s, 10, 10)
return int(res)
}
队列
实现
// 使用一个结构体 管理 环形队列
type CircleQueue struct {
Capacity int // 容量
head int //指向 队头 元素,初始值设为0
tail int //指向 队尾 元素 的 后一位,初始值设为0
array [5]int
}
// 是否 满员 (重点)
func (that CircleQueue) IsFull() bool {
/*
每次插入数据之前都要 先看一下 插入的这个 数据位 的 后一位 是否为 头元素位,
若是,就定义为满员,即使tail指向的该位一直空着,也不允许插入,
所以 队列的 实际容量 比数组的长度 少一位,因为总有一个位置始终是空着的。
*/
return (that.tail+1)%that.Capacity == that.head
}
// 是否 为空
func (that CircleQueue) IsEmpty() bool {
// 若相等,则说明:对头 和 队尾 之间 没有成员
return that.tail == that.head
}
// 入队 (入队的时候 tail负责记录)
func (that *CircleQueue) Push(val int) (err error) {
if that.IsFull() {
fmt.Printf("%v 装不下了\n", val)
return errors.New("full")
}
that.array[that.tail] = val
// 数组 的 最后一位 空出来(始终不会装载元素),当作了标志位
that.tail = (that.tail + 1) % that.Capacity //取模的妙用: 给定范围 让数值圈内打转
return nil
}
// 出队 (出队的时候 head负责记录)
func (that *CircleQueue) Pop() (int, error) {
if that.IsEmpty() {
return -1, errors.New("完犊子,啥也没有了")
}
val := that.array[that.head]
that.head = (that.head + 1) % that.Capacity
return val, nil
}
// 元素个数(重点)
func (that CircleQueue) Size() int {
return (that.tail + that.Capacity - that.head) % that.Capacity
}
// 显示 队列
func (that CircleQueue) ShowQueue() {
length := that.Size()
if length == 0 {
fmt.Println("空队列")
} else {
var indexOfHead int = that.head
for i := 0; i < length; i++ {
fmt.Printf("arr[%d]=%d \t", i, that.array[indexOfHead])
indexOfHead = (indexOfHead + 1) % that.Capacity
}
fmt.Println()
}
}
应用
- channel
链表
单向链表
// 链表 节点
type node struct {
name string // 存储数据的地方
next *node // 指向下一个节点
}
// 链尾 加 节点
func InsertNewNode(head *node, newNode *node) {
var temp = head
for {
if temp.next == nil {
break
}
// 移动到下一个节点
temp = temp.next
}
temp.next = newNode
}
func showAllNode(head *node) {
if head.next == nil {
fmt.Println("空链表")
} else {
var i = 1
var temp = head.next
for {
fmt.Printf("第%d个节点的值是 %v \n", i, temp.name)
if temp.next == nil {
break
}
temp = temp.next
i++
}
}
}
// 根据name 删除节点
func deleteNode(head *node, name string) {
var temp = head
for {
if temp.next.name == name {
// 此时 temp 是 要删除节点 的 上一个节点
break
}
if temp.next == nil {
fmt.Println("整条链都找完了,无此节点")
return
}
temp = temp.next
}
// 判断删除的 是否为 最后一个节点
if temp.next.next == nil {
temp.next = nil
} else {
temp.next = temp.next.next
}
fmt.Println("删除成功")
}
双向链表
// 链表 节点
type node struct {
name string
pre *node
next *node
}
func InsertNewNode(head, tail *node, newNode *node) {
var temp = head
for {
if temp.next == tail {
break
}
temp = temp.next
}
// 先链接
newNode.pre = temp
newNode.next = tail
// 再断开
temp.next = newNode
tail.pre = newNode
}
func GetLength(head, tail *node) int {
var temp = head
var i = 0
for {
if temp.next == tail {
//此时 temp为 tail 的前一个 节点
return i
}
i++
temp = temp.next
}
}
func showAllNode(head, tail *node) {
if head.next == tail {
fmt.Println("空链表")
} else {
var i = 1
var temp = head.next
for {
fmt.Printf("第%d个节点的值是 %v \n", i, temp.name)
if temp.next == tail {
break
}
temp = temp.next
i++
}
}
}
func deleteNode(head, tail *node, name string) {
var temp = head
fmt.Println(temp.next)
for {
if temp.name == name { // 在前
// 此时 temp 是 要删除节点
break
}
if temp.next == tail { // 在后
fmt.Println("无此节点")
return
}
temp = temp.next
}
temp.pre.next = temp.next
temp.next.pre = temp.pre
fmt.Println("删除成功")
}
排序
选择排序: 8万个数字排序 用时:12秒
插入排序: 8万个数字排序 用时:5秒
快速排序: 8百万个数字排序 用时:3秒
选择排序
// 从大到小
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]
}
fmt.Printf("第%d次比较之后的结果是:%v \n", j, arr)
}
}
- 说明:
假设 第一个元素为 最大值,然后 遍历其后面元素,若身后有大约第一个元素的,就让 大元素当 最大值,等到全部遍历完身后的元素,真正的最大值就找出来了,然后将真正的最大值 和 一开始 假设的最大值交换位置。
插入排序
// 从大到小
func InserSort(arr *[5]int) {
for i := 1; i < len(arr); i++ {
// 从第二个元素 开始和 前面的元素 进行 比较,索引 i=1开始
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)
}
}
- 说明:
从第二位元素开始,依次作为被比较元素insertVal, 分别 和 其前面的元素比较,如果前面的元素 小于 被比较的元素,则前面的元素后移一位,如果前面的元素大于 被比较元素,则被比较元素就放在大元素的邻后位就可以了。
快速排序
// 从小到大
// left: 数组左边的下标
// right:数组右边的下标
func QuickSort(left, right int, arr *[6]int) {
l := left
r := right
// 中轴
pivot := arr[(left+right)/2]
// for循环的作用是: 将 比 pivot 小的 放到 左边, 大的放到右边。
for l < r {
// 从 pivot 左侧找到一个 比 pivot 大的 元素的 索引 l
for arr[l] < pivot {
l++
}
// 从 pivot 左侧找到一个 比 pivot 小的 元素的 索引 l
for arr[r] > pivot {
r--
}
// l >= r 表明 此次 两边元素 交换的 任务 完成了
if l >= r {
break
}
// 交换 两个元素(一个比pivot大 和 一个比pivot小) 的位置
arr[l], arr[r] = arr[r], arr[l]
fmt.Println("交换之后的结果:", arr)
// 相等的情况下不必交换
if arr[l] == pivot {
r--
}
// 相等的情况下不必交换
if arr[r] == pivot {
l++
}
}
// 如果 l==r , 在移动下, 如果没有该句,可能会 死循环
if l == r {
l++
r--
}
// 向左递归
if left < r {
QuickSort(left, r, arr)
}
// 向右递归
if right > l {
QuickSort(l, right, arr)
}
}
- 说明:
哈希表
package main
import (
"fmt"
)
/*
** 1.定义员工Emp结构体(数据节点)
*/
type Emp struct {
Id int
Name string
Next *Emp
}
// Emp的方法
func (that *Emp) ShowMe() {
fmt.Printf("在链表%d中,找到id=%d的员工\n", that.Id%7, that.Id)
}
/*
** 2.定义EmpLink结构体(数据链)
*/
type EmpLink struct {
Head *Emp //不要表头,即第一个结点就存放员工
}
//2.1添加员工的方法, 重点: 保证添加时,编号 从小到大
func (that *EmpLink) EmpLinkInsert(emp *Emp) {
cur := that.Head // 这是辅助指针
var pre *Emp = nil // 让 pre 始终在cur 的 邻前
//如果当前的EmpLink就是一个空链表
if that.Head == nil {
that.Head = emp //员工插入完成
return
}
//如果不是一个空链表,给emp找到对应的位置并插入
//思路是 让 cur 和 emp 比较,然后让pre 保持在 cur 邻前
for {
if cur != nil {
//比较
if cur.Id > emp.Id {
//找到位置,cur的前面,pre的后面
break
// pre.Next, emp.Next = emp, cur
// return
}
//pre和cur各向后移动一位
pre, cur = cur, cur.Next
} else {
break
// pre.Next, emp.Next = emp, cur
// return
}
}
//退出时,将emp插入pre的后面,cur的前面
pre.Next, emp.Next = emp, cur
}
//2.2显示链表的信息
func (that *EmpLink) ShowLink(no int) {
if that.Head == nil {
fmt.Printf("链表%d 为空\n", no)
return
}
//遍历当前的链表,并显示数据
cur := that.Head
for {
if cur != nil {
fmt.Printf("链表%d:员工id=%d,名字=%s ->", no, cur.Id, cur.Name)
cur = cur.Next
} else {
break
}
}
fmt.Println() //换行处理
}
//2.3根据id查找对应的员工,如果没有就返回nil
func (that *EmpLink) FindEmpById(id int) *Emp {
cur := that.Head
for {
if cur != nil && cur.Id == id {
return cur
} else if cur == nil {
break
}
cur = cur.Next
}
return nil
}
/*
** 数组型:哈希表
*/
type HashTable struct {
LinkArr [7]EmpLink
}
// 哈希函数
func (that *HashTable) HashFun(id int) int {
return id % 7
}
// 増
func (that *HashTable) HashTableInsert(emp *Emp) {
//使用哈希函数,确定将该员工添加到哪个链表
linkNo := that.HashFun(emp.Id)
//使用对应的链表添加
//HashTable这里只做了id散列,真正添加在EmpLink的EmpLinkInsert方法中完成的
that.LinkArr[linkNo].EmpLinkInsert(emp)
}
// 查
func (that *HashTable) ShowAll() {
for i := 0; i < len(that.LinkArr); i++ {
that.LinkArr[i].ShowLink(i)
}
}
// 找
func (that *HashTable) FindHashTableById(id int) *Emp {
//使用散列函数,确定将该员工应该在哪个链表
linkNo := that.HashFun(id)
return that.LinkArr[linkNo].FindEmpById(id)
}
func main() {
HX := HashTable{}
data1 := Emp{
Id: 1,
Name: "tom",
}
data2 := Emp{
Id: 8,
Name: "cat",
}
HX.HashTableInsert(&data1)
HX.HashTableInsert(&data2)
HX.ShowAll()
res := HX.FindHashTableById(1)
fmt.Println(res)
}
二叉树
package main
import "fmt"
//定义二叉树的节点
type Node struct {
value int
left *Node
right *Node
}
//功能:打印节点的值
//参数:nil
//返回值:nil
func (node *Node) Print() {
fmt.Printf("%d ", node.value)
}
//功能:设置节点的值
//参数:节点的值
//返回值:nil
func (node *Node) SetValue(value int) {
node.value = value
}
//功能:创建节点
//参数:节点的值
//返回值:nil
func CreateNode(value int) *Node {
return &Node{value, nil, nil}
}
//功能:查找节点,利用递归进行查找
//参数:根节点,查找的值
//返回值:查找值所在节点
func (node *Node) FindNode(n *Node, x int) *Node {
if n == nil {
return nil
} else if n.value == x {
return n
} else {
p := node.FindNode(n.left, x)
if p != nil {
return p
}
return node.FindNode(n.right, x)
}
}
//功能:求树的高度
//参数:根节点
//返回值:树的高度,树的高度=Max(左子树高度,右子树高度)+1
func (node *Node) GetTreeHeigh(n *Node) int {
if n == nil {
return 0
} else {
lHeigh := node.GetTreeHeigh(n.left)
rHeigh := node.GetTreeHeigh(n.right)
if lHeigh > rHeigh {
return lHeigh + 1
} else {
return rHeigh + 1
}
}
}
// 根节点在前: 前序遍历
//功能:递归--前序--遍历二叉树
//参数:根节点
//返回值:nil
func (node *Node) PreOrder(n *Node) {
if n != nil {
fmt.Printf("%d ", n.value)
node.PreOrder(n.left)
node.PreOrder(n.right)
}
}
// 根节点在中: 中序遍历
//功能:递归--中序--遍历二叉树
//参数:根节点
//返回值:nil
func (node *Node) InOrder(n *Node) {
if n != nil {
node.InOrder(n.left)
fmt.Printf("%d ", n.value)
node.InOrder(n.right)
}
}
// 根节点在后: 后序遍历
//功能:递归--后序--遍历二叉树
//参数:根节点
//返回值:nil
func (node *Node) PostOrder(n *Node) {
if n != nil {
node.PostOrder(n.left)
node.PostOrder(n.right)
fmt.Printf("%d ", n.value)
}
}
//功能:打印所有的叶子节点
//参数:root
//返回值:nil
func (node *Node) GetLeafNode(n *Node) {
if n != nil {
if n.left == nil && n.right == nil {
fmt.Printf("%d ", n.value)
}
node.GetLeafNode(n.left)
node.GetLeafNode(n.right)
}
}
func main() {
//创建一根节点
root := CreateNode(1)
root.left = CreateNode(2)
root.right = CreateNode(3)
root.left.left = CreateNode(4)
root.left.right = CreateNode(5)
root.right.left = CreateNode(6)
root.right.right = CreateNode(7)
root.left.left.left = CreateNode(8)
root.left.left.right = CreateNode(9)
root.left.right.left = CreateNode(10)
root.left.right.right = CreateNode(11)
root.right.left.left = CreateNode(12)
root.right.left.right = CreateNode(13)
root.right.right.left = CreateNode(14)
root.right.right.right = CreateNode(15)
/*
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
*/
fmt.Printf("树的高度%d\n", root.GetTreeHeigh(root))
fmt.Printf("前序遍历:")//1 2 4 8 9 5 10 11 3 6 12 13 7 14 15
root.PreOrder(root)
fmt.Printf("\n")
fmt.Printf("中序遍历:")//8 4 9 2 10 5 11 1 12 6 13 3 14 7 15
root.InOrder(root)
fmt.Printf("\n")
fmt.Printf("后序遍历:")//8 9 4 10 11 5 2 12 13 6 14 15 7 3 1
root.PostOrder(root)
fmt.Printf("\n")
fmt.Printf("叶节点:")//8 9 10 11 12 13 14 15
root.GetLeafNode(root)
fmt.Printf("\n")
}
相关说明:
-
前序(根左右),中序(左根右),后序(左右根)
-
前序 + 中序 遍历的结果 可以还原 二叉树
-
后序 + 中序 遍历的结果 也可以还原 二叉树
-
前序 + 后序 遍历的结果
不
可以还原 二叉树