数据结构与算法(flutter)之链表(三)

顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。

链表是一种在存储单元上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过链表中的指针链接次序实现。

链表是由一系列的结点组成,结点可以在运行时动态生成。每个结点包含两部分:数据域与指针域。数据域存储数据元素,指针域存储下一结点的指针。

链表与顺序表的对比

链表失去了序列的随机读取优点,同时链表增加了指针域,空间开销也较大,但它对存储空间的使用要相对灵活。

操作 链表 顺序表
访问元素 O(n) O(1)
在头部插入/删除 O(1) O(n)
在尾部插入/删除 O(1) O(1)
在中间插入/删除 O(1) O(n)

注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作:

  • 链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是 O(1)。
  • 顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。

单向链表

单向链表也叫单链表,是链表中最简单的形式。它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。

head 保存首地址,item 存储数据,next 指向下一结点地址。

列如:有一堆数据1,2,3,5,6,7,8,9,11,13,要在11和13之间插入12, 如果用数组,需要将13之后的数据都往后退一位,然后再插入12,这样非常麻烦,但是如果用链表,我就直接在11和13之间插入12就行。

自定义单链表的操作:

  • isEmpty():链表是否为空。
  • length():链表长度。
  • travel():遍历整个链表。
  • add(item):链表头部添加元素。
  • append(item):链表尾部添加元素。
  • insert(index, item):指定位置添加元素。
  • remove(item):删除元素。
  • find(item):元素是否存在。

定义节点

节点的数据结构为数据元素(item)与 指针(next)

kotlin 复制代码
class Node {
  /// 单链表的结点
  var item;
  Node? next;
  Node({this.item, this.next});
}

定义链表

链表需要具有首地址指针head。

ini 复制代码
class SingleLinkList {
  /// 单链表

  /// 指向头节点
  Node? _head;

  /// 链表是否为空
  bool isEmpty() {
    return _head == null;
  }

  /// 链表长度
  int length() {
    /// 初始指针指向_head
    Node? cur = _head;
    int count = 0;
    while (cur != null) {
      count += 1;

      /// 指针下移
      cur = cur.next;
    }

    return count;
  }

  /// 遍历链表
  void travel() {
    Node? cur = _head;
    while (cur != null) {
      print('cur.item: ${cur.item}');
      cur = cur.next;
    }
  }

  /// 链表头部添加元素
  void add(item) {
    final Node node = Node(item: item);

    /// 新节点指针指向头节点
    node.next = _head;

    /// 头节点重新指向该节点
    _head = node;
  }

  /// 链表尾部添加元素
  void append(item) {
    final Node node = Node(item: item);

    /// 先判断链表是否为空, 若是空链表, 则将head指向新节点
    if (isEmpty()) {
      _head = node;
    } else {
      /// 若不为空, 则将尾部节点的next节点指向新节点
      Node? cur = _head;
      while (cur?.next != null) {
        cur = cur?.next;
      }
      cur?.next = node;
    }
  }

  /// 指定位置插入元素
  insert(int index, item) {
    /// index <=0, 插入头部
    if (index <= 0) {
      add(item);
    } else if (index > (length() - 1)) {
      /// 指定位置超过尾部, 尾部插入
      append(item);
    } else {
      /// 指定位置插入

      final Node node = Node(item: item);

      Node? cur = _head;

      /// 循环到需要插入的位置
      for (var _ in List.generate(index - 1, (i) => i)) {
        cur = cur?.next;
      }

      node.next = cur?.next;
      cur?.next = node;
    }
  }

  /// 移除节点
  remove(item) {
    /// 删除节点
    Node? cur = _head;
    Node? pre;
    while (cur != null) {
      if (cur.item == item) {
        /// 头结点就是被删除的节点
        if (pre == null) {
          /// 将头指针指向头结点的后一个节点
          _head = cur.next;
        } else {
          /// 将删除位置前一个节点的next指向删除位置的后一个节点
          pre.next = cur.next;
        }
      } else {
        /// 继续按链表后移节点
        pre = cur;
        cur = cur.next;
      }
    }
  }

  /// 查看节点是否存在,返回布尔值
  bool find(item) {
    Node? cur = _head;

    while (cur != null) {
      if (cur.item == item) {
        return true;
      }
      cur = cur.next;
    }
    return false;
  }
}

双向链表

参考资料

Python 数据结构与算法详解

相关推荐
GreenTea6 小时前
发布 3 天登顶 HN:不生成一个字的模型 Jev,我把它的源码和黑料都扒了一遍
前端·后端·算法
庖丁解牛6 小时前
无穷小不是一个“神秘小数”,而是一场奔向 0 的过程
算法
ZPC82107 小时前
YOLO26 识别串番茄果梗
人工智能·深度学习·算法·机器学习
景熙55237 小时前
14.Java 集合框架从入门到源码万字详解(含 ArrayList/LinkedList/HashMap 源码、泛型通配符、红黑树)
java·开发语言·数据结构·算法
aaaameliaaa7 小时前
结构体 结构体
c语言·笔记·算法
有Li8 小时前
【AI答疑】MR数据预处理步骤
人工智能·笔记·算法·语言模型·医学生
盛世宏博北京9 小时前
多传感器数据融合算法:实现档案库房温湿度全域无死角监测
算法·档案温湿度
AgentMaster9 小时前
零售行业智能客服系统怎么选?3 大场景落地与 4 款平台对比实践
大数据·人工智能·算法
远航计算机10 小时前
客服记录怎么变成 AI 会引用的内容?五步清洗法
大数据·人工智能·算法·aigc
码流子10 小时前
几万路监控怎么接:高速公路视频汇聚平台的架构设计与落地坑点
大数据·人工智能·物联网·算法·架构