单向链表与双向链表

线性表的链式存储

单向链表
链表的操作方法
  1. 创建链表 Create(数据集)
  2. 打印链表 Print()
  3. 获取链表长度 (数据结点的个数,不包含头结点)Length() int
  4. 插入结点:头插法 InsertByHead(数据)
  5. 插入结点:尾插法 InsertByTail(数据)
  6. 按位置插入:InsertByIndex(数据,index)
  7. 按位置删除:DeleteByIndex(index)
  8. 按数据删除:DeleteByData(数据)
  9. 按数据查找:SearchByData(数据) index
  10. 销毁链表:Destroy()
创建链表
// 定义链表的结点
type LinkNode struct {
	Data interface{} // 数据域
	Next *LinkNode   // 指针域  -- 8
}

// 创建链表 
func (node *LinkNode) Create(Data ...interface{}) {
	if node == nil || Data == nil {
		return
	}
	if len(Data) == 0 {
		return
	}
	// 创建头结点
	head := node

	// 循环遍历 Data 依次取出数据创建单向链表
	for _, v := range Data {
		// 创建新结点, 并且初始化
		newNode := new(LinkNode)
		newNode.Data = v
		newNode.Next = nil

		node.Next = newNode		// 将当前的下一结点 赋值为 新结点
		node = node.Next		// 更新 新结点为 当前结点.
	}
	// 将node 赋值为 头结点
	node = head
}
打印链表
// 打印链表 ----递归法
func (node *LinkNode) Print1() {

	if node == nil { // 容错, 也是递归出口
		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 //跳过头结点
		if node.Data != nil {
			fmt.Println(node.Data)
		}
	}
}
获取链表长度
// 获取链表的长度
func (node *LinkNode) Length() int {
	if node == nil {
		return -1
	}
	// 定义一个计数器
	i := 0
	// 循环统计数据结点个数
	for node.Next != nil {
		// 后移node 结点
		node = node.Next
		i++
	}
	return i
}
头插法
// 插入链表结点---头插
func (node *LinkNode) InsertByHead(Data interface{}) {
	if node == nil || Data == nil {
		return
	}
	// 创建新结点, 初始化
	newNode := new(LinkNode)
	newNode.Data = Data
	newNode.Next = nil

	// 将新结点的下一个结点,赋值为 原链表的 第一个结点(node.Next)
	newNode.Next = node.Next

	// 头结点的下一个结点赋值为 新结点
	node.Next = newNode
}
尾插法
// 插入链表结点---尾插
func (node *LinkNode) InsertByTail(Data interface{}) {
	if node == nil || Data == nil {
		return
	}
	// 创建新结点
	newNode := new(LinkNode)
	newNode.Data = Data
	newNode.Next = nil

	// 找到原链表的最后一个结点
	for node.Next != nil {
		node = node.Next
	}

	// 将新结点设置为 尾结点
	node.Next = newNode
}
按位置插入
// 插入链表结点---按位置插
func (node *LinkNode) InsertByIndex(Data interface{}, index int) {
	if node == nil || Data == nil {
		return
	}
	if index < 0 || index > node.Length() {
		return
	}
	// 创建新结点
	newNode := new(LinkNode)
	newNode.Data = Data
	newNode.Next = nil

	// 定义变量,记录插入结点的前一个结点
	preNode := node

	// 找寻待插入位置
	for i := 0; i < index; i++ {
		preNode = node
		node = node.Next
	}

	// 将新结点的下一结点,指向 node
	newNode.Next = node
	// 将 preNode 的下一结点赋值为 新结点
	preNode.Next = newNode

}
// 插入链表结点---按位置插 2
func (node *LinkNode) InsertByIndex2(Data interface{}, index int) {
	if node == nil || Data == nil {
		return
	}
	if index < 0 || index > node.Length() {
		return
	}
	// 如果插入位置在尾部.
	if index == node.Length() {
		node.InsertByTail(Data)
		return
	}

	// 创建新结点
	newNode := new(LinkNode)
	newNode.Data = Data
	newNode.Next = nil

	// 找寻待插入位置
	for i := 0; i < index-1; i++ {
		node = node.Next
	}
	newNode.Next = node.Next
	node.Next = newNode
}
按位置删除
// 按位置删除结点
func (node *LinkNode) DeleteByIndex(index int) {
	if node == nil {
		return
	}
	if index < 0 || index > node.Length() {
		return
	}
	// 定义index 指代结点的前一个结点
	preNode := node

	// 循环找到 index 对应结点,保存在 node 中
	for i := 0; i < index; i++ {
		preNode = node
		node = node.Next
	} // 当循环结束时, prenode 保存 index 的前一个结点, node 保存index 对应的结点

	// 将index 前一个结点的 next 指针,指向 index 后一个结点
	preNode.Next = node.Next

	// 将摘下的结点置空, 促使 GC 回收
	node.Data = nil
	node.Next = nil
	node = nil

}
按数据删除
// 按数据删除结点
func (node *LinkNode) DeleteByData(Data interface{}) {
	if node == nil || Data == nil {
		return
	}
	// 创建遍历,保存待删除结点的前一个结点
	preNode := node

	for node.Next != nil {
		preNode = node
		node = node.Next  // 循序后移
		// 比较数据/类型
		if reflect.TypeOf(node.Data)==reflect.TypeOf(Data) && reflect.DeepEqual(node.Data, Data) {

			preNode.Next = node.Next // 前一个结点,指向后一个结点
			// 置空 ,驱使 GC工作
			node.Data = nil
			node.Next = nil
			node = nil
			return
		}
	}
}
按数据查找结点
// 按数据查找结点
func (node *LinkNode) SearchByData(Data interface{}) int {
	if node == nil || Data == nil {
		return -1
	}
	// 定义计数器 i
	i := 0
	for node.Next != nil {
		i++
		node = node.Next
		if reflect.TypeOf(node.Data)==reflect.TypeOf(Data) && reflect.DeepEqual(node.Data, Data) {
			return i
		}
	}
	return -1
}
销毁链表
// 销毁链表
func (node *LinkNode) Destroy() {
	if node == nil {
		return
	}
	//递归 销毁链表结点
	node.Next.Destroy()

	// 置空结点,促使 GC 工作
	node.Data = nil
	node.Next = nil
	node = nil
}
比较顺序表与链式表
  • 顺序表(切片):内存连续
    • 优点
      • 查找速度快
      • 内存空间占用小
    • 缺点
      • 增删效率低
    • 使用场景
      • 数据查询批量较高,可适当牺牲存储空间
  • 链式表(链表):散乱存储
    • 优点
      • 增删效率高
    • 缺点
      • 查询效率低
      • 需要额外占用内存
    • 使用场景
      • 数据修改(增删)频繁

