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()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章