数据结构与算法(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 数据结构与算法详解

相关推荐
Juan_201212 小时前
P1041题解
c++·算法·题解·搜索
晨非辰13 小时前
【数据结构入坑指南】--《层序分明:堆的实现、排序与TOP-K问题一站式攻克(源码实战)》
c语言·开发语言·数据结构·算法·面试
hansang_IR13 小时前
【题解】P2217 [HAOI2007] 分割矩阵 [记忆化搜索]
c++·数学·算法·记忆化搜索·深搜
Voyager_415 小时前
算法学习记录03——二叉树学习笔记:从两道题看透后序位置的关键作用
笔记·学习·算法
我搞slam20 小时前
快乐数--leetcode
算法·leetcode·哈希算法
WWZZ202520 小时前
快速上手大模型:机器学习3(多元线性回归及梯度、向量化、正规方程)
人工智能·算法·机器学习·机器人·slam·具身感知
东方佑21 小时前
从字符串中提取重复子串的Python算法解析
windows·python·算法
西阳未落1 天前
LeetCode——二分(进阶)
算法·leetcode·职场和发展
通信小呆呆1 天前
以矩阵视角统一理解:外积、Kronecker 积与 Khatri–Rao 积(含MATLAB可视化)
线性代数·算法·matlab·矩阵·信号处理
CoderCodingNo1 天前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法