Golang(五十)[線性存儲-鏈表-雙向鏈表]

1.初始化

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

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

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

3.創建雙向鏈表

// 創建雙向鏈表
func (node *LinkNode) NewLinkList(data ...interface{}) {
	// 容錯處理
	if node == nil || data == nil || len(data) == 0 {
		return
	}
	// 保存head(頭結點)
	head := node
	// 遍歷參數數據,創建雙向鏈表的結點
	for _, v := range data {
		// 1.創建新結點
		newNode := new(LinkNode)
		// 2.初始化結點
		newNode.Data = v
		newNode.Prev = node // 新結點的前引地址域就是node
		newNode.Next = nil  // 新結點的後引地址域爲nil
		// 將當前結點的下一個結點指向新結點
		node.Next = newNode
		// 更新當前結點
		node = node.Next
	}
	// 還原head 結點
	node = head
}

4.打印雙向鏈表

1.正向

1.遞歸實現

// 正向打印雙向鏈表--遞歸實現
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, 6)
	list.Print1()
}

2.循環實現

// 正向打印雙向鏈表--循環實現
func (node *LinkNode) Print2() {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		node = node.Next // 第一次可以跳過head結點
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
	}
	// 換行
	fmt.Println()
}

測試

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

2.反向

1.遞歸實現

// 反向打印雙向鏈表--遞歸實現
var isFirstHeadNode = true // 定義head結點是在第一次出現
func (node *LinkNode) Print3() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		// 在遞歸出口處執行換行
		fmt.Println()
		return
	}
	// 如果是head結點,跳轉到尾結點
	// head結點:前引爲nil,Data爲nil,Next不爲nil
	// isFirstHeadNode作爲控制跳轉的次數,確保遞歸從尾到頭結束後不會二次跳轉到尾結點,不至於造成無限遞歸
	if node.Prev == nil && node.Data == nil && isFirstHeadNode == true {
		for node.Next != nil {
			node = node.Next
		}
	}

	if node.Data != nil {
		fmt.Print(node.Data, " ")
	}
	// 已經不再是head結點第一次出現了
	isFirstHeadNode = false
	// 使用前引指針域調用打印方法
	node.Prev.Print3()
}

測試

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

2.循環實現

// 反向打印雙向鏈表--循環實現
func (node *LinkNode) Print4() {
	// 容錯處理
	if node == nil {
		return
	}
	// 跳轉到尾結點
	for node.Next != nil {
		node = node.Next
	}
	for node.Prev != nil {
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
		node = node.Prev // 跳轉到上一節點
	}
	// 換行
	fmt.Println()
}

測試

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

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, 6)
	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.Prev = node // 將新結點的前引指針域指向原node結點[因爲第一個是head結點,所以指向的是head結點]
	newNode.Next = nil
	// 將新結點的後引指針域指向原數據結點[因爲第一個爲head結點,所以數據結點從node.Next開始
	newNode.Next = node.Next
	// 將head結點的後引指針域指向新結點
	node.Next = newNode
}

測試

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

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.Prev = node // 因爲已經偏移到尾結點,所以將新結點的前引指針域設置爲尾結點
	newNode.Next = nil
	// 將尾結點的後引指針域指向新結點
	node.Next = newNode
}

測試

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

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.Prev = preNode // 將新結點的前引指針域指向待插入結點的前一個結點
	newNode.Next = node    // 將新結點的後引指針域指向待插入結點[原結點]
	// 將待插入結點[原結點]的前引指針域指向新結點
	node.Prev = newNode
	// 將待插入的前一個結點的後引指針域指向新結點
	preNode.Next = newNode
}

