数据结构和算法之链表 | 链表介绍(难度级别:简单)

简介: 数据结构和算法之链表 | 链表介绍(难度级别:简单)

与数组一样,链表是一种线性数据结构。与数组不同,链表元素不存储在连续的位置;元素使用指针链接。


image.png


为什么使用链表?


数组可用于存储类似类型的线性数据,但数组有以下限制。

1)数组的大小是固定的:所以我们必须提前知道元素数量的上限。此外,一般而言,分配的内存与使用情况无关,等于上限。

2)在元素数组中插入一个新元素是昂贵的,因为必须为新元素创建房间,并且必须移动现有元素才能创建房间。


例如,在一个系统中,如果我们在数组 id[] 中维护一个已排序的 ID 列表。

id[] = [1000, 1010, 1050, 2000, 2040]。


而如果我们要插入一个新的ID 1005,那么为了保持排序顺序,我们必须将1000之后的所有元素(不包括1000)移动。


除非使用某些特殊技术,否则删除数组的代价也很高。例如,要删除 id[] 中的 1010,必须移动 1010 之后的所有内容。


优于数组的优点


1)动态大小

2)易于插入/删除


缺点:


1)不允许随机访问。我们必须从第一个节点开始按顺序访问元素。所以我们不能用它的默认实现有效地对链表进行二分搜索。在这里阅读。

2)列表的每个元素都需要额外的指针存储空间。

3) 对缓存不友好。由于数组元素是连续的位置,因此存在引用的局部性,而在链表的情况下则不存在。


表示:


链表由指向链表第一个节点的指针表示。第一个节点称为头部。如果链表为空,则头部的值为NULL。


列表中的每个节点至少由两部分组成:


1) 数据

2) 指向下一个节点的指针(或引用)


在 C 中,我们可以使用结构来表示一个节点。下面是一个带有整数数据的链表节点的例子。

在 Java 或 C# 中,LinkedList 可以表示为一个类,而一个 Node 可以表示为一个单独的类。LinkedList 类包含一个 Node 类类型的引用。


第一个简单链表


1.C


//一个链表节点
struct Node {
  int data;
  struct Node* next;
};


2.C++


class Node {
public:
  int data;
  Node* next;
};


3.Java


class LinkedList {
    Node head; // head of the list
    /* 链表节点*/
    class Node {
        int data;
        Node next;
        // 创建新节点的构造函数
        // next  默认初始化为 null
        Node(int d) { data = d; }
    }
}


4.Python


class Node:
  # 初始化节点对象的函数
  def __init__(self, data):
  self.data = data # 分配数据
  self.next = None # 将 next 初始化为 null
class LinkedList: 
  # 初始化链表对象的函数
  def __init__(self):
  self.head = None



5.C#


class LinkedList {
  // 链表的第一个节点(head)
  // 将是 Node 类型的对象(默认为 null)
  Node head;
  class Node {
  int data;
  Node next;
  // 创建新节点的构造函数
  Node(int d) { data = d; }
  }
}



让我们创建一个具有 3 个节点的简单链表。


