算法与数据结构(Go语言版本)学习笔记Day02: 数组栈、数组队列,链式栈,链式队列,用栈实现深度遍历,用队列实现广度遍历

目录

0x00 递归实现文件夹的遍历

0x01 用栈+循环深度遍历文件夹:

0x02 队列

0x03 用队列广度遍历文件夹

0x04 循环队列

0x05 链式栈

0x06 链式队列


栈和队列的应用:遍历树形结构

下面以遍历文件夹的为例:我们电脑中的文件夹 本质上就是一个树形结构,我们可以分别用递归、栈、队列来实现深度遍历(递归和栈)和广度遍历(队列)

有子节点的就是文件夹,没有子节点就是文件

0x00 递归实现文件夹的遍历

思路:

写一个读取文件夹的函数:用一个函数读取文件中的所有文件和文件夹,然后遍历所有文件和文件夹,如果是文件,就存起来,如果是文件夹,就再次调用该函数。

func GetAll(path string, files []string) ([]string, error) {
	read, err := ioutil.ReadDir(path) //读取文件夹
	if err != nil {
		return files, errors.New("文件夹不可读取")
	}
	for _, fi := range read { //循环该文件夹中的文件/文件夹
		if fi.IsDir() { //判断是否是文件夹
			fulldir := path + "/" + fi.Name() //构造新的路径
			files = append(files, fulldir)
			files, _ = GetAll(fulldir, files) //文件夹递归处理
		} else {
			fullpath := path + "/" + fi.Name()
			files = append(files, fullpath)
		}
	}
	return files, nil
}

测试:

func main10() {
	path := "/Applications/MAMP/htdocs/learngo/DataStructure"
	files := []string{}
	files, _ = GetAll(path, files)
	for i := 0; i < len(files); i++ {
		fmt.Println(files[i])
	}
}

0x01 用栈+循环深度遍历文件夹:

思路:

如果是文件夹 就入栈,然后不断从栈中获取文件夹,然后读取该文件夹下的文件夹和文件(一层),如果是文件夹就入栈

func main11() {
	path := "/Applications/MAMP/htdocs/learngo"
	files := []string{}
	stack := StackArray.NewStack()
	stack.Push(path)
	//是文件夹就压入栈中,不是文件夹就不压,即用栈来缓存文件夹名
	for !stack.IsEmpty() {
		data := stack.Pop()
		read, _ := ioutil.ReadDir(data.(string))
		for _, fi := range read {
			fullpath := data.(string) + "/" + fi.Name()
			if fi.IsDir() {
				stack.Push(fullpath)
			}
			files = append(files, fullpath)
		}
	}
	for i := 0; i < len(files); i++ {
		fmt.Println(files[i])
	}
}

在递归中加入层级:

func GetAllX(path string, files []string, level int) ([]string, error) {
	levelstr := ""
	if 1 == level {
		levelstr = "|-- "
	} else {
		n := level
		for levelstr = "|"; n >= 1; n-- {
			levelstr = levelstr + "   "
		}
		levelstr += "|-- "
	}

	read, err := ioutil.ReadDir(path) //读取文件夹
	if err != nil {
		return files, errors.New("文件夹不可读取")
	}
	for _, fi := range read { //循环该文件夹中的文件/文件夹
		if fi.IsDir() { //判断是否是文件夹
			fulldir := path + "/" + fi.Name() //构造新的路径
			files = append(files, levelstr+fi.Name())
			files, _ = GetAllX(fulldir, files, level+1) //文件夹递归处理
		} else {
			files = append(files, levelstr+fi.Name())
		}
	}
	return files, nil
}

测试:

func main() {
	path := "/Applications/MAMP/htdocs/learngo"
	files := []string{}
	files, _ = GetAllX(path, files, 1)
	for i := 0; i < len(files); i++ {
		fmt.Println(files[i])
	}
}

效果图:

0x02 队列

/*
 * @Author: your name
 * @Date: 2020-12-10 21:43:51
 * @LastEditTime: 2020-12-10 22:44:31
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /learngo/DataStructure/Queue/Queue.go
 */
package Queue

type MyQueue interface {
	Size() int
	Front() interface{}       //第一个元素
	End() interface{}         //最后一个元素
	IsEmpty() bool            //是否为空
	EnQueue(data interface{}) //入队
	DeQueue() interface{}     //出队
	Clear()                   //清空
}
type Queue struct {
	dataStore []interface{}
	theSize   int
}

