Go的数据结构与实现【Stack】

2,461 阅读2分钟

介绍

栈是存放值的一种特殊容器,在插入与删除值时,这种结构遵循后进先出(Last-in-first-outLIFO)的原则,也就是说,值可以任意插入栈中,但每次取出的都是此前插入的最后一个值。

本文代码地址为go-store

实现

栈必须支持以下方法:

操作方法功能描述
Push(t)将值t压至栈顶
Pop()若栈非空,则将栈顶对象移除,并将其返回;否则,报错

此外,还可以定义如下的方法:

操作方法功能描述
Size()返回栈内值的数目
IsEmpty()检查栈是否为空

此外,还应该提供一个类似构造器的NewStack()方法,当我们开始使用它时,它会初始化一个栈。

基于数组的简单实现

为了实现栈接口,我们可以用一个数组来存放其中的元素。

type T int

type Stack struct {
   sync.RWMutex
   array []T
}

构造器NewStack()方法如下:

func NewStack() *Stack {
   stack := &Stack{}
   stack.array = []T{}

   return stack
}

接下来,我们去实现之前提到的操作方法:

// Push adds t to the top of the stack
func (s *Stack) Push(t T) {
   s.Lock()
   s.array = append(s.array, t)
   s.Unlock()
}

对于Push()方法,只需要将值添加到数组中,Go的原生语法为我们解决了这一步骤。

// Pop removes the top element from the stack
func (s *Stack) Pop() (*T, error) {
   if s.IsEmpty() {
      return nil, fmt.Errorf("stack must not be empty")
   }

   s.Lock()
   item := s.array[len(s.array)-1]
   s.array = s.array[0 : len(s.array)-1]
   s.Unlock()
   return &item, nil
}

Pop()方法中,首先检查栈是否为空,如果栈空,则返回空值以及错误信息,否则,将数组第一位取出,整个数组右移一位。

// Size returns the size of the stack
func (s *Stack) Size() int {
   s.RLock()
   defer s.RUnlock()

   return len(s.array)
}

func (s *Stack) IsEmpty() bool {
   s.RLock()
   defer s.RUnlock()

   return len(s.array) == 0
}

至于额外的两个方法,即检查栈结构体中成员变量即可。注意到,栈结构体在定义时加入了锁资源,因此以上所有方法都是并发安全的。

单元测试

我们对实现的方法进行单元测试:

package stack

import "testing"

var (
   t1 T = 1
   t2 T = 2
   t3 T = 3
)

func TestStack_Push(t *testing.T) {
   stack := NewStack()
   stack.Push(t1)
   stack.Push(t2)
   stack.Push(t3)

   first := stack.array[0]
   last := stack.array[len(stack.array)-1]

   if first != t1 || last != t3 {
      t.Errorf("wrong order, expected first 1 and last 3 but got %d and %d", t1, t3)
   }
}

func TestStack_Pop(t *testing.T) {
   stack := NewStack()
   stack.Push(t1)
   stack.Push(t2)
   stack.Push(t3)

   _, _ = stack.Pop()
   if size := stack.Size(); size != 2 {
      t.Errorf("wrong count, expected 2 and got %d", size)
   }
   _, _ = stack.Pop()
   _, _ = stack.Pop()
   if size := stack.Size(); size != 0 {
      t.Errorf("wrong count, expected 0 and got %d", size)
   }

   _, err := stack.Pop()
   if err == nil {
      t.Errorf("stack must not be empty")
   }
}

func TestStack_Size(t *testing.T) {
   stack := NewStack()
   stack.Push(t1)
   stack.Push(t2)
   stack.Push(t3)

   if size := stack.Size(); size != 3 {
      t.Errorf("wrong count, expected 3 and got %d", size)
   }
}

func TestStack_IsEmpty(t *testing.T) {
   stack := NewStack()
   empty := stack.IsEmpty()
   if !empty {
      t.Errorf("wrong status, expected true and got %t", empty)
   }
   stack.Push(t1)
   empty = stack.IsEmpty()
   if empty {
      t.Errorf("wrong status, expected false and got %t", empty)
   }
}

至此,单元测试通过,我们就完成了栈数据结构的实现。