目录
栈和队列的应用:遍历树形结构
下面以遍历文件夹的为例:我们电脑中的文件夹 本质上就是一个树形结构,我们可以分别用递归、栈、队列来实现深度遍历(递归和栈)和广度遍历(队列)
有子节点的就是文件夹,没有子节点就是文件
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)
}
}