func NewQueue() *Queue {
	q := new(Queue)
	q.dataStore = make([]interface{}, 0, 100)
	q.theSize = 0
	return q
}

func (this *Queue) Size() int {
	return this.theSize
}

func (this *Queue) Front() interface{} {
	if 0 == this.Size() {
		return nil
	}
	return this.dataStore[0]
}

func (this *Queue) End() interface{} {
	if 0 == this.Size() {
		return nil
	}
	return this.dataStore[len(this.dataStore)-1]
}

func (this *Queue) IsEmpty() bool {
	return 0 == this.theSize
}

func (this *Queue) EnQueue(data interface{}) {
	this.dataStore = append(this.dataStore, data)
	this.theSize++
}

func (this *Queue) DeQueue() interface{} {
	if this.Size() == 0 {
		return nil
	}
	tail := this.Front()
	if this.Size() > 1 {
		this.dataStore = this.dataStore[1:this.Size()]
	} else {
		this.dataStore = make([]interface{}, 0, 100)
	}
	this.theSize--
	return tail
}

func (this *Queue) Clear() {
	this.dataStore = make([]interface{}, 0, 100)
	this.theSize = 0
}

0x03 用队列广度遍历文件夹

func main() {
	path := "/Applications/MAMP/htdocs/learngo"
	files := []string{}
	queue := Queue.NewQueue()
	queue.EnQueue(path)
	for !queue.IsEmpty() {
		data := queue.DeQueue()
		read, _ := ioutil.ReadDir(data.(string))
		for _, fi := range read {
			fullpath := data.(string) + "/" + fi.Name()
			if fi.IsDir() {
				queue.EnQueue(fullpath)
			}
			files = append(files, fullpath)
		}
	}
	for i := 0; i < len(files); i++ {
		fmt.Println(files[i])
	}
}

总结:

队列 和栈在遍历文件夹的过程中起的都是一个暂存文件夹的功能。

区别是当从他们中读取一个文件夹后,其子文件夹进入的方式不同,从而导致了广度遍历和深度遍历的差异。

队列是将子文件夹放到最后,栈是将子文件夹放到最前。

0x04 循环队列

思路:

代码:

/*
 * @Author: your name
 * @Date: 2020-12-11 11:42:37
 * @LastEditTime: 2020-12-11 14:27:16
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /learngo/DataStructure/CircleQueue/CircleQueue.go
 */
package CircleQueue

import "errors"

const QueueSize = 100

type CircleQueue struct {
	data  [QueueSize]interface{} //存储数据的结构
	front int                    //头部的位置
	rear  int                    //尾部的位置
}

func (this *CircleQueue) InitQueue() { //头部尾部重合为空
	this.front = 0
	this.rear = 0
}

func (this *CircleQueue) EnQueue(data interface{}) error {
	//最多存储QueueSize-1个数据 空一个格表示满格 用与区分开满格状态和空队列状态
	if (this.rear+1)%QueueSize == this.front%QueueSize {
		return errors.New("队列已满")
	}
	this.data[this.rear] = data
	this.rear = (this.rear + 1) % QueueSize

	return nil
}

func (this *CircleQueue) DeQueue() (interface{}, error) {
	if this.rear == this.front {
		return nil, errors.New("队列空了")
	}
	res := this.data[this.front]
	this.front = (this.front + 1) % QueueSize
	return res, nil
}

func (this *CircleQueue) QueueLength() int {
	return (this.rear - this.front + QueueSize) % QueueSize
	//情形1:rear>front 直接减即可
	//情形2: front > rear 给rear补上QueueSize 去减front
}

0x05 链式栈

一般小玩具可以用数组栈,大型项目多使用链式栈,因为如果数据量大的话,数组栈就需要开辟很大一块连续的内存,显然这是非常困难的。

头部入栈,头部出栈:

/*
 * @Author: your name
 * @Date: 2020-12-11 15:01:12
 * @LastEditTime: 2020-12-11 16:46:47
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /DataStructure(Golang)/LinkStack.go
 */
package Link

import "errors"

type Node struct {
	data  interface{}
	pNext *Node
}

type LinkStackX interface {
	IsEmpty() bool
	Push(data interface{}) error
	Pop() (interface{}, error)
	Length() int
}

func NewStackX() *Node {
	node := new(Node)
	node.data = 0
	return node
}

//头节点的data用来存长度
func (this *Node) IsEmptyX() bool {
	if nil == this.pNext {
		return true
	} else {
		return false
	}
}