双向链表

结点数据结构

type LinkNode struct {
    Data interface{}
    Prev *LinkNode
    Next *LinkNode
}

在这里插入图片描述

双向链表方法:
  1. 创建链表 Create(数据)
  2. 打印链表 Print()
  3. 获取结点个数 Length() int
  4. 插入结点 Insert(index, 数据)
  5. 删除结点 Delete(index)
  6. 销毁链表 Destroy()
创建双向链表 Create()
  1. 容错
  2. 创建头结点 head = node
  3. 遍历 Data 参数列表,依次创建新结点。 初始化
    1. newNode.Prev = node
    2. node.Next = newNode
    3. node = node.Next
  4. 还原头结点
// 创建双向链表结点
type LinkNode struct {
	Data interface{} // 数据域
	Prev *LinkNode   // 前引指针域
	Next *LinkNode   // 后继指针域
}

// 创建双向链表
func (node *LinkNode) Create(Data ...interface{}) {
	if node == nil || Data == nil {
		return
	}
	if len(Data) == 0 {
		return
	}
	// 创建头结点
	head := node

	// 循环Data 创建数据结点
	for _, v := range Data {
		// 创建新结点,并初始化
		newNode := new(LinkNode)
		newNode.Data = v
		newNode.Prev = node
		newNode.Next = nil

		node.Next = newNode		// 当前结点的下一个结点 赋值为新结点
		node = node.Next		// 更新新结点为当前结点
	}
	// 还原头结点
	node = head
}

