数据结构代码
1.稀疏数组
当一个数组中大部分元素为0或者同为一个值时,可以用稀疏数组保存。即只记录不同的值。
第一个值一般记录原始大小。
package mainimport ("encoding/json""fmt""io/ioutil")const (BLACK_CHESS = 1 // 黑子WIHTE_CHESS = 2 // 白子FILE_NAME = "data.json")type ValNode struct {Row int `json:"row"`Col int `json:"col"`Val int `json:"val"`}// 稀疏数组func main() {var chessMap [10][10]intchessMap[1][5] = BLACK_CHESSchessMap[2][3] = WIHTE_CHESSchessMap[4][6] = BLACK_CHESSchessMap[7][9] = WIHTE_CHESSfor _, v := range chessMap {for _, v2 := range v {fmt.Printf("%d\t", v2)}fmt.Println()}var sparseArr []ValNode// 第一个结构体记录原始数组的大小sparseArr = append(sparseArr, ValNode{Row: len(chessMap), Col: len(chessMap[0]), Val: 0})for r, v := range chessMap {for c, v2 := range v {if v2 != 0 {valNode := ValNode{Row: r, Col: c, Val: v2}sparseArr = append(sparseArr, valNode)}}}// 打印for _, node := range sparseArr {fmt.Println(node)}// 存到文件,用json格式j, err := json.Marshal(sparseArr)if err != nil {fmt.Println(err)return}err = ioutil.WriteFile(FILE_NAME, j, 0666)if err != nil {fmt.Println(err)return}// 恢复b, err := ioutil.ReadFile(FILE_NAME)if err != nil {fmt.Println(err)return}var sparseSlice []ValNodeerr = json.Unmarshal(b, &sparseSlice)if err != nil {fmt.Println(err)return}fmt.Println("恢复后的棋盘")for _, sp := range sparseSlice {fmt.Println(sp)}}
2.环形队列
package mainimport ("errors""fmt")/**环形队列思路:1.创建一个数组,设置最大值2.一个指向队首的指针front和一个指向队尾的指针real,初始化为03.提供三个方法:push,pop和show4.当push的时候,real 指针往后移5.pop的时候front指针往后移6.当两个指针重合的时候,就空了7.当real指针加一取模后等于头就满了实际容量是数组容量减一*/type CircleQueue struct {MaxSize int // 最大数量Array [5]int // sliceHead int // 指向队首Tail int // 指向队尾}// 加入队列func (queue *CircleQueue) Push(val int) error {if queue.IsFull() {return errors.New("queue is full")}queue.Array[queue.Tail] = valqueue.Tail = (queue.Tail + 1) % queue.MaxSizereturn nil}// 弹出func (queue *CircleQueue) Pop() (val int, err error) {if queue.IsEmpty() {return 0, errors.New("queue is empty")}val = queue.Array[queue.Head]queue.Head = (queue.Head + 1) % queue.MaxSizereturn val, nil}// 显示队列func (queue *CircleQueue) ShowQueue() {size := queue.Size()if size == 0 {fmt.Println("queue is empty")return}var tempHead = queue.Headfor i := 0; i < size; i++ {fmt.Printf("arr[%d]=%d\t", tempHead, queue.Array[tempHead])tempHead = (tempHead + 1) % queue.MaxSize}fmt.Println()}// 判断队列已满func (queue *CircleQueue)IsFull() bool {return (queue.Tail + 1) % queue.MaxSize == queue.Head}// 是否为空func (queue *CircleQueue) IsEmpty() bool {return queue.Head == queue.Tail}// 有多少个元素func (queue *CircleQueue) Size() int {return (queue.Tail - queue.Head + queue.MaxSize) % queue.MaxSize}func main() {// 队列circleQueue := CircleQueue{MaxSize: 5}circleQueue.ShowQueue()err := circleQueue.Push(1)ifError(err)err = circleQueue.Push(4)ifError(err)err = circleQueue.Push(6)ifError(err)err = circleQueue.Push(5)ifError(err)err = circleQueue.Push(7)ifError(err)circleQueue.ShowQueue()err = circleQueue.Push(7)ifError(err)v, err := circleQueue.Pop()ifError(err)fmt.Printf("pop value=%d\n", v)circleQueue.ShowQueue()}func ifError(err error) {if err != nil {fmt.Println(err)}}
3.单链表
一般来说,为了比较好的对单链表进行增删改查,我们会给它设置一个头节点,主要作用是标识链表头,而且不存放数据。
用链表实现队列更方便,效率更高。
package mainimport "fmt"// 单链表type HeroNode struct {no intname stringnickName stringnext *HeroNode // 指向下一个节点}// 直接在最后插入func insertLast(head *HeroNode, new *HeroNode) {temp := headfor {if temp.next == nil { // 如果找到最后则跳出break}temp = temp.next // 不断指向下一个节点}temp.next = new}// 排序插入,根据no的大小func insertSort(head *HeroNode, new *HeroNode) {temp := headflag := true// 比较新节点和temp的下一个节点for {if temp.next == nil { // 到了链表最后break} else if new.no < temp.next.no {// new 应该插入到temp的下一个break} else if new.no == temp.next.no {flag = false}temp = temp.next // 不断指向下一个节点}if !flag {fmt.Println("对不起,节点已经存在,no=", new.no)return} else {// 插入节点new.next = temp.nexttemp.next = new}}// 删除一个节点// no : 删除的numberfunc deleteNode(head *HeroNode, no int) {temp := headfinded := falsefor {if temp.next == nil { // 到了链表最后break} else if no == temp.next.no {finded = truebreak}temp = temp.next // 不断指向下一个节点}if finded {temp.next = temp.next.next} else {fmt.Println("没有找到")}}func showAll(head *HeroNode) {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.nextif temp.next == nil {break}}fmt.Println()}func main() {head := HeroNode{}node1 := HeroNode{no: 1, name: "宋江", nickName: "及时雨"}node2 := HeroNode{no: 2, name: "吴用", nickName: "智多星"}node3 := HeroNode{no: 3, name: "林冲", nickName: "豹子头"}node4 := HeroNode{no: 4, name: "X", nickName: "x"}//insertLast(&head, &node2)//insertLast(&head, &node3)//insertLast(&head, &node1)insertSort(&head, &node2)insertSort(&head, &node4)insertSort(&head, &node3)insertSort(&head, &node1)showAll(&head)deleteNode(&head, 3)showAll(&head)}
还有 双向链表 和 环形链表,先放一放,估计也很少用到。
4. 栈
应用场景:
- 子程序调用:在跳往子程序前,会将下个指令的地址保存在栈中,直到子程序执行完,再从栈中取出;
- 处理递归调用;
- 表达式的求值与转换;
- 二叉树遍历;
- 图形的深度优先搜索法。
package chapter2import ("errors""fmt")// 实现一个栈// 出栈、入栈、遍历type Stack struct {}var arr [5]int // 用数组实现栈var pointer = -1 // 位置指针// 出栈func (this *Stack) Pop() (val int, err error){if pointer < 0 {return -1, errors.New("stack is empty")}val = arr[pointer]pointer -= 1return val, nil}// 入栈func (this *Stack) Push(val int) {if pointer >= len(arr)-1 {fmt.Println("stack is full")return}pointer += 1arr[pointer] = val}// 遍历func (this *Stack) List() {if pointer < 0 {fmt.Println("stack is empty")return}fmt.Println("stack list:")for i := pointer; i >= 0; i-- {fmt.Println(arr[i])}}
表达式求值思路:
- 用两个栈,一个保存数,一个保存操作符;
- 如果已经入栈的操作符优先级比要入栈的低,那么先计算