// 一个示例 C++ 程序来介绍
// 一个链表
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
  int data;
  Node* next;
};
// 程序创建一个简单的链接
// 包含 3 个节点的列表
int main()
{
  Node* head = NULL;
  Node* second = NULL;
  Node* third = NULL;
  // 在堆中分配 3 个节点
  head = new Node();
  second = new Node();
  third = new Node();
  /* 三个块已被动态分配。
  我们有指向这三个块的指针作为头部,
  第二个和第三个
  head   second   third
  |    |    |
  |    |    |
  +---+-----+  +----+----+  +----+----+
  | # | # |  | # | # |  | # | # |
  +---+-----+  +----+----+  +----+----+
# 代表任何随机值。
数据是随机的,因为我们没有分配
什么都还没有 */
  head->data = 1; // 在第一个节点分配数据
  head->next = second; // 将第一个节点与
  // 第二个节点
  /* 数据已分配到第一个的数据部分
  块(头部指向的块)。 接下来
  第一个块的指针指向第二个。
  所以他们两个是有联系的。
  head   second   third
  |    |    |
  |    |    |
  +---+---+  +----+----+  +-----+----+
  | 1 | o----->| # | # |  | # | # |
  +---+---+  +----+----+  +-----+----+  
*/
  // 将数据分配给第二个节点
  second->data = 2;
  // 将第二个节点与第三个节点连接起来
  second->next = third;
  /* 数据已经分配到第二个数据部分
    块(由秒指向的块)。 接下来
    第二个块的指针指向第三个
    堵塞。 所以所有三个块都是链接的。
  head   second   third
  |    |    |
  |    |    |
  +---+---+  +---+---+  +----+----+
  | 1 | o----->| 2 | o-----> | # | # |
  +---+---+  +---+---+  +----+----+  */
  third->data = 3; // 将数据分配给第三个节点
  third->next = NULL;
  /* 数据已分配到第三个数据部分
  块(由第三个指向的块)。 和下一个指针
  第三块的 NULL 表示
  链表在这里终止。
  我们已经准备好了链表。
  head  
    |
    |
  +---+---+  +---+---+  +----+------+
  | 1 | o----->| 2 | o-----> | 3 | NULL |
  +---+---+  +---+---+  +----+------+ 
  请注意,只有头部足以表示
  整个列表。 我们可以遍历完整的
  按照下一个指针列出。*/
  return 0;
}


链表遍历


在前面的程序中,我们创建了一个简单的具有三个节点的链表。让我们遍历创建的列表并打印每个节点的数据。对于遍历,让我们编写一个通用函数 printList() 来打印任何给定的列表。


// 一个用于遍历链表的简单 C++ 程序
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
  int data;
  Node* next;
};
// 此函数打印链表的内容
// 从给定节点开始
void printList(Node* n)
{
  while (n != NULL) {
    cout << n->data << " ";
    n = n->next;
  }
}
// 驱动程序代码
int main()
{
  Node* head = NULL;
  Node* second = NULL;
  Node* third = NULL;
  // 在堆中分配 3 个节点
  head = new Node();
  second = new Node();
  third = new Node();
  head->data = 1; // 在第一个节点分配数据
  head->next = second; // 将第一个节点与第二个节点连接起来
  second->data = 2; // 将数据分配给第二个节点
  second->next = third;
  third->data = 3; // 将数据分配给第三个节点
  third->next = NULL;
  printList(head);
  return 0;
}


目录
相关文章
|
16天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
27 1
|
19天前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
63 4
|
25天前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
51 4
|
17天前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
|
17天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
25天前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
86 23
|
25天前
|
算法
数据结构之蜜蜂算法
蜜蜂算法是一种受蜜蜂觅食行为启发的优化算法,通过模拟蜜蜂的群体智能来解决优化问题。本文介绍了蜜蜂算法的基本原理、数据结构设计、核心代码实现及算法优缺点。算法通过迭代更新蜜蜂位置,逐步优化适应度,最终找到问题的最优解。代码实现了单链表结构,用于管理蜜蜂节点,并通过适应度计算、节点移动等操作实现算法的核心功能。蜜蜂算法具有全局寻优能力强、参数设置简单等优点,但也存在对初始化参数敏感、计算复杂度高等缺点。
57 20
|
17天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
38 5
|
16天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
42 1
|
25天前
|
机器学习/深度学习 算法 C++
数据结构之鲸鱼算法
鲸鱼算法(Whale Optimization Algorithm,WOA)是由伊朗研究员Seyedali Mirjalili于2016年提出的一种基于群体智能的全局优化算法,灵感源自鲸鱼捕食时的群体协作行为。该算法通过模拟鲸鱼的围捕猎物和喷出气泡网的行为,结合全局搜索和局部搜索策略,有效解决了复杂问题的优化需求。其应用广泛,涵盖函数优化、机器学习、图像处理等领域。鲸鱼算法以其简单直观的特点,成为初学者友好型的优化工具,但同时也存在参数敏感、可能陷入局部最优等问题。提供的C++代码示例展示了算法的基本实现和运行过程。
44 0

热门文章

最新文章