Golang(四十九)[線性存儲-鏈表-單向鏈表]

1.初始化

mkdir -p linkList
cd linkList
go mod init linkList
touch linkList.go
vim linkList.go

2.定義單向鏈表結點結構體

// 定義單向鏈表的結點結構體
type LinkNode struct {
	// 定義單向鏈表的數據域
	Data interface{}
	// 定義單向鏈表指針域
	Next *LinkNode
}

3.創建單向鏈表

// 創建單向鏈表
func (node *LinkNode) NewLinkList(data ...interface{}) {
	// 容錯處理
	if node == nil || len(data) == 0 {
		return
	}
	// 保存head結點
	head := node
	// 遍歷data數據創建新結點
	for _, v := range data {
		// 創建新結點,並初始化
		newNode := new(LinkNode)
		newNode.Data = v
		newNode.Next = nil

		// 將當前結點的指針域指向新結點
		node.Next = newNode
		// 更新當前結點
		node = node.Next
	} // 遍歷結束,node在尾結點
	// 將head結點賦值給node,以便node回到head結點位置
	node = head
}

4.打印雙向鏈表

1.遞歸實現

遞歸的內存佔用較大,釋放慢,需要等所有的依次釋放完畢
遞歸的棧空間的棧基和棧頂會調用是會保存每一次的變化值
和for循環相比,循環的棧空間調用完立馬釋放,空間利用率較高,故而不推薦使用,除非特別需要才用犧牲內存空間的方式使用遞歸,否則建議使用for循環實現

// 打印單向鏈表--遞歸實現
func (node *LinkNode) Print1() {
	// 容錯處理,同時也是遞歸的出口
	if node == nil {
		// 在遞歸調用出口處打印換行
		fmt.Println()
		return
	}
	// 數據結點的特點:有數據數據域
	if node.Data != nil {
		fmt.Print(node.Data, " ")
	}
	// 遞歸實現
	node.Next.Print1()
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print1()
}

2.循環實現

// 打印單向鏈表--循環實現
func (node *LinkNode) Print2() {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		node = node.Next // 更新node結點位置,在初始時,可以跳過head指針[head結點:沒有數據域(nil),只有指針域(指向第一個數據結點)]
		// 數據結點的特點:有數據數據域
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
	}
	// 打印換行
	fmt.Println()
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print2()
}

5.獲取單向鏈表的長度[數據結點長度]

// 獲取數據結點長度
func (node *LinkNode) Length() (length int) {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		node = node.Next // 初始可以跳過head結點
		length++

	}

	/* // 也可以使用如下方法獲取:
	for node.Next != nil {
		node = node.Next // 初始跳過head 結點
		if node.Data != nil {
			length++
		}
	} */
	return
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print2()
	length := list.Length()
	fmt.Println("雙向鏈表的數據結點的長度爲:", length)
}

6.插入單向鏈表結點[數據結點]

1.頭插法

// 插入數據結點 1.頭插法
func (node *LinkNode) InsertNode(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	// 創建新結點並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = nil
	// 將新結點的指針域指向原數據結點[因爲第一個爲head結點,所以數據結點從node.Next開始
	newNode.Next = node.Next
	// 將head結點的指針域指向新結點[因爲第一個爲head結點]
	node.Next = newNode
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print2()
	list.InsertNode(11)
	list.Print2()
}

2.尾插法

// 插入數據結點 2.尾插法
func (node *LinkNode) InsertNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	// 偏移到尾結點
	for node.Next != nil {
		node = node.Next
	}
	// 創建新結點並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = nil
	// 將尾結點的指針域指向新結點
	node.Next = newNode
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print2()
	list.InsertNode2(12)
	list.Print2()
}

3.按位置插入[位置,非索引]

// 插入數據結點 3.按位置插入
func (node *LinkNode) InsertNode3(index int, data interface{}) {
	// 容錯處理
	if node == nil || data == nil || index < 0 {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	if index > length {
		return
	}
	// 頭部插入
	if index == 0 {
		node.InsertNode(data)
		return
	}
	// 尾部插入
	if index == length {
		node.InsertNode2(data)
		return
	}
	// 定義待插入結點的前一個結點
	preNode := node
	// 遍歷數據結點,到達指定位置
	for i := 0; i < index; i++ {
		// 待插入結點的前一個結點
		preNode = node
		// 偏移node結點到下一個結點
		node = node.Next
	} // node此時在待插入結點
	// 創建新結點,並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = node // 將新結點的指針域指向待插入結點[原結點]
	// 將待插入的前一個結點的指針域指向新結點
	preNode.Next = newNode
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5)
	list.Print2()
	list.InsertNode3(2, 13)
	list.Print2()
}

7.刪除數據結點

1.按位置刪除[位置,非索引]

