Go语言中的链表与双向链表实现

链表基础

链表是一种由有限元素组成的数据结构,其中每个元素至少使用两个内存空间:一个存储实际数据,另一个存储指向下一个元素的指针,从而形成一个元素序列构成链表。链表的第一个元素称为头结点,而最后一个元素通常被称为尾结点。为了操作链表,保持对头结点的引用非常重要,因为头结点是我们访问整个链表的唯一入口。如果丢失了指向头结点的指针,将无法再次找到链表的其他元素。

链表的操作

在链表中,移除节点时,主要操作是调整要删除节点的前一个节点的指针,使其指向要删除节点的下一个节点。

Go中的链表实现

我们通过Go语言的链表实现来更好地理解链表的操作过程。

package main

import (
    "fmt"
)

// 定义链表节点结构
type Node struct {
    Value int
    Next  *Node
}

// 全局变量root,保存链表的头结点
var root = new(Node)

在以上代码中,定义了链表节点的结构,包含一个Value用于存储节点的值,一个Next指针指向链表的下一个节点。此外,还定义了一个全局变量root,用于保存链表的头结点。

添加节点

链表通常不允许重复元素,并且当链表未排序时,新节点通常添加到链表末尾。以下是添加节点的代码实现:

func addNode(t *Node, v int) int {
    if root == nil {
        t = &Node{v, nil}
        root = t
        return 0
    }
    if v == t.Value {
        fmt.Println("节点已存在:", v)
        return -1
    }
    if t.Next == nil {
        t.Next = &Node{v, nil}
        return -2
    }
    return addNode(t.Next, v)
}

addNode函数中,首先检查链表是否为空,如果为空,则将新节点作为头结点插入。接着,检查链表中是否已有待插入的值,避免重复元素。如果当前节点的Next指针为空,说明已到达链表末尾,便将新节点添加到链表的末尾。若以上情况均不满足,则递归地继续检查下一个节点。

遍历链表

遍历链表的代码如下:

func traverse(t *Node) {
    if t == nil {
        fmt.Println("-> 空链表!")
        return
    }
    for t != nil {
        fmt.Printf("%d -> ", t.Value)
        t = t.Next
    }
    fmt.Println()
}

traverse函数通过循环遍历链表,并输出每个节点的值,直到遍历到最后一个节点。

查找节点与计算链表长度

func lookupNode(t *Node, v int) bool {
    if root == nil {
        t = &Node{v, nil}
        root = t
        return false
    }
    if v == t.Value {
        return true
    }
    if t.Next == nil {
        return false
    }
    return lookupNode(t.Next, v)
}

func size(t *Node) int {
    if t == nil {
        fmt.Println("-> 空链表!")
        return 0
    }
    i := 0
    for t != nil {
        i++
        t = t.Next
    }
    return i
}

lookupNode用于检查链表中是否存在某个值,而size函数用于计算链表的长度,即节点的数量。通过递归或循环,分别遍历链表的每个节点,返回结果。

主函数测试

func main() {
    fmt.Println(root)
    root = nil
    traverse(root)
    addNode(root, 1)
    addNode(root, -1)
    traverse(root)
    addNode(root, 10)
    addNode(root, 5)
    addNode(root, 45)
    traverse(root)
    if lookupNode(root, 100) {
        fmt.Println("节点存在!")
    } else {
        fmt.Println("节点不存在!")
    }
    fmt.Println("链表长度:", size(root))
}

输出结果:

&{0 <nil>}
-> 空链表!
1 -> -1 ->
1 -> -1 -> 10 -> 5 -> 45 ->
节点不存在!
链表长度: 5

链表的优势

链表的优势在于其灵活性和实现的简单性,适合处理各种数据类型。链表在进行顺序查找时效率较高,特别是在动态数据管理(如插入和删除)时优于数组等静态数据结构。此外,链表可以动态增长,且删除节点的操作较为简单,尤其是有序链表。

双向链表

双向链表是一种特殊的链表结构,每个节点不仅有一个指向下一个节点的指针,还有一个指向前一个节点的指针。这样可以实现双向遍历。双向链表的头节点的前一个节点为nil,尾节点的下一个节点也为nil

Go中的双向链表实现

双向链表的节点定义如下:

type Node struct {
    Value    int
    Previous *Node
    Next     *Node
}

该结构体中有两个指针字段:一个指向前一个节点,一个指向下一个节点。

添加节点