//从头部入栈
func (this *Node) PushX(data interface{}) error {
	node := new(Node)
	node.data = data
	node.pNext = this.pNext
	this.pNext = node
	this.data = this.data.(int) + 1
	return nil
}

//从头部出栈
func (this *Node) PopX() (interface{}, error) {
	if this.IsEmptyX() {
		return nil, errors.New("栈空了")
	}
	ret := this.pNext.data
	this.pNext = this.pNext.pNext
	this.data = this.data.(int) - 1
	return ret, nil
}

func (this *Node) LengthX() int {
	return this.data.(int)
}

尾部入栈,尾部出栈:

/*
 * @Author: your name
 * @Date: 2020-12-11 15:01:12
 * @LastEditTime: 2020-12-11 16:46:57
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /DataStructure(Golang)/LinkStack.go
 */
package Link

import "errors"

type NodeX struct {
	data  interface{}
	pNext *Node
}

type LinkStack interface {
	IsEmpty() bool
	Push(data interface{}) error
	Pop() (interface{}, error)
	Length() int
}

func NewStack() *Node {
	node := new(Node)
	node.data = 0
	return node
}

//头节点的data用来存长度
func (this *Node) IsEmpty() bool {
	if nil == this.pNext {
		return true
	} else {
		return false
	}
}

func (this *Node) Push(data interface{}) error {
	node := new(Node)
	node.data = data
	node.pNext = nil
	_pNext := this
	for _pNext.pNext != nil {
		_pNext = _pNext.pNext
	}
	_pNext.pNext = node
	this.data = this.data.(int) + 1
	return nil
}

func (this *Node) Pop() (interface{}, error) {
	if this.IsEmpty() {
		return nil, errors.New("栈空了")
	}
	_pNext := this
	for _pNext.pNext.pNext != nil {
		_pNext = _pNext.pNext
	}
	ret := _pNext.pNext.data
	_pNext.pNext = nil
	this.data = this.data.(int) - 1
	return ret, nil
}

func (this *Node) Length() int {
	return this.data.(int)
}

测试:

/*
 * @Author: your name
 * @Date: 2020-12-11 14:59:45
 * @LastEditTime: 2020-12-11 16:52:46
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /DataStructure(Golang)/main.go
 */
package main

import (
	"DataStructure/Link"
	"fmt"
)

func main() {
	linkStack := Link.NewStack()
	for i := 0; i < 10000; i++ {
		linkStack.Push(i)
	}
	for !linkStack.IsEmpty() {
		fmt.Println(linkStack.Pop())
	}
}

func main1() {
	linkStack := Link.NewStackX()
	for i := 0; i < 100000; i++ {
		linkStack.PushX(i)
	}
	for !linkStack.IsEmptyX() {
		fmt.Println(linkStack.PopX())
	}
}

0x06 链式队列

/*
 * @Author: your name
 * @Date: 2020-12-11 15:01:17
 * @LastEditTime: 2020-12-11 17:48:52
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /DataStructure(Golang)/LinkQueue.go
 */
package Link

type LinkQueue interface {
	Length() int
	EnQueue(value interface{})
	DeQueue() interface{}
}

type QueueLink struct {
	rear  *Node
	front *Node
}

func NewQueue() *QueueLink {
	q := new(QueueLink)
	return q
}

func (this *QueueLink) Length() int {
	length := 0
	point := this.front
	for point.pNext != nil {
		length++
	}
	return length
}

//尾部入队
func (this *QueueLink) EnQueue(value interface{}) {
	newnode := &Node{value, nil}
	if nil == this.front { //如果当前没有头节点,说明队列是空的,需要同时设定头结点和尾节点
		this.front = newnode
		this.rear = newnode
	} else {
		this.rear.pNext = newnode
		this.rear = this.rear.pNext
	}
}

//头部出队
func (this *QueueLink) DeQueue() interface{} {
	if nil == this.front {
		return nil //队列为空
	}
	newnode := this.front        //记录头部位置
	if this.front == this.rear { //只剩下一个
		this.front = nil
		this.rear = nil
	} else {
		this.front = this.front.pNext
	}
	return newnode.data
}

测试:

func main() {
	queue := Link.NewQueue()
	for i := 0; i < 10000000; i++ {
		queue.EnQueue(i)
	}
	for data := queue.DeQueue(); data != nil; data = queue.DeQueue() {
		fmt.Println(data)
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值