// 刪除單向鏈表的數據結點--根據位置刪除
func (node *LinkNode) DeleteNode(index int) {
	// 容錯處理
	if node == nil || index < 0 {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	if index > length {
		return
	}

	// 如果index爲0,則刪除第一個數據結點
	if index == 0 {
		// 保存頭結點
		head := node
		// 到達第一個數據結點
		node = node.Next
		// 1.將頭結點的指針域設置爲第一個數據結點的下一個結點
		head.Next = node.Next
		// 2.置爲nil,方便GC回收
		// 第一個數據結點的數據清nil
		node.Data = nil
		// 第一個數據結點的指針域設置爲nil
		node.Next = nil
		// 3.還原頭指針
		node = head
		return
	}
	// 定義待刪除結點的前一個結點
	preNode := node
	// 遍歷結點到達待刪除結點的前一個結點位置
	for i := 0; i < index; i++ {
		// 給待刪除結點重新賦值
		preNode = node
		// 偏移到下一個結點
		node = node.Next
	} // 遍歷完成後,node到達待刪除結點位置
	// 如果刪除的是尾結點
	if index == length {
		// node到達尾結點
		// 尾結點的前一個結點的指針域設置爲nil
		preNode.Next = nil
		// 置爲nil,方便GC回收
		// 尾結點的數據清nil
		node.Data = nil
		return
	}
	// 1.將待刪除結點(node)的前一個結點(preNode)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
	preNode.Next = node.Next

	// 2.置爲nil,方便GC回收
	// 將待刪除結點的數據置爲nil
	node.Data = nil
	// 將待刪除結點的指針域置爲nil
	node.Next = nil
	// 將待刪除結點置爲nil
	node = nil
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
	list.Print1()
	list.DeleteNode(5)
	list.Print1()
}

2.按元素刪除

// 刪除單向鏈表的數據結點--根據元素刪除
func (node *LinkNode) DeleteNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}

	// 保存前一個結點
	preNode := node
	// 遍歷結點到達待刪除結點的前一個結點位置
	for node.Next != nil {
		preNode = node
		// 偏移到下一個結點
		node = node.Next
		// 判斷數據類型和數據是否一致
		if reflect.TypeOf(data) == reflect.TypeOf(node.Data) && reflect.DeepEqual(node.Data, data) {
			// 如果刪除的結點是尾結點 node.Next ==nil
			if node.Next == nil {
				// 將前一個結點的指針域設置爲空
				preNode.Next = nil
				// 置爲nil,方便GC回收
				node.Data = nil
				node.Next = nil
				node = nil
				return
			}
			// node到達待刪除結點位置
			// 1.將待刪除結點(node)的前一個結點(preNode)的地址域指向待刪除結點(node)的下一個結點(node.Next)
			preNode.Next = node.Next
			// 2.置爲nil,方便GC回收
			// 將待刪除結點的數據置爲nil
			node.Data = nil
			// 將待刪除結點的後引指針域置爲nil
			node.Next = nil
			// 將待刪除結點置爲nil
			node = nil
			break
		}
	}
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
	list.Print1()
	list.DeleteNode2(7)
	list.Print1()
}

8.銷燬單向鏈表

// 銷燬單向鏈表
func (node *LinkNode) Destroy() {
	// 容錯處理
	if node == nil {
		return
	}
	// 遞歸調用
	node.Next.Destroy()
	// 置nil
	node.Data = nil
	node.Next = nil
	node = nil
}

測試

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
	list.Print1()
	list.Destroy()
	list.Print1()
}

9.完整代碼

package main

import (
	"fmt"
	"reflect"
)

// 定義單向鏈表的結點結構體
type LinkNode struct {
	// 定義單向鏈表的數據域
	Data interface{}
	// 定義單向鏈表指針域
	Next *LinkNode
}

// 創建單向鏈表
func (node *LinkNode) NewLinkList(data ...interface{}) {
	// 容錯處理
	if node == nil || len(data) == 0 {
		return
	}
	// 保存head結點
	head := node
	// 遍歷data數據創建新結點
	for _, v := range data {
		// 創建新結點,並初始化
		newNode := new(LinkNode)
		newNode.Data = v
		newNode.Next = nil

		// 將當前結點的指針域指向新結點
		node.Next = newNode
		// 更新當前結點
		node = node.Next
	} // 遍歷結束,node在尾結點
	// 將head結點賦值給node,以便node回到head結點位置
	node = head
}

// 正向打印單向鏈表--遞歸實現
func (node *LinkNode) Print1() {
	// 容錯處理,同時也是遞歸的出口
	if node == nil {
		// 在遞歸調用出口處打印換行
		fmt.Println()
		return
	}
	// 數據結點的特點:有數據數據域
	if node.Data != nil {
		fmt.Print(node.Data, " ")
	}
	// 遞歸實現
	node.Next.Print1()
}

// 正向打印單向鏈表--循環實現
func (node *LinkNode) Print2() {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		node = node.Next // 更新node結點位置,在初始時,可以跳過head指針[head結點:沒有數據域(nil),只有指針域(指向第一個數據結點)]
		// 數據結點的特點:有數據數據域
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
	}
	// 打印換行
	fmt.Println()
}

// 獲取數據結點長度
func (node *LinkNode) Length() (length int) {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		node = node.Next // 初始可以跳過head結點
		length++

	}

	/* // 也可以使用如下方法獲取:
	for node.Next != nil {
		node = node.Next // 初始跳過head 結點
		if node.Data != nil {
			length++
		}
	} */
	return
}