測試

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

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.將第一個數據結點的後一個結點的前引指針域設置爲頭結點
		node.Next.Prev = head
		// 2.將頭結點的後引指針域設置爲第一個數據結點的下一個結點
		head.Next = node.Next
		// 3.置爲nil,方便GC回收
		// 第一個數據結點的數據清nil
		node.Data = nil
		// 第一個數據結點的前引指針域設置爲nil
		node.Prev = nil
		// 第一個數據結點的後引指針域設置爲nil
		node.Next = nil
		// 4.還原頭指針
		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
		// 尾結點的前引指針域設置爲nil
		node.Prev = nil
		return
	}
	// 1.將待刪除結點(node)的前一個結點(preNode)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
	preNode.Next = node.Next
	// 2.將待刪除結點(node)的後一個結點的前引地址域指向待刪除結點的前一個結點
	node.Next.Prev = preNode

	// 3.置爲nil,方便GC回收
	// 將待刪除結點的數據置爲nil
	node.Data = nil
	// 將待刪除結點的前引指針域置爲nil
	node.Prev = 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(0)
	list.Print1()
}

2.按元素刪除

// 刪除雙向鏈表的數據結點--根據元素刪除
func (node *LinkNode) DeleteNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	// 遍歷結點到達待刪除結點的前一個結點位置
	for i := 0; i <= length; i++ {
		// 判斷數據類型和數據是否一致
		if reflect.TypeOf(data) == reflect.TypeOf(node.Data) && reflect.DeepEqual(node.Data, data) {
			// 如果刪除的結點是尾結點 i == length
			if i == length {
				// 1.將尾結點的前一結點的後引指針域設置爲空
				node.Prev.Next = nil
				// 2.置爲nil,方便GC回收
				node.Data = nil
				node.Prev = nil
				node.Next = nil
				node = nil
				return
			}
			// node到達待刪除結點位置
			// 1.將待刪除結點(node)的前一個結點(node.Prev)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
			node.Prev.Next = node.Next
			// 2.將待刪除結點(node)的後一個結點(node.Next)的前引地址域指向待刪除結點的前一個結點
			node.Next.Prev = node.Prev

			// 3.置爲nil,方便GC回收
			// 將待刪除結點的數據置爲nil
			node.Data = nil
			// 將待刪除結點的前引指針域置爲nil
			node.Prev = nil
			// 將待刪除結點的後引指針域置爲nil
			node.Next = nil
			// 將待刪除結點置爲nil
			node = nil
			break
		}
		// 偏移到下一個結點
		node = node.Next
	}
}

測試

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.Prev = 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{}
	// 定義雙向鏈表的前引指針域
	Prev *LinkNode
	// 定義雙向鏈表的後引指針域
	Next *LinkNode
}

// 創建雙向鏈表
func (node *LinkNode) NewLinkList(data ...interface{}) {
	// 容錯處理
	if node == nil || data == nil || len(data) == 0 {
		return
	}
	// 保存head(頭結點)
	head := node
	// 遍歷參數數據,創建雙向鏈表的結點
	for _, v := range data {
		// 1.創建新結點
		newNode := new(LinkNode)
		// 2.初始化結點
		newNode.Data = v
		newNode.Prev = node // 新結點的前引地址域就是node
		newNode.Next = nil  // 新結點的後引地址域爲nil
		// 將當前結點的下一個結點指向新結點
		node.Next = newNode
		// 更新當前結點
		node = node.Next
	}
	// 還原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 // 第一次可以跳過head結點
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
	}
	// 換行
	fmt.Println()
}

// 反向打印雙向鏈表--遞歸實現
var isFirstHeadNode = true // 定義head結點是在第一次出現
func (node *LinkNode) Print3() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		// 在遞歸出口處執行換行
		fmt.Println()
		return
	}
	// 如果是head結點,跳轉到尾結點
	// head結點:前引爲nil,Data爲nil,Next不爲nil
	// isFirstHeadNode作爲控制跳轉的次數,確保遞歸從尾到頭結束後不會二次跳轉到尾結點,不至於造成無限遞歸
	if node.Prev == nil && node.Data == nil && isFirstHeadNode == true {
		for node.Next != nil {
			node = node.Next
		}
	}

	if node.Data != nil {
		fmt.Print(node.Data, " ")
	}
	// 已經不再是head結點第一次出現了
	isFirstHeadNode = false
	// 使用前引指針域調用打印方法
	node.Prev.Print3()
}

