重学数据结构与算法(3)-从Golang切片来简单认识栈

310 阅读4分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

数据结构

数据结构是计算机储存组织数据的方式,数据结构大体分成三大类型:线性结构树形结构图形结构,当然我们从最简单的线性结构来说。
线性结构的数据结构通常指的是n个相同类型元素的有限序列,N>=0。 线性结构数据通常也叫线性表。我们最常见的数组就是线性结构的,一般线性表中的数据类型应该是相同的,并且个数是有限的。线性表存储在计算机的储存中是连续的,所以内存地址也是连续的,所以在定义一个数组的时候数据是无法动态修改容量,如果储存方式不是连续的那么这种数据结构就是链表,链表我们放在后面再说吧。
在学习golang的过程中说了一下Golang的切片扩容: Go语言切片(8)
学习golang中我才认真了解到堆和栈这个概念,对于更底层的与堆栈相关性更高的底层语言C和C++没有认真去了解过。Golang切片底层是一个数组,数组是一个值类型的变量,切片是一个引用类型的变量。值类型的变量的值一般储存在栈中,引用类型是一种更复杂的存储结构,引用类型储存的值是一个内存地址,这个地址储存最终的值,引用类型的变量一般在堆上分配,通过GC(垃圾回收机制)回收,要学习后面的知识先复习一下基础。

栈是一种先进后出的数据结构,使用数组实现的栈叫做顺序栈。栈只能在一端进行删除和增加的操作,经常听说的说的两个词语:入栈(也叫压栈,push)和出栈(也叫退栈,pop)就是在栈这个数据结构里面进行增加元素和删除元素的操作,在只能操作删除和增加的一端叫做栈顶,不能操作的一端叫做栈底,这样理解的话栈底就是最先入栈的元素,栈顶就是最后入栈的元素。最先入栈的元素是最后出栈的元素,所以叫先进后出。堆的知识相对于栈来说较为复杂,我们放在后面说。

Golang来实现一个栈的方法

栈的常用方法就是入栈和出栈,获取栈的元素个数,获取栈顶元素,顺便巩固一下Golang的知识. 首先使用Golang的结构体(struct)制定一个栈的抽象结构。包含了一个切片,切片中元素的个数.因为Golang中数组是不能进行扩容的,而切片是可以的,切片底层是一个数组,所以采用切片:

package main

import (
	"errors"
	"fmt"
)

type myStack struct {
	sliceData []string //定义一个切片。切片元素类型为string
	sliceLen  int      //栈中元素的个数
}

func makeStack() *myStack {
	return &myStack{sliceData: make([]string, 0)} //返回一个结构体指针 ,让下面栈方法去操作指针类型(底层类型)而不是值类型
}

//返回栈的大小
func (s *myStack) size() int {
	return s.sliceLen
}

//元素入栈
func (s *myStack) push(insertData string) {
	s.sliceData = append(s.sliceData, insertData)
	s.sliceLen = s.sliceLen + 1
}

//元素出栈并返回删除的元素
func (s *myStack) pop() interface{} {
	if s.sliceLen > 0 {
		s.sliceLen--                           //栈长度减一
		index := s.sliceData[s.sliceLen]       //获取栈顶的元素
		s.sliceData = s.sliceData[:s.sliceLen] //重新剪切切片。接去掉最后一个元素
		return index                           //返回删除的元素
	}
	return errors.New("栈没有元素了")
}

//返回栈顶的元素
func (s *myStack) top() interface{} {
	if s.isEmpty() { //栈为空就报错
		return nil
	}
	return s.sliceData[s.sliceLen-1] // 切片的最后一个元素
}

//判断切片长度是否为空
func (p *myStack) isEmpty() bool {
	return p.sliceLen == 0
}

func main() {
	var stack = makeStack()
	stack.push("hello")
	fmt.Printf("数组长度:%d \n", stack.size())
	stack.push("world")
	fmt.Printf("数组长度:%d \n", stack.size())
	stack.pop()
	fmt.Printf("数组长度:%d \n", stack.size())
	stack.push("我是土圭垚墝")
	fmt.Printf("切片的栈顶是:%s \n", stack.top())
	for i, j := range stack.sliceData {
		fmt.Printf("切片第%d个内容是%s\n", i, j)
	}
	fmt.Printf("切片的长度是:%d \n", stack.sliceLen)
}

输出内容:

数组长度:1 
数组长度:2 
数组长度:1
切片的栈顶是:我是土圭垚墝
切片第0个内容是hello
切片第1个内容是我是土圭垚墝
切片的长度是:2

总结

这篇就简单认识了一下栈的各种操作方法,对数据结构有了初步了解,Goalng代码都很简单,没有学习Golang应该也可以看得懂。