// 插入數據結點 1.頭插法
func (node *LinkNode) InsertNode(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	// 創建新結點並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = nil
	// 將新結點的指針域指向原數據結點[因爲第一個爲head結點,所以數據結點從node.Next開始
	newNode.Next = node.Next
	// 將head結點的指針域指向新結點[因爲第一個爲head結點]
	node.Next = newNode
}

// 插入數據結點 2.尾插法
func (node *LinkNode) InsertNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	// 偏移到尾結點
	for node.Next != nil {
		node = node.Next
	}
	// 創建新結點並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = nil
	// 將尾結點的指針域指向新結點
	node.Next = newNode
}

// 插入數據結點 3.按位置插入
func (node *LinkNode) InsertNode3(index int, data interface{}) {
	// 容錯處理
	if node == nil || data == nil || index < 0 {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	if index > length {
		return
	}
	// 頭部插入
	if index == 0 {
		node.InsertNode(data)
		return
	}
	// 尾部插入
	if index == length {
		node.InsertNode2(data)
		return
	}
	// 定義待插入結點的前一個結點
	preNode := node
	// 遍歷數據結點,到達指定位置
	for i := 0; i < index; i++ {
		// 待插入結點的前一個結點
		preNode = node
		// 偏移node結點到下一個結點
		node = node.Next
	} // node此時在待插入結點
	// 創建新結點,並初始化
	newNode := new(LinkNode)
	newNode.Data = data
	newNode.Next = node // 將新結點的指針域指向待插入結點[原結點]
	// 將待插入的前一個結點的指針域指向新結點
	preNode.Next = newNode
}

// 刪除單向鏈表的數據結點--根據位置刪除
func (node *LinkNode) DeleteNode(index int) {
	// 容錯處理
	if node == nil || index < 0 {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	if index > length {
		return
	}

	// 如果index爲0,則刪除第一個數據結點
	if index == 0 {
		// 保存頭結點
		head := node
		// 到達第一個數據結點
		node = node.Next
		// 1.將頭結點的指針域設置爲第一個數據結點的下一個結點
		head.Next = node.Next
		// 2.置爲nil,方便GC回收
		// 第一個數據結點的數據清nil
		node.Data = nil
		// 第一個數據結點的指針域設置爲nil
		node.Next = nil
		// 3.還原頭指針
		node = head
		return
	}
	// 定義待刪除結點的前一個結點
	preNode := node
	// 遍歷結點到達待刪除結點的前一個結點位置
	for i := 0; i < index; i++ {
		// 給待刪除結點重新賦值
		preNode = node
		// 偏移到下一個結點
		node = node.Next
	} // 遍歷完成後,node到達待刪除結點位置
	// 如果刪除的是尾結點
	if index == length {
		// node到達尾結點
		// 尾結點的前一個結點的指針域設置爲nil
		preNode.Next = nil
		// 置爲nil,方便GC回收
		// 尾結點的數據清nil
		node.Data = nil
		return
	}
	// 1.將待刪除結點(node)的前一個結點(preNode)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
	preNode.Next = node.Next

	// 2.置爲nil,方便GC回收
	// 將待刪除結點的數據置爲nil
	node.Data = nil
	// 將待刪除結點的指針域置爲nil
	node.Next = nil
	// 將待刪除結點置爲nil
	node = nil
}

// 刪除單向鏈表的數據結點--根據元素刪除
func (node *LinkNode) DeleteNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}

	// 保存前一個結點
	preNode := node
	// 遍歷結點到達待刪除結點的前一個結點位置
	for node.Next != nil {
		preNode = node
		// 偏移到下一個結點
		node = node.Next
		// 判斷數據類型和數據是否一致
		if reflect.TypeOf(data) == reflect.TypeOf(node.Data) && reflect.DeepEqual(node.Data, data) {
			// 如果刪除的結點是尾結點 node.Next ==nil
			if node.Next == nil {
				// 將前一個結點的指針域設置爲空
				preNode.Next = nil
				// 置爲nil,方便GC回收
				node.Data = nil
				node.Next = nil
				node = nil
				return
			}
			// node到達待刪除結點位置
			// 1.將待刪除結點(node)的前一個結點(preNode)的地址域指向待刪除結點(node)的下一個結點(node.Next)
			preNode.Next = node.Next
			// 2.置爲nil,方便GC回收
			// 將待刪除結點的數據置爲nil
			node.Data = nil
			// 將待刪除結點的後引指針域置爲nil
			node.Next = nil
			// 將待刪除結點置爲nil
			node = nil
			break
		}
	}
}

// 銷燬單向鏈表
func (node *LinkNode) Destroy() {
	// 容錯處理
	if node == nil {
		return
	}
	// 遞歸調用
	node.Next.Destroy()
	// 置nil
	node.Data = nil
	node.Next = nil
	node = nil
}

func main() {
	list := new(LinkNode)
	list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
	list.Print1()
	list.Destroy()
	list.Print1()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章