链表打印 Print()
正序打印 – 递归
  1. 容错,递归出口
  2. 判断 node.Data 不为空,打印数据
  3. 递归调用 本函数。
// 双向链表打印 --- 递归 --正向
func (node *LinkNode) Print1() {
	if node == nil { // 容错,递归出口
		return
	}
	if node.Data != nil {
		fmt.Print(node.Data, " ")
	}
	// 递归调用本函数
	node.Next.Print1()
}

倒序打印 – 循环
  1. 容错
  2. 找到链表尾结点,保存在 node 中
  3. 循环,从链表的尾结点开始,使用 prev ,依次向前打印
    1. 判断 node.Data 不为空,打印数据
// 双向链表打印 --- 循环 --反向
func (node *LinkNode) Print2() {
	if node == nil { // 容错
		return
	}
	// 找到链表的尾结点,保存在 node 中
	for node.Next != nil {
		node = node.Next
	}

	// 借助 prev 倒序 打印链表数据
	for node.Prev != nil {
		// 打印数据
		if node.Data != nil {
			fmt.Print(node.Data, " ")
		}
		// node依次前移
		node = node.Prev
	}
}
按位置插入结点 InsertByIndex()
  1. 容错
  2. 定义 preNode 用来保存 index 对应的前一个结点
  3. 循环 移动 node、preNode。 循环结束时, node 保存 index 对应结点。
  4. 创建新结点, 初始化
    1. 数据域初始化
    2. 新结点的 Prev, 是 preNode
    3. 新结点的 Next,是 node
  5. node 的Prev 指向 新结点
  6. preNode 的 Next 指向 新结点。
    在这里插入图片描述
// 按位置插入链表结点
func (node *LinkNode) InsertByIndex(index int, Data interface{}) {
	if node == nil || Data == nil {
		return
	}
	if index < 0 || index > node.Length() {
		return
	}
	// 定义preNode ,保存前一个结点
	preNode := node

	// 循环 找到 index 对应结点, 保存在 node 中
	for i := 0; i < index; i++ {
		preNode = node
		node = node.Next
	}	// 循环结束, node指向index 对应结点, prenode 指向前一个结点

	// 创建新结点
	newNode := new(LinkNode)
	newNode.Data = Data
	newNode.Prev = preNode
	newNode.Next = node

	// node 的 prev指向新结点
	node.Prev = newNode

	// preNode 的 next 是新结点
	preNode.Next = newNode
}
按位置删除结点 DeleteByIndex()
  1. 容错
  2. 定义preNode , 用来保存 index 对应的前一个结点
  3. 循环找到 node 、preNode
  4. 将 preNode 的 Next 指向 node 的 Next
  5. 将 node 的 下一个结点的 Prev 保存 preNode
  6. 置空 摘下的结点,促使GC工作
  7. 判断 如果 index 为 链表的 尾结点时
    1. 将 尾结点(node)的 前一个结点 (preNode)的Next 置空
    2. 将 尾结点 置空 。 促使GC工作

在这里插入图片描述

/ 按位置删除链表结点
func (node *LinkNode) DeleteByIndex(index int) {
	if node == nil  {
		return
	}
	if index < 0 || index > node.Length() {
		return
	}

	l := node.Length()	// 保存长度,防止后续node变化,无法获取链表完整长度。

	// 定义 preNode 记录 index 对应的前一个结点
	preNode := node

	// 循环 移动 preNode 和 node
	for i:=0; i<index; i++ {
		preNode = node
		node = node.Next
	}

	// 如果 index 是最后一个结点
	if index == l{
		preNode.Next = nil

		node.Data = nil
		node.Prev = nil
		node.Next = nil
		node = nil
		return
	} else {
		// 将 preNode 的 next 保存 node 的 下一个结点
		preNode.Next = node.Next

		// 将 node 的一下结点 的 prev 保存 preNode
		node.Next.Prev = preNode

		// 置空摘下的结点
		node.Data = nil
		node.Prev = nil
		node.Next = nil
		node = nil
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值