Golang-線性表-鏈式存儲-鏈式棧Stack
- 棧:
受限的線性表,受該數據類型存儲的限制。遵循 “ 先進後出 ” 的原則【FILO】 - 依然是鏈式存儲【1:1】
- 無論是壓棧(入棧)、彈棧(出棧)都屬於增、刪操作,故而選用的是鏈式存儲。
- 棧的生長:從棧底【高地址】向 棧頂【低地址】
- 棧的實現:單向鏈表,可以沒有head指針
- 棧的壓棧:單向鏈表的頭插法實現【先進後出】倒敘生長【1,2,3,4,】則頭結點爲【4】
1.初始化
mkdir -p linkStack
cd linkStack
go mod init linkStack
touch linkStack.go
vim linkStack.go
2.定義鏈式棧結點結構體
// 定義鏈式棧結點結構體
type StackNode struct {
Data interface{} // 數據域
Next *StackNode // 指針域
}
3.創建鏈式棧
// 創建鏈式棧
func NewLinkStack(data ...interface{}) *StackNode {
// 容錯處理
if data == nil || len(data) == 0 {
return nil
}
// 定義當前結點的下一個結點
var nodeNext *StackNode = nil
// 創建鏈式棧對象,初始類似於單向鏈表的head結點
stack := new(StackNode)
// 遍歷data數據,創建新結點
for _, v := range data {
// 創建新結點並初始化
newNode := new(StackNode)
newNode.Data = v
newNode.Next = nil
// 將stack保存數據結點爲當前結點,
// 初始時:將newNode覆蓋了stack,類似於數據結點覆蓋了頭結點
// 後續類似於將stack向前偏移了
stack = newNode
// 當前結點的下一個結點nodeNext,初始爲nil,在下一步執行後,變更爲當前的結點
stack.Next = nodeNext
// 當有新結點產生時,nextNode記錄當前結點爲下一個結點,類似於nextNode向前偏移:從而實現棧【倒序生長】
nodeNext = stack
}
// 將鏈式棧返回
return stack
}
- 定義當前結點的下一個結點:nextNode
var nodeNext *StackNode = nil
- 創建鏈式棧對象stack
// 創建鏈式棧對象,初始類似於單向鏈表的head結點
stack := new(StackNode)
- 遍歷data數據,創建新結點
// 創建新結點並初始化
newNode := new(StackNode)
newNode.Data = v
newNode.Next = nil
- 將stack保存數據結點爲當前結點
// 將stack保存數據結點爲當前結點,
// 初始時:將newNode覆蓋了stack,類似於數據結點覆蓋了頭結點
// 後續類似於將stack向前偏移了
stack = newNode
- 當前結點的下一個結點nodeNext
// 當前結點的下一個結點nodeNext,初始爲nil,在下一步執行後,變更爲當前的結點
stack.Next = nodeNext
- 當有新結點產生時,nextNode記錄當前結點爲下一個結點,類似於nextNode向前偏移:從而實現棧【倒序生長】
// 當有新結點產生時,nextNode記錄當前結點爲下一個結點,類似於nextNode向前偏移:從而實現棧【倒序生長】
nodeNext = stack
- 下一次的循環
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4)
fmt.Println(stack)
}
4.打印鏈式棧
// 打印鏈式棧
func PrintLinkStack(s *StackNode) {
// 容錯處理
if s == nil {
return
}
// 遍歷打印,沒有使用s.Next!=nil做退出循環的條件:因爲傳參方式進行打印,且沒有頭結點,如果s.Next!=nil做條件會導致少一次的遍歷,全是數據結點,只需要判斷s!=nil即可
for s != nil {
// 不用if s.Data!=nil,因爲沒有頭結點,全是數據結點,直接打印即可
fmt.Print(s.Data, " ")
s = s.Next // 偏移至下一個結點
}
// 打印完成換行
fmt.Println()
}
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4, 5, 6, 7, 8)
PrintLinkStack(stack)
}
5.獲取鏈式棧的長度
// 獲取鏈式棧長度
func StackLength(s *StackNode) (length int) {
if s == nil {
return -1
}
for s != nil {
length++
s = s.Next
}
return
}
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4, 5, 6, 7, 8)
PrintLinkStack(stack)
length := StackLength(stack)
fmt.Println("鏈式棧的長度爲:", length)
}
6.壓棧
// 壓棧--單向鏈表的頭插法
func PushStack(s *StackNode, data interface{}) *StackNode {
if s == nil {
return nil
}
if data == nil {
return s
}
// 創建新結點
newNode := new(StackNode)
newNode.Data = data
newNode.Next = nil
// 將新結點的指針域指向原鏈表,新結點做爲鏈式棧的頭
newNode.Next = s
return newNode
}
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4, 5, 6, 7, 8)
PrintLinkStack(stack)
stack = PushStack(stack, 11)
PrintLinkStack(stack)
}
7.彈棧
// 彈棧
func PopStack(s *StackNode) *StackNode {
if s == nil {
return nil
}
// 彈出棧內的第一個結點
popStack := s.Next
// 置爲nil,促使GC工作
s.Data = nil
s.Next = nil
s = nil
// 返回彈棧後的鏈式棧
return popStack
}
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4, 5, 6, 7, 8)
PrintLinkStack(stack)
fmt.Println("彈棧後:")
stack = PopStack(stack)
PrintLinkStack(stack)
}
8.清空鏈式棧
// 清空鏈式棧,如果依賴go的GC,直接可以使用stack = nil即可
func ClearStack(s *StackNode) {
// 容錯處理,也是遞歸出口
if s == nil {
return
}
ClearStack(s.Next)
s.Data = nil
s.Next = nil
s = nil
}
測試
func main() {
stack := NewLinkStack(1, 2, 3, 4, 5, 6, 7, 8)
PrintLinkStack(stack)
fmt.Println("清空鏈式棧:")
ClearStack(stack)
PrintLinkStack(stack)
}