文章目錄
數組、鏈表 實現 棧、隊列
導論
隊列可以使用鏈表或者數組實現,使用鏈表的優點是擴容簡單,缺點是無法通過索引定位元素,使用數組則相反,擴容不容易但是可以通過索引定位元素。
鏈表(Link)
鏈表一般有下面這幾個基本操作,先定義一個接口,方便開發和測試:
- 創建空鏈表
- 獲取鏈表的長度
- 判斷鏈表是否爲空
- 在鏈表後追加元素
- 在鏈表前追加元素
- 指定位置插入元素
- 獲取指定位置的元素信息
- 打印鏈表信息
- 刪除指定位置的鏈表元素
- 清空鏈表
type Element interface {}
type Node struct {
Data Element
Next *Node
}
type Link struct {
Head *Node
Length int
}
/*
當鏈表中只包含頭節點,則鏈表長度爲0,稱爲空鏈表。 當插入元素是,也是忽略頭節點進行的操作。
*/
// 1、創建空鏈表
//創建鏈表的時候,不需要傳入參數,直接返回一個鏈表就ok
func New() *Link{
head := new(Node)
return &Link{head,0}
}
// 2、獲取鏈表的長度。返回 0 則爲空
func (l *Link)Len() int{
return l.Length
}
// 3、判斷鏈表是否爲空。爲空則返回 true
func (l *Link)IsEmpty()bool{
return l.Length == 0
}
// 4、在鏈表後追加元素
func (l *Link)Append(value interface{}){
n := &Node{value,nil}
p := l.Head
if !l.IsEmpty() {
for p.Next != nil{
p = p.Next
}
}
p.Next = n
l.Length++
return
}
// 5、在鏈表前追加元素
func (l *Link)PreAppend(value interface{}){
n := &Node{value,nil}
n.Next = l.Head.Next
l.Head.Next = n
l.Length++
return
}
// 6、向鏈表的指定位置插入元素
func (l *Link)Insert(index int, value interface{}){
if index < 0 || l.IsEmpty(){
l.PreAppend(value)
}else if index > l.Len(){
l.Append(value)
}else{
n := &Node{value,nil}
p := l.Head
for i:=0; i<index-1; i++{
p = p.Next
}
n.Next = p.Next
p.Next = n
l.Length++
return
}
}
// 7、獲取指定位置的元素信息
func (l *Link)Query(index int)Element{
if l.IsEmpty() || index < 0 || index > l.Len(){
return nil
}
p := l.Head.Next
for i:=1; i<index;i++{
p = p.Next
}
return p.Data
}
// 8、打印鏈表信息
func (l *Link)Print(){
if l.IsEmpty(){
return
}
p := l.Head
for p != nil{
fmt.Println(p.Data)
p = p.Next
}
}
// 9、刪除指定位置的鏈表元素
func (l *Link)Delete(index int){
p := l.Head
for i:=0;i<index-1;i++{
p = p.Next
}
p.Next = p.Next.Next
l.Length--
}
// 10、清空鏈表
func (l *Link)Empty(){
l.Head.Next = nil
l.Length = 0
}
循環鏈表(約瑟夫問題)
package main
import "fmt"
type Element interface{}
var NoData Element = 0
type Node struct {
Data Element
Next *Node
}
type CirculationLink struct {
Head *Node
Length int
}
// 1、創建循環鏈表
func New() *CirculationLink {
head := new(Node)
return &CirculationLink{Head: head, Length: 0}
}
// 2、獲取鏈表長度
func (l *CirculationLink ) Len() int {
return l.Length
}
// 3、判斷是否爲空鏈表
func (l *CirculationLink ) IsEmpty() bool {
if l.Head.Next == nil {
return true
}
return false
}
// 4、向鏈表末尾追加數據
func (l *CirculationLink ) Append(e Element) {
node := &Node{Data: e, Next: nil}
p := l.Head
if l.IsEmpty() {
p.Next = node
l.Length++
return
}
for p.Next != l.Head {
p = p.Next
}
p.Next = node
l.Length++
return
}
// 5、在頭部追加數據
func (l *CirculationLink ) PreAppend(e Element) {
p := l.Head
node := &Node{Data: e, Next: nil}
if l.IsEmpty() {
p.Next = node
l.Length++
return
}
// 插入節點的 NEXT 指向頭節點的 Next
node.Next = p.Next
// 頭節點的 Next 指向 新插入的節點
p.Next = node
l.Length++
return
}
// 6、在指定位置插入數據
func (l *CirculationLink ) Insert(index int, e Element) {
if l.IsEmpty() || index < 0 {
l.PreAppend(e)
return
}
if index > l.Len() {
l.Append(e)
return
}
p := l.Head
node := &Node{Data: e, Next: nil}
for i := 0; i < index-1; i++ {
p = p.Next
}
// 新插入節點的 Next 節點指向 p[index-1]的Next 節點
node.Next = p.Next
// p[index -1] 的Next 節點 指向 新插入的節點
p.Next = node
l.Length++
return
}
// 7、刪除指定位置的數據, 並返回該數據
func (l *CirculationLink ) Delete(index int) Element {
if l.IsEmpty() {
fmt.Println("list is empty. delete error")
return NoData
}
if index < 0 || index > l.Len() {
fmt.Println("index out of range. delete error")
}
p := l.Head
for i := 0; i < index; i++ {
p = p.Next
}
e := p.Next.Data
// 先將 p [index -1] 的 Next 指向 p [index] 的 Next
p.Next = p.Next.Next
l.Length--
return e
}
// 8、查找指定位置的數據
func (l *CirculationLink) Query(index int) Element {
if l.IsEmpty() {
fmt.Println("list is empty. ")
return NoData
}
if index < 0 || index > l.Len() {
return NoData
}
p := l.Head
for i := 0; i < index; i++ {
p = p.Next
}
return p.Data
}
// 9、打印鏈表
func (l *CirculationLink) Print() {
if l.IsEmpty() {
fmt.Println("list is empty")
}
p := l.Head.Next
i := 1
for p != l.Head {
fmt.Printf("iNode %d, Data %#v\n", i, p.Data)
i++
p = p.Next
}
}
雙向鏈表
package main
import "fmt"
type Element interface{}
var NoData Element = 0
type Node struct {
Data Element
Next *Node
Pre *Node
}
type DoubleLink struct {
Head *Node
Length int
}
// 1、創建雙向鏈表
func New() *DoubleLink {
head := new(Node)
return &DoubleLink {Head: head, Length: 0}
}
// 2、獲取鏈表長度
func (l *DoubleLink ) Len() int {
return l.Length
}
// 3、判斷是否爲空鏈表
func (l *DoubleLink ) IsEmpty() bool {
if l.Head.Next == nil && l.Head.Pre == nil {
return true
}
return false
}
// 4、向鏈表末尾追加數據
func (l *DoubleLink ) Append(e Element) {
node := &Node{Data: e, Next: nil, Pre: nil}
p := l.Head
if l.IsEmpty() {
p.Next = node
node.Pre = p
l.Length++
return
}
for p.Next != nil {
p = p.Next
}
p.Next = node
node.Pre = p
l.Length++
return
}
// 5、在頭部追加數據
func (l *DoubleLink ) PreAppend(e Element) {
p := l.Head
node := &Node{Data: e, Next: nil, Pre: nil}
if l.IsEmpty() {
p.Next = node
node.Pre = p
l.Length++
return
}
// 插入節點的 NEXT 指向頭節點的 Next
node.Next = p.Next
// 頭節點的 Next 的 Pre 指向 新插入的節點
p.Next.Pre = node
// 頭節點的 Next 指向 新插入的節點
p.Next = node
// 新插入節點的 Pre 指向頭節點
node.Pre = p
l.Length++
return
}
// 6、在指定位置插入數據
func (l *DoubleLink ) Insert(index int, e Element) {
if l.IsEmpty() || index < 0 {
l.PreAppend(e)
return
}
if index > l.Len() {
l.Append(e)
return
}
p := l.Head
node := &Node{Data: e, Next: nil, Pre: nil}
for i := 0; i < index-1; i++ {
p = p.Next
}
// 新插入節點的 Next 節點指向 p[index-1]的Next 節點
node.Next = p.Next
// p[index - 1]的 Next.Pre 節點 指向 node 節點
p.Next.Pre = node
// p[index -1] 的Next 節點 指向 新插入的節點
p.Next = node
// ❤新插入的節點的Pre 指向 p[index-1]
node.Pre = p
l.Length++
return
}
// 7、刪除指定位置的數據, 並返回該數據
func (l *DoubleLink ) Delete(index int) Element {
if l.IsEmpty() {
fmt.Println("list is empty. delete error")
return NoData
}
if index < 0 || index > l.Len() {
fmt.Println("index out of range. delete error")
}
p := l.Head
for i := 0; i < index; i++ {
p = p.Next
}
e := p.Data
// 先將 p [index -1] 的 Next 指向 p [index] 的 Next
p.Pre.Next = p.Next
// 再將 p [index + 1] 的 Pre 指向 p [index -1]
p.Next.Pre = p.Pre
l.Length--
return e
}
// 8、查找指定位置的數據
func (l *DoubleLink) Query(index int) Element {
if l.IsEmpty() {
fmt.Println("list is empty. ")
return NoData
}
if index < 0 || index > l.Len() {
return NoData
}
p := l.Head
for i := 0; i < index; i++ {
p = p.Next
}
return p.Data
}
// 9、打印鏈表
func (l *DoubleLink) Print() {
if l.IsEmpty() {
fmt.Println("list is empty")
}
p := l.Head.Next
i := 1
for p != nil {
fmt.Printf("iNode %d, Data %#v\n", i, p.Data)
i++
p = p.Next
}
}
棧 (Stack)
-
棧是一種線性結構,與數組相比,棧對應的操作是數組的子集。
-
它只能從一端添加元素,也只能從一端取出元素(這一端稱之爲棧頂)。
-
Stack這種數據結構用途很廣泛,在計算機的使用中,大量的運用了棧,比如編譯器中的詞法分析器、Java虛擬機、軟件中的撤銷操作(Undo)、瀏覽器中的回退操作,編譯器中的函數調用實現等等。
接口 | 說明 | 複雜度 |
---|---|---|
push() | 向棧中加入元素 | O(1) |
pop() | 彈出棧頂元素 | O(1) |
peek() | 查看棧頂元素 | O(1) |
getSize() | 獲取棧中元素個數 | O(1) |
isEmpty() | 判斷棧是否爲空 | O(1) |
數組實現stack
package stack
const NoData int = 0
const MaxSize int = 5
type Item interface {
}
// ItemStack the stack of Items
type ItemStack struct {
Items [MaxSize ]Item
Top int
}
// 1、New Create a new ItemStack
func (s *ItemStack) New() *ItemStack {
Items := [MaxSize]Item{}
return &ItemStack{Items,0}
}
// 2、Push adds an Item to the top of the stack
func (s *ItemStack) Push(t Item) {
if s.IsFull() {
fmt.Println(" Stack is full")
return
}
s.Items = append(s.Items, t)
s.Top++
}
// 3、Pop removes an Item from the top of the stack
func (s *ItemStack) Pop() *Item {
if s.IsEmpty() {
fmt.Println("stack is empty")
return NoData
}
s.Top--
item := s.Items[s.Top] // 後進先出
s.Items[s.Top] = 0
return &item
}
// 4、判斷棧是否爲空
func (s *ItemStack) IsEmpty() bool {
if s.Top == 0 {
return true
}
return false
}
// 5、判斷棧是否滿了
func (s *ItemStack) IsFull() bool {
if s.Top == MaxSize {
return true
}
return false
}
// 6、清空stack
func (s *ItemStack) Clear() {
var item [MaxSize]Item
s.Items = item
s.Top = 0
}
順序存儲雙stack共享空間,解決數組存儲問題
單鏈表實現stack
package main
// 棧頂放在單鏈表的尾部,當然放在頭部也很好
type Stack struct {
top *node
length int
}
type node struct {
value interface{}
prev *node
}
// 創建一個棧
func New() *Stack{
return &Stack{nil,0}
}
// 入棧
func (s *Stack)Push(value interface){
n := &node{value, s.top}
s.top = n
s.length++
}
// 出棧
func (s *Stack)Pop()interface{}{
if s.length == 0 {
return nil
}
res := s.top
s.top = s.top.prev
s.length--
return res.value
}
// 查看棧頂元素
func (s *Stack) Query() interface{} {
if s.length == 0 {
return nil
}
return s.top.value
}
// 取棧長度
func (s *Stack)Len()int{
return s.length
}
package main
import "fmt"
/*
雙向鏈表實現棧
*/
type Element interface{}
type Node struct {
Data Element
Next *Node
Pre *Node
}
type Stack struct {
size int
Top *Node
}
// 1、新建一個棧
func New() *Stack {
return &Stack{size: 0, Top: new(Node)}
}
// 3、獲取當前棧的大小
func (s *Stack) Len() int {
return s.size
}
// 3、判斷是否爲空棧,true 爲空, false 爲非空
func (s *Stack) IsEmpty() bool {
if s.Len() != 0 {
return false
}
return true
}
// 4、入棧
func (s *Stack) Push(e Element) {
node := &Node{Data: e, Next: nil, Pre: nil}
p := s.Top
// 如果鏈表爲空
if s.IsEmpty() {
p.Pre = node
node.Next = p
s.size++
return
}
// top節點的前一個節點的Next 指向新加入的節點。
p.Pre.Next = node
// 新加入節點指向 top 節點的前一個節點。
node.Pre = p.Pre
// 新加入節點的 Next 指向 Top 節點。
node.Next = p
// top 節點的 Pre 指向新加入的節點
p.Pre = node
// 更新top
s.Top = node
s.size++
return
}
// 5、將棧頂元素彈出棧,並返回被彈出的元素
func (s *Stack) Pop() (e Element) {
if s.IsEmpty() {
fmt.Println("stack is empty")
}
// 獲取棧頂指針
p := s.Top
// 被彈出的元素,一定是 倒數第一個元素
e = p.Pre.Data
// 如果只有一個節點, 將 Top 節點的 Pre 指向 nil, 前一個節點的 Next 指向 nil
if p.Pre.Pre == nil {
p.Pre.Next = nil
p.Pre = nil
s.size--
return
}
// 將 top 節點的 前一個節點 的前一個節點。也就是倒入第二個節點的 Next 指向 top 節點。
// 將 top 節點的 Pre 指向 倒數第二個節點。
p.Pre.Pre.Next = p
p.Pre = p.Pre.Pre
s.size--
return
}
// 6、本着 先入後出的原則,我們從棧頂開始遍歷 棧
func (s *Stack) Print() {
if s.IsEmpty() {
fmt.Println("stack is empty")
return
}
p := s.Top.Pre
iNode := s.Len()
for p != nil {
fmt.Printf("iNode == %d, data == %#v\n", iNode, p.Data)
iNode--
p = p.Pre
}
return
}
// 7、清空棧
func (s *Stack) Clear() {
if s.IsEmpty() {
fmt.Println("stack is empty")
}
s.Top.Pre = nil
s.size = 0
return
}
##隊列 Queue
-
隊列也是一種線性數據結構,與數組相比,隊列對應的操作是數組的子集。
-
只能從一端 (隊尾) 添加元素,只能從另一端 (隊首) 取出元素。
-
隊列的應用可以在播放器上的播放列表,數據流對象,異步的數據傳輸結構(文件IO,管道通訊,套接字等)上體現,當然最直觀的的就是排隊了。
隊列的實現
接口 | 說明 | 複雜度 |
---|---|---|
enqueue() | 入隊 | O(1) 均攤 |
dequeue() | 出隊 | O(n) |
getFront() | 獲取隊首元素 | O(1) |
getSize() | 獲取隊列元素個數 | O(1) |
isEmpty() | 判斷隊列是否爲空 | O(1) |
數組實現queue
package queue
type Item interface {
}
// Item the type of the queue
type ItemQueue struct {
items []Item
}
type ItemQueuer interface {
New() ItemQueue
Enqueue(t Item)
Dequeue() *Item
IsEmpty() bool
Size() int
}
// New creates a new ItemQueue
func (s *ItemQueue) New() *ItemQueue {
s.items = []Item{}
return s
}
// Enqueue adds an Item to the end of the queue
func (s *ItemQueue) Enqueue(t Item) {
s.items = append(s.items, t)
}
// dequeue
func (s *ItemQueue) Dequeue() Item {
item := s.items[0] // 先進先出
s.items = s.items[1:len(s.items)]
return &item
}
func (s *ItemQueue) IsEmpty() bool {
return len(s.items) == 0
}
// Size returns the number of Items in the queue
func (s *ItemQueue) Size() int {
return len(s.items)
}
數組實現循環隊列
package queue
import "errors"
type CyclicQueue struct {
front int
rear int
length int
capacity int
nodes []*Node
}
func NewCyclicQueue(capacity int) (*CyclicQueue, error) {
if capacity <= 0 {
return nil, errors.New("capacity is less than 0")
}
nodes := make([]*Node, capacity, capacity)
return &CyclicQueue{
front: -1,
rear: -1,
capacity: capacity,
nodes: nodes,
}, nil
}
func (q *CyclicQueue) Length() int {
return q.length
}
func (q *CyclicQueue) Capacity() int {
return q.capacity
}
func (q *CyclicQueue) Front() *Node {
if q.length == 0 {
return nil
}
return q.nodes[q.front]
}
func (q *CyclicQueue) Rear() *Node {
if q.length == 0 {
return nil
}
return q.nodes[q.rear]
}
func (q *CyclicQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
index := (q.rear + 1) % cap(q.nodes)
q.nodes[index] = node
q.rear = index
q.length++
if q.length == 1 {
q.front = index
}
return true
}
func (q *CyclicQueue) Dequeue() interface{} {
if q.length == 0 {
return nil
}
result := q.nodes[q.front].value
q.nodes[q.front] = nil
index := (q.front + 1) % cap(q.nodes)
q.front = index
q.length--
return result
}
單鏈表實現queue
package main
import "fmt"
/*
我們用單鏈表來實現隊列的功能。
頭指針指向 隊列的第一個元素。
尾指針指向 隊列的最後一個元素。
*/
type Element interface{}
type Node struct {
Data Element
Next *Node
}
type Queue struct {
Head *Node
Tail *Node
Length int
}
// 1、創建一個隊列
func Create() *Queue {
head := &Node{Data: nil, Next: nil}
tail := &Node{Data: nil, Next: nil}
return &Queue{Head: head, Tail: tail, Length: 0}
}
// 2、返回隊列的長度
func (q *Queue) Len() int {
return q.Length
}
// 3、判斷隊列是否爲空
func (q *Queue) IsEmpty() bool {
if q.Len() == 0 {
return true
}
return false
}
// 4、打印隊列元素
func (q *Queue) Print() {
if q.IsEmpty() {
fmt.Println("queue is empty")
}
p := q.Head.Next
iNode := 1
for p != nil {
fmt.Printf("iNode == %#v,Data == %#v\n", iNode, p.Data)
iNode++
p = p.Next
}
return
}
// 5、向隊列內添加元素
func (q *Queue) Append(e Element) {
node := &Node{Data: e, Next: nil}
// 如果爲空
if q.IsEmpty() {
q.Head.Next = node
q.Tail.Next = node
q.Length++
return
}
// 讓最後一個節點的 Next 指向 新的節點
q.Tail.Next = node
// 尾節點指向 新的節點
q.Tail = node
q.Length++
return
}
// 6、刪除元素,出隊列
func (q *Queue) Delete() {
if q.IsEmpty() {
fmt.Println("queue is empty")
return
}
if q.Len() == 1 {
q.Head.Next = nil
q.Tail.Next = nil
q.Length--
return
}
q.Head.Next = q.Head.Next.Next
q.Length--
return
}
// 7、清空隊列
func (q *Queue) Clear() {
if q.IsEmpty() {
fmt.Println("queue is empty")
return
}
q.Head.Next = nil
q.Tail.Next = nil
q.Length = 0
return
}
雙鏈表實現queue
package queue
import "errors"
type Queue interface {
Length() int
Capacity() int
Front() *Node
Rear() *Node
Enqueue(value interface{}) bool
Dequeue() interface{}
}
type Node struct {
value interface{}
previous *Node
next *Node
}
type NormalQueue struct {
front *Node
rear *Node
length int
capacity int
}
func NewNormalQueue(capacity int) (*NormalQueue, error) {
if capacity <= 0 {
return nil, errors.New("capacity is less than 0")
}
front := &Node{
value: nil,
previous: nil,
}
rear := &Node{
value: nil,
previous: front,
}
front.next = rear
return &NormalQueue{
front: front,
rear: rear,
capacity: capacity,
}, nil
}
func (q *NormalQueue) Length() int {
return q.length
}
func (q *NormalQueue) Capacity() int {
return q.capacity
}
func (q *NormalQueue) Front() *Node {
if q.length == 0 {
return nil
}
return q.front.next
}
func (q *NormalQueue) Rear() *Node {
if q.length == 0 {
return nil
}
return q.rear.previous
}
func (q *NormalQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
if q.length == 0 {
q.front.next = node
}
node.previous = q.rear.previous
node.next = q.rear
q.rear.previous.next = node
q.rear.previous = node
q.length++
return true
}
func (q *NormalQueue) Dequeue() interface{} {
if q.length == 0 {
return nil
}
result := q.front.next
q.front.next = result.next
result.next = nil
result.previous = nil
q.length--
return result.value
}
小結:
可以看到,具體實現和單鏈表基本一致,這種方法好處在於不需要考慮數組溢出的問題。但是有時候,我們可能會向 queue 插入相同的元素,我們當前的實現是無法判斷數據是否已經存在的,這時我們就需要實現一個無重複元素的 queue。
雙鏈表實現無重複元素的隊列
我們只需要在原來的基礎上加一個 Map 存放我們的具體值就可以了。直接上代碼:
package queue
import (
"errors"
"reflect"
)
type UniqueQueue struct {
front *Node
rear *Node
length int
capacity int
nodeMap map[interface{}]bool
}
func NewUniqueQueue(capacity int) (*UniqueQueue, error) {
if capacity <= 0 {
return nil, errors.New("capacity is less than 0")
}
front := &Node{
value: nil,
}
rear := &Node{
value: nil,
previous: front,
}
front.next = rear
nodeMap := make(map[interface{}]bool)
return &UniqueQueue{
front: front,
rear: rear,
capacity: capacity,
nodeMap: nodeMap,
}, nil
}
func (q *UniqueQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
// Ignore uncomparable type.
if kind := reflect.TypeOf(value).Kind(); kind == reflect.Map || kind == reflect.Slice || kind == reflect.Func {
return false
}
if v, ok := q.nodeMap[value]; ok || v {
return false
}
if q.length == 0 {
q.front.next = node
}
node.previous = q.rear.previous
node.next = q.rear
q.rear.previous.next = node
q.rear.previous = node
q.nodeMap[value] = true
q.length++
return true
}
最小棧、最小隊列、最大棧、最大隊列
- 實現一個棧,帶有出棧(pop),入棧(push),取最小元素(getMin)三個方法。這三個方法的時間複雜度都是O(1)
- 實現一個棧,帶有出棧(pop),入棧(push),取最大元素(getMax)三個方法。這三個方法的時間複雜度都是O(1)
- 實現一個隊列,帶有出隊(deQueue),入隊(enQueue),取最小元素(getMin)三個方法。時間複雜度都儘可能小。
- 實現一個隊列,帶有出隊(deQueue),入隊(enQueue),取最大元素(getMax)三個方法。時間複雜度都儘可能小。