Golang(五十二)[線性表-鏈式存儲-鏈式棧]

  • 棧:
    受限的線性表,受該數據類型存儲的限制。遵循 “ 先進後出 ” 的原則【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)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章