// 反向打印雙向鏈表--循環實現
func (node *LinkNode) Print4() {
	// 容錯處理,同時也是
	if node == nil {
		return
	}
	// 跳轉到尾結點
	for node.Next != nil {
		node = node.Next
	}
	for node.Prev != nil {
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
		node = node.Prev // 跳轉到上一節點
	}
	// 換行
	fmt.Println()
}

// 獲取數據結點長度
func (node *LinkNode) Length() (length int) {
	// 容錯處理
	if node == nil {
		return
	}
	for node.Next != nil {
		length++ // 初始可以跳過head結點
		node = node.Next
	}
	/*
		也可以使用如下方法獲取:
		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.Prev = node // 將新結點的前引指針域指向原node結點[因爲第一個是head結點,所以指向的是head結點]
	newNode.Next = nil
	// 將新結點的後引指針域指向原數據結點[因爲第一個爲head結點,所以數據結點從node.Next開始
	newNode.Next = node.Next
	// 將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.Prev = node // 因爲已經偏移到尾結點,所以將新結點的前引指針域設置爲尾結點
	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 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.Prev = preNode // 將新結點的前引指針域指向待插入結點的前一個結點
	newNode.Next = node    // 將新結點的後引指針域指向待插入結點[原結點]
	// 將待插入結點[原結點]的前引指針域指向新結點
	node.Prev = newNode
	// 將待插入的前一個結點的後引指針域指向新結點
	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.將第一個數據結點的後一個結點的前引指針域設置爲頭結點
		node.Next.Prev = head
		// 2.將頭結點的後引指針域設置爲第一個數據結點的下一個結點
		head.Next = node.Next
		// 3.置爲nil,方便GC回收
		// 第一個數據結點的數據清nil
		node.Data = nil
		// 第一個數據結點的前引指針域設置爲nil
		node.Prev = nil
		// 第一個數據結點的後引指針域設置爲nil
		node.Next = nil
		// 4.還原頭指針
		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
		// 尾結點的前引指針域設置爲nil
		node.Prev = nil
		return
	}
	// 1.將待刪除結點(node)的前一個結點(preNode)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
	preNode.Next = node.Next
	// 2.將待刪除結點(node)的後一個結點的前引地址域指向待刪除結點的前一個結點
	node.Next.Prev = preNode

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

// 刪除雙向鏈表的數據結點--根據元素刪除
func (node *LinkNode) DeleteNode2(data interface{}) {
	// 容錯處理
	if node == nil || data == nil {
		return
	}
	length := node.Length()
	if length == 0 {
		return
	}
	if length == 0 {
		return
	}
	// 遍歷結點到達待刪除結點的前一個結點位置
	for i := 0; i <= length; i++ {
		// 判斷數據類型和數據是否一致
		if reflect.TypeOf(data) == reflect.TypeOf(node.Data) && reflect.DeepEqual(node.Data, data) {
			// 如果刪除的結點是尾結點 i == length
			if i == length {
				// 1.將尾結點的前一結點的後引指針域設置爲空
				node.Prev.Next = nil
				// 2.置爲nil,方便GC回收
				node.Data = nil
				node.Prev = nil
				node.Next = nil
				node = nil
				return
			}
			// node到達待刪除結點位置
			// 1.將待刪除結點(node)的前一個結點(node.Prev)的後引地址域指向待刪除結點(node)的下一個結點(node.Next)
			node.Prev.Next = node.Next
			// 2.將待刪除結點(node)的後一個結點(node.Next)的前引地址域指向待刪除結點的前一個結點
			node.Next.Prev = node.Prev

			// 3.置爲nil,方便GC回收
			// 將待刪除結點的數據置爲nil
			node.Data = nil
			// 將待刪除結點的前引指針域置爲nil
			node.Prev = nil
			// 將待刪除結點的後引指針域置爲nil
			node.Next = nil
			// 將待刪除結點置爲nil
			node = nil
			break
		}
		// 偏移到下一個結點
		node = node.Next
	}
}

// 銷燬雙向鏈表
func (node *LinkNode) Destroy() {
	// 容錯處理
	if node == nil {
		return
	}
	// 遞歸調用
	node.Next.Destroy()
	// 置nil
	node.Data = nil
	node.Prev = 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()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章