func addNode(t *Node, v int) int {
    if root == nil {
        t = &Node{v, nil, nil}
        root = t
        return 0
    }
    if v == t.Value {
        fmt.Println("节点已存在:", v)
        return -1
    }
    if t.Next == nil {
        temp := t
        t.Next = &Node{v, temp, nil}
        return -2
    }
    return addNode(t.Next, v)
}

与单链表类似,双向链表的节点通常添加到链表末尾。

遍历与反向遍历

func traverse(t *Node) {
    if t == nil {
        fmt.Println("-> 空链表!")
        return
    }
    for t != nil {
        fmt.Printf("%d -> ", t.Value)
        t = t.Next
    }
    fmt.Println()
}

func reverse(t *Node) {
    if t == nil {
        fmt.Println("-> 空链表!")
        return
    }
    temp := t
    for t != nil {
        temp = t
        t = t.Next
    }
    for temp.Previous != nil {
        fmt.Printf("%d -> ", temp.Value)
        temp = temp.Previous
    }
    fmt.Printf("%d -> ", temp.Value)
    fmt.Println()
}

reverse函数实现了反向遍历,先遍历到链表末尾,再通过Previous指针反向输出节点值。

双向链表的优势

双向链表相比单链表,优势在于可以进行双向遍历,删除和插入操作更加灵活。如果丢失了头结点的指针,仍可以通过尾节点找到链表的其他节点。但双向链表需要维护两个指针,增加了存储空间的消耗和代码的复杂性。

结语

链表和双向链表作为基础的数据结构,在处理动态数据和顺序查找中具有显著优势。通过对Go语言中链表的实现,读者可以更深入理解如何在实际开发中使用这些数据结构来提高程序的性能和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/875215.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c/c++面试100道

1.一道笔试题解析_哔哩哔哩_bilibili P20&#xff1a;#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER) 1、 offsetof 宏是 C 语言中用于计算结构体成员相对于结构体起始地址的偏移量的宏定义。这个宏的定义如下&#xff1a; #define offsetof(TYPE, …

macOS上谷歌浏览器的十大隐藏功能

谷歌浏览器&#xff08;Google Chrome&#xff09;在macOS上拥有一系列强大而隐蔽的特性&#xff0c;这些功能能显著提高您的浏览体验。从多设备同步到提升安全性和效率&#xff0c;这些被低估的功能等待着被发掘。我们将逐步探索这些功能&#xff0c;帮助您最大化利用谷歌浏览…

09-排序1 排序(C)

这一节&#xff0c;测试各类排序算法的运行速度&#xff08;没有基数排序&#xff08;桶&#xff09; 其实在实际学习中&#xff0c;还是有意义的 给定 n 个&#xff08;长整型范围内的&#xff09;整数&#xff0c;要求输出从小到大排序后的结果。 本题旨在测试各种不同的排序…

【代码随想录训练营第42期 Day57打卡 - 图论Part7 - Prim算法

一、Prim算法 Prim算法是一种贪心算法&#xff0c;用于求解加权无向图的最小生成树问题。其中&#xff0c;最小生成树是指一个边的子集&#xff0c;它连接图中的所有顶点&#xff0c;且边的总权重最小&#xff0c;并且没有形成环。 对于Prim算法的简单了解&#xff0c;这里推…

基于小程序的教学辅助微信小程序设计+ssm(lw+演示+源码+运行)

教学辅助微信小程序 摘 要 随着移动应用技术的发展&#xff0c;越来越多的学生借助于移动手机、电脑完成生活中的事务&#xff0c;许多的传统行业也更加重视与互联网的结合&#xff0c;由于学生学习的压力越来越大&#xff0c;教学辅助是一个非常不错的教育平台&#xff0c;对…

9.12-kubeadm方式安装k8s+基础命令的使用

一、安装环境 编号主机名称ip地址1k8s-master192.168.2.662k8s-node01192.168.2.773k8s-node02192.168.2.88 二、前期准备 1.设置免密登录 [rootk8s-master ~]# ssh-keygen[rootk8s-master ~]# ssh-copy-id root192.168.2.77[rootk8s-master ~]# ssh-copy-id root192.168.2.…

指令——计算机的语言(part 2)

目录 1.1 翻译并执行程序 1.1.1 编译器 1.1.2 汇编器 1.1.3 链接器 1.1.4 加载器 1.1.5 动态链接库 接上一篇文章: 指令——计算机的语言(part 1) 1.1 翻译并执行程序 程序翻译层次图如下: 首先高级语言比如说C&#xff0c;会被编译器编译成汇编语言&#xff0c;然后汇…

Python面试宝典第48题:找丑数

题目 我们把只包含质因子2、3和5的数称作丑数&#xff08;Ugly Number&#xff09;。比如&#xff1a;6、8都是丑数&#xff0c;但14不是&#xff0c;因为它包含质因子7。习惯上&#xff0c;我们把1当做是第一个丑数。求按从小到大的顺序的第n个丑数。 示例 1&#xff1a; 输入…

另类动态规划

前言&#xff1a;一开始我根本想不到这个题目是一个动态规划的题目&#xff0c;而且我一开始的初始状态还写错了 我还忘记了写算法题的基本步骤&#xff0c;先看数据范围&#xff0c;再考虑能不能用动态规划写 题目地址 #include <bits/stdc.h> using namespace std; #de…

3C电子胶黏剂在手机制造方面有哪些关键的应用

3C电子胶黏剂在手机制造方面有哪些关键的应用 3C电子胶黏剂在手机制造中扮演着至关重要的角色&#xff0c;其应用广泛且细致&#xff0c;覆盖了手机内部组件的多个层面&#xff0c;确保了设备的可靠性和性能。以下是电子胶在手机制造中的关键应用&#xff1a; 手机主板用胶&…

浏览器百科:网页存储篇-IndexedDB介绍(十)

1.引言 在现代网页开发中&#xff0c;数据存储需求日益增多和复杂&#xff0c;传统的客户端存储技术如localStorage和sessionStorage已难以满足大型数据的存储和管理需求。为了解决这一问题&#xff0c;HTML5 引入了 IndexedDB&#xff0c;在本篇《浏览器百科&#xff1a;网页…

网络学习-eNSP配置路由器

#PC1网关&#xff1a;192.168.1.254 #PC3网关&#xff1a;192.168.3.254 #PC4网关&#xff1a;192.168.4.254# 注&#xff1a;路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…

IBM中国研发中心撤出:挑战与机遇并存

IBM中国研发中心撤出&#xff1a;挑战与机遇并存 引言 近日&#xff0c;IBM宣布撤出在中国的两大研发中心的消息&#xff0c;引起了广泛关注。这一举动不仅对IBM自身的全球布局产生了影响&#xff0c;也在一定程度上反映了跨国公司在中国市场策略的调整。本文将探讨这一事件背…

keras和tensorflow可用的一组版本

目录 keras版本&#xff1a;3.5.0tensorflow&#xff1a;2.17.0之前的错误导包现在的正确导包 keras版本&#xff1a;3.5.0 tensorflow&#xff1a;2.17.0 之前的错误导包 其实也不是说错误&#xff0c;就是因为文件位置不对&#xff0c;所以VSCode总是有黄色波浪线&#xff0…

只出现一次的数II

只出现一次的数&#xff1a;力扣&#xff08;LeetCode&#xff09;-----只出现一次的数 题目描述 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&am…

C# WinForm:禁用Panel容器滚动条自动移动位置的功能

1.在WinForm项目中新建一个类&#xff1a; 2.类里面的内容&#xff0c;重写Panel的这个方法 3.编译后这个控件就出现在工具箱了 4.然后用这个新Panel控件就好了 5.完事大吉。

Datasheet SHT20芯片的数据手册

Datasheet SHT20芯片的数据手册 I2C读取湿度传感器返回的16位数据。SCL SDA 14位有效&#xff0c;我以为是将后二位删除&#xff0c;实际上看完手册才知道是后二位值无用&#xff0c;不是删除&#xff0c;而是清0&#xff0c;实际上还是16为&#xff0c;知识后二位是0还是1&…

深度学习——基础知识

深度学习的重点在于优化&#xff0c;其中很重要的步骤在于如何调参&#xff0c;会涉及到一些微积分等数学知识。不同于以往接触到的数值运算&#xff0c;深度&#xff08;机器&#xff09;学习都是关于张量Tensor&#xff08;向量&#xff09;的计算&#xff0c;Python中最常用…

leetcode21. 合并两个有序链表

思路&#xff1a; 用一个新链表来表示合并后的有序链表&#xff0c; 每次比较两个链表&#xff0c;将较小的那个结点存储至新链表中 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNone): # self.val val # …

机器学习(西瓜书)第 9 章 聚类

9.1 聚类任务和距离计算 在”无监督学习“中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础.此类学习任务中研究最多、应用最广的是“聚类”(clustering). 聚类试图…