数据结构基础之链表

简介: 数据结构基础之链表

本节,我们先学习链表的基础知识,再看一个链表的经典应用场景。而且,本节只涉及链表的基础知识和实现,链表相关的高级讲解和面试题目等,请关注公众号 攻城狮客栈  查看,可通过关键字 链表 来获取。

首先,我们来说一下链表和数组的区别,上节我们知道,数组和链表都是线性表,且数组是用连续的内存空间来存储数据的,但链表,在内存上并不是连续的,这既是数组和链表的最大区别。

链表分为单链表、双链表、循环链表、循环双向链表等多种类型。下面我们依次来看一下。

单链表

链表节点,除了存储数据外,还有一个存储下一节点地址的指针,链表的第一个节点称为头结点,记录的是链表的基地址,最后一个节点称为尾节点,指向null.

单链表支持O(1)的时间复杂度内完成链表的插入和删除操作。因为在插入和删除时,只需要确定要删除节点与要插入节点的位置,只操作相邻的节点即可完成。

虽然单链表单独执行插入和删除的时间复杂度是O(1),但是在这之前,我们需要知道插入或删除位置的前一个节点,查找这个节点的过程时间复杂度是O(n)而非O(1),因此要强调的是,单链表支持在O(1)的时间复杂度内完成删除和插入操作,并非整个过程。

 

双向链表

双向链表和单向链表的区别是,除了存储了指向下一个节点的指针外,还存储了指向上一个节点的指针,也就是说,双向链表支持在O(1)的时间复杂度内找到节点的上一个节点。这是单链表不支持的。这也是为什么双向链表虽然占用更多的空间,却在实际开发中更常用的原因。

双向链表适合的场景:

  1. 双向链表某些情况下,插入、删除要比单链表高效,因为,单链表删除结点时,需要先通过遍历获得结点的前驱结点。
  2. 对于一个有序链表,双向链表的按值查询效率比单链表高。因为,可以记录上次查找的位置p,每次查询时,根据要查找的值与p的关系,决定是向前还是向后查找,平均只需查找一半数据

 

循环链表

循环链表的尾节点不是指向null,而是指向链表的头结点。首尾相连,即为循环链表。循环链表的优点是从链表尾到链表头方便,当要处理的问题具有环形结构时,可以考虑用循环链表来处理,比如经典的约瑟夫问题。

双向循环链表

双向循环链表则兼具双向链表和循环链表的优点,如图所示。

和数组相比,链表更适合用于插入、删除操作频繁的场景,查找的时间复杂度较高。

好了到这里,基础知识就讲完了,我们下面来模拟实现以下链表的一些基础操作。

 

这里,我们用单链表来模拟链表的实现。

那么首先,我们先给出节点定义和链表的构造函数

/// <summary>
/// 单链表节点
/// </summary>
public class SNode<T>where T:IComparable<T>
{
    public T Value { get; set; }
    public SNode<T> Next { get; set; }
    public SNode(T val)
    {
        Value = val;
    }
}
public class SLinkedList<T>where T:IComparable<T>
{
    public int Length { get; set; }
    public SLinkedList(params T[] tlist)
    {
        var Head = new SNode<T>(default(T));
        if (tlist == null) return;
        var p = Head;
        int count = 0;
        foreach (var item in tlist)
        {
            SNode<T> node = new SNode<T>(item);
            p.Next = node;
            p = node;
            count++;
            if (count==tlist.Count())
            {
                node.Next = null;
            }
        }
        Length = tlist.Length;
    }
}

我们这里实现在特定位置插入值、删除特定值节点以及打印链表全部值三个方法,其他方法就不一一实现了,若有需要,可以关注公众号,获取完整代码。

首先,我们先来看,如何打印链表的全部节点值,我们知道,链表是一个线性表结构,只要我们知道链表的头节点,那么我们就可以同个next指针逐个遍历,直接遍历完全部节点。现在我们来实现一下:

/// <summary>
/// 打印全部节点
/// </summary>
/// <param name="list">链表头节点</param>
public void PrintAll(SNode<T> list)
{
    while (list!=null)
    {
        Console.WriteLine(list.Value);
        list = list.Next;
    }
}

是不是觉得很简单,没错,链表的操作就是这么简洁,但是,一定要注意指针的位置,在复杂的链表操作中,很容易将指针指来指去,将指针丢失,要杜绝这种情况,就需要深刻理解指针的含义并多写多练。

下面,直接给出单链表的删除和插入的代码,因为前面已经分析过其过程,因此这里就不再赘述了

public int Insert(int index, T val)
{
    if (index<0||index>Length)
    {
        return -1;
    }
    var p = Head;
    int count = 0;
    while (p!=null&&count<index)
    {
        p = p.Next;
        count++;
    }
    SNode<T> node = new SNode<T>(val);
    node.Next = p.Next;
    p.Next = node;
    Length++;
    return 0;
}
//返回值-1:删除失败;0:删除成功。
public int Delete(T val)
{
    var p = Head;
    while (p!=null)
    {
        if (p.Next.Value.Equals(val))
        {
            p.Next = p.Next.Next;
            Length--;
            return 0;
        }
        else
        {
            p = p.Next;
        }
    }
    return -1;
}

链表的基础操作,到此就结束了。关于链表的更多内容,可以通过公众号 攻城狮客栈获取。欢迎提问或提建议,另外,公众号寻找合作伙伴,有意向的同学可以联系我咯,多谢。(๑•ᴗ•๑)

 

相关文章
|
14天前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
31 10
【数据结构】链表从实现到应用,保姆级攻略
|
1月前
|
存储 C语言
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
|
2月前
【数据结构OJ题】环形链表
力扣题目——环形链表
31 3
【数据结构OJ题】环形链表
|
1月前
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
31 4
|
2月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
44 1
【数据结构OJ题】复制带随机指针的链表
|
2月前
【数据结构OJ题】环形链表II
力扣题目——环形链表II
20 1
【数据结构OJ题】环形链表II
|
2月前
【数据结构OJ题】相交链表
力扣题目——相交链表
25 1
【数据结构OJ题】相交链表
|
30天前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
30 0
|
1月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
1月前
|
存储 测试技术
【初阶数据结构篇】双向链表的实现(赋源码)
因为头结点的存在,plist指针始终指向头结点,不会改变。