介绍
栈是存放值的一种特殊容器,在插入与删除值时,这种结构遵循后进先出(Last-in-first-out,LIFO)的原则,也就是说,值可以任意插入栈中,但每次取出的都是此前插入的最后一个值。
实现
栈必须支持以下方法:
此外,还可以定义如下的方法:
此外,还应该提供一个类似构造器的NewStack()方法,当我们开始使用它时,它会初始化一个栈。
基于数组的简单实现
为了实现栈接口,我们可以用一个数组来存放其中的元素。
type T inttype Stack struct {sync.RWMutexarray []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 stackimport "testing"var (t1 T = 1t2 T = 2t3 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)}
}
至此,单元测试通过,我们就完成了栈数据结构的实现。