next数组_数组和链表有啥区别?

相爱相杀好基友——数组与链表

作为线性表的两种存储方式 —— 链表和数组,这对相爱相杀的好基友有着各自的优缺点。接下来,我们梳理一下这两种方式。
逻辑结构
从逻辑结构上来说,这两种数据结构都属于线性表。所谓线性表,就是表中所有元素排排站,除了头尾节点外,其他节点都只有一个前趋,也只有一个后继。
线性表的严格定义:
  • 表中存在唯一的一个被称作“第一个”的数据元素
  • 表中存在唯一的一个被称作“最后一个”的数据元素
  • 除第一个元素以外,集合中的其他元素均有且只有一个前驱
  • 除最后一个元素以外,集合中的其他元素均有且只有一个后继
2d04bd1e000f938516dbb6a7d549fd4d.png
存储方式
数组,所有元素都连续的存储于一段内存中,且每个元素占用的内存大小相同。这使得数组具备了通过下标快速访问数据的能力(通过首元素地址和偏移量可以计算出目标元素的内存地址)。
但连续存储的缺点也很明显,增加容量,增删元素的成本很高,时间复杂度均为 O(n)。
增加数组容量需要先申请一块新的内存,然后复制原有的元素。如果需要的话,可能还要删除原先的内存。
63e507edecff77c94bb7b8c1355b6a00.gif数组扩容

删除元素时需要移动被删除元素之后的所有元素以保证所有元素是连续的。增加元素时需要移动指定位置及之后的所有元素,然后将新增元素插入到指定位置,如果容量不足的话还需要先进行扩容操作。
30d97d5be9beb115d8b3e0b50520af1a.gif数组删除元素

总结一下数组的优缺点:
  • 优点:可以根据偏移实现快速的随机读写。
  • 缺点:扩容,增删元素极慢。
链表,由若干个结点组成,每个结点包含数据域和指针域。结点结构如下图所示:
b876a09da024d02a3b17b2d0b4206a2a.png链表的一个结点

一般来讲,链表中只会有一个结点的指针域为空,该结点为 尾结点,其他结点的指针域都会存储一个结点的内存地址。链表中也只会有一个结点的内存地址没有存储在其他结点的指针域,该结点称为 头结点
ccaf4493babcaeca95fc592c0ae3555a.png内存中的链表

链表的存储方式使得它可以高效的在指定位置插入与删除,时间复杂度均为 O(1)。
在结点 p 之后增加一个结点 q 总共分三步:
  1. 申请一段内存用以存储 q (可以使用内存池避免频繁申请和销毁内存)。
  2. 将 p 的指针域数据复制到 q 的指针域。
  3. 更新 p 的指针域为 q 的地址。
1bb44f175aa85fb8596da0416d1fbda2.png插入新元素

删除结点 p 之后的结点 q 总共分两步:
  1. 将 q 的指针域复制到 p 的指针域。

  2. 释放 q 结点的内存。

19eebc6778c91a1bf091b5067ad8db71.png删除结点
总计一下链表的优缺点:
  • 优点:可以快速的在指定位置增删节点,时间复杂度为O(1)。
  • 缺点:因为节点的内存地址没有规律可循,所以无法像数组那样通过偏移高效的访问节点。
链表的主要代码
#include using namespace std;//定义一个结点模板template<typename T>struct Node {
    T data;
    Node *next;
    Node() : next(nullptr) {}
    Node(const T &d) : data(d), next(nullptr) {}
};//删除 p 结点后面的元素template<typename T>void Remove(Node *p) {if (p == nullptr || p->next == nullptr) {return;
    }auto tmp = p->next->next;delete p->next;
    p->next = tmp;
}//在 p 结点后面插入元素template<typename T>void Insert(Node *p, const T &data) {auto tmp = new Node(data);
    tmp->next = p->next;
    p->next = tmp;
}//遍历链表template<typename T, typename V>void Walk(Node *p, const V &vistor) {while(p != nullptr) {
        vistor(p);
        p = p->next;
    }
}int main() {auto p = new Node<int>(1);
    Insert(p, 2);int sum = 0;
    Walk(p, [&sum](const Node<int> *p) -> void { sum += p->data; });cout <endl;
    Remove(p);
    sum = 0;
    Walk(p, [&sum](const Node<int> *p) -> void { sum += p->data; });cout <endl;return 0;
}

91b05688602bfbc12406236d5c6cb120.png

12c7d46587a3b386e2efceff8b5d460b.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值