Я отвечу на этот вопрос в Go, потому что в стандартной библиотеке Go нет большого количества коллекций.
Поскольку стек действительно прост в реализации, я подумал, что попробую использовать два стека для создания двусторонней очереди. Чтобы лучше понять, как я пришел к своему ответу, я разделил реализацию на две части. Надеемся, что первую часть легче понять, но она неполна.
type IntQueue struct {
front []int
back []int
}
func (q *IntQueue) PushFront(v int) {
q.front = append(q.front, v)
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[0]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
q.back = q.back[1:]
}
}
func (q *IntQueue) PushBack(v int) {
q.back = append(q.back, v)
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[0]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
q.front = q.front[1:]
}
}
Это в основном два стека, в которых мы позволяем друг другу манипулировать основанием стеков. Я также использовал соглашения об именах STL, в которых традиционные операции стека push, pop, peek имеют префикс front / back независимо от того, ссылаются они на начало или конец очереди.
Проблема с приведенным выше кодом заключается в том, что он не очень эффективно использует память. На самом деле, он растет бесконечно, пока вы не исчерпаете пространство. Это действительно плохо. Решением этой проблемы является простое повторное использование нижней части пространства стека, когда это возможно. Мы должны ввести смещение для отслеживания этого, так как срез в Go не может расти в передней части после сокращения.
type IntQueue struct {
front []int
frontOffset int
back []int
backOffset int
}
func (q *IntQueue) PushFront(v int) {
if q.backOffset > 0 {
i := q.backOffset - 1
q.back[i] = v
q.backOffset = i
} else {
q.front = append(q.front, v)
}
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[q.backOffset]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
if len(q.back) > 0 {
q.backOffset++
} else {
panic("Cannot pop front of empty queue.")
}
}
}
func (q *IntQueue) PushBack(v int) {
if q.frontOffset > 0 {
i := q.frontOffset - 1
q.front[i] = v
q.frontOffset = i
} else {
q.back = append(q.back, v)
}
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[q.frontOffset]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
if len(q.front) > 0 {
q.frontOffset++
} else {
panic("Cannot pop back of empty queue.")
}
}
}
Это много маленьких функций, но из 6 функций 3 из них являются просто зеркалами другой.