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

相关推荐
天才在此2 分钟前
汽车加油行驶问题-动态规划算法(已在洛谷AC)
算法·动态规划
莫叫石榴姐1 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
茶猫_2 小时前
力扣面试题 - 25 二进制数转字符串
c语言·算法·leetcode·职场和发展
肥猪猪爸4 小时前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
readmancynn4 小时前
二分基本实现
数据结构·算法
萝卜兽编程4 小时前
优先级队列
c++·算法
盼海4 小时前
排序算法(四)--快速排序
数据结构·算法·排序算法
一直学习永不止步5 小时前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
Rstln5 小时前
【DP】个人练习-Leetcode-2019. The Score of Students Solving Math Expression
算法·leetcode·职场和发展
芜湖_5 小时前
【山大909算法题】2014-T1
算法·c·单链表