结构算法(golang)

粗浅理解

  • 数据结构: 摆放数据的构造。
  • 算法: 处理数据的方法。

实现

// 栈结构
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")
}

相关说明:

  • 前序(根左右),中序(左根右),后序(左右根)

  • 前序 + 中序 遍历的结果 可以还原 二叉树

  • 后序 + 中序 遍历的结果 也可以还原 二叉树

  • 前序 + 后序 遍历的结果 可以还原 二叉树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值