算法复杂度与数据结构:Java 集合篇的第一块基石

算法复杂度与数据结构:Java 集合篇的第一块基石

Java 面试里有一个问题几乎每次都会出现:ArrayList 和 LinkedList 有什么区别?

大部分人的回答是"ArrayList 查询快,LinkedList 增删快"。面试官再追问一句"为什么",很多人就卡住了。

其实答案不在 ArrayList 和 LinkedList 本身,而在更底层的东西------数组是怎么寻址的?链表是怎么找下一个节点的?时间复杂度 O(1) 和 O(n) 到底差了多少?

这些基础概念是整个 Java 集合框架的通用语言。HashMap 为什么用红黑树?ArrayList 扩容为什么是 1.5 倍?HashSet 为什么能去重?全都要回到复杂度分析和数据结构上讲。

这篇文章先把地基打好,后面几篇拆 ArrayList、HashMap 的时候,你会省很多力气。

先回答一个问题:为什么要做复杂度分析?

两个原因。第一,指导你写出性能更优的代码------两个功能一样的算法,数据量一大,O(n log n) 和 O(n²) 的差距可能是"1 秒和 1 小时"的区别。第二,阅读别人代码时能快速评判好坏------看到嵌套 for 循环遍历集合,你立刻知道这个写法可能在数据量大时是个坑。

搞清楚了"为什么",接下来看"是什么"。

时间复杂度:不是精确计时,而是增长趋势

先看一段最常见的代码:

java 复制代码
public int sum(int n) {
    int sum = 0;            // 执行 1 次
    for (int i = 1; i <= n; i++) {
        sum = sum + i;      // 执行 n 次
    }
    return sum;             // 执行 1 次
}

假设每行代码执行耗时一样,比如 1ms,那么总耗时 T(n) = (3n + 3) × 1ms。

但这里有个问题:当 n 很大的时候,那个 +3 和系数 3 根本不左右大局。n = 10000 时,3n = 30000,+3 几乎没影响。

所以时间复杂度描述的不是"代码执行多少毫秒",而是代码执行时间随数据规模增长的变化趋势。用大 O 表示法写出来就是 T(n) = O(n)。

搞定这个概念之后,常见的时间复杂度就是五类,PPT 里给了一句特别好记的口诀:

text 复制代码
常对幂指阶

对应:

  • --- O(1):执行时间不随 n 变化
  • --- O(log n):每轮数据量折半
  • --- O(n):线性增长
  • --- O(n²):嵌套循环
  • --- O(n!):全排列,极少见

一个一个看过去。

O(1) --- 常数阶

java 复制代码
public int test01(int n) {
    int i = 0;
    int j = 1;
    return i + j;
}

没有循环,没有递归,不管 n 多大,执行行数不变。O(1)。

再看一个容易误判的:

java 复制代码
public void test02(int n) {
    int i = 0;
    int sum = 0;
    for (; i < 100; i++) {
        sum = sum + i;
    }
    System.out.println(sum);
}

虽然有个 for 循环,但它只跑 100 次,跟 n 没关系。所以也是 O(1)。

一句话:只要代码的执行时间不随 n 的增大而增大,就是 O(1)

O(n) --- 线性阶

java 复制代码
public int sum(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum = sum + i;
    }
    return sum;
}

n = 100 就执行约 100 次,n = 10000 就执行约 10000 次。O(n)。

O(n²) --- 平方阶

java 复制代码
public int sum2(int n) {
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            sum = sum + i * j;
        }
    }
    return sum;
}

外层 n 次,内层每次 n 次,总共约 n² 次。O(n²)。

O(log n) --- 对数阶

java 复制代码
public void test04(int n) {
    int i = 1;
    while (i <= n) {
        i = i * 2;
    }
}

i 的值变化:1 → 2 → 4 → 8 → 16 → ...,什么时候超过 n?当 2^x = n 时,x = log₂n。所以 O(log n)。

有一个常考变体:

java 复制代码
public void test04(int n) {
    int i = 1;
    while (i <= n) {
        i = i * 10;
    }
}

此时 x = log₁₀n,但大 O 表示法忽略常数底,所以还是 O(log n)。
#mermaid-svg-eW7CGAwxSVlYRtSW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eW7CGAwxSVlYRtSW .error-icon{fill:#552222;}#mermaid-svg-eW7CGAwxSVlYRtSW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eW7CGAwxSVlYRtSW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eW7CGAwxSVlYRtSW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eW7CGAwxSVlYRtSW .marker.cross{stroke:#333333;}#mermaid-svg-eW7CGAwxSVlYRtSW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eW7CGAwxSVlYRtSW p{margin:0;}#mermaid-svg-eW7CGAwxSVlYRtSW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster-label text{fill:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster-label span{color:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster-label span p{background-color:transparent;}#mermaid-svg-eW7CGAwxSVlYRtSW .label text,#mermaid-svg-eW7CGAwxSVlYRtSW span{fill:#333;color:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW .node rect,#mermaid-svg-eW7CGAwxSVlYRtSW .node circle,#mermaid-svg-eW7CGAwxSVlYRtSW .node ellipse,#mermaid-svg-eW7CGAwxSVlYRtSW .node polygon,#mermaid-svg-eW7CGAwxSVlYRtSW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eW7CGAwxSVlYRtSW .rough-node .label text,#mermaid-svg-eW7CGAwxSVlYRtSW .node .label text,#mermaid-svg-eW7CGAwxSVlYRtSW .image-shape .label,#mermaid-svg-eW7CGAwxSVlYRtSW .icon-shape .label{text-anchor:middle;}#mermaid-svg-eW7CGAwxSVlYRtSW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eW7CGAwxSVlYRtSW .rough-node .label,#mermaid-svg-eW7CGAwxSVlYRtSW .node .label,#mermaid-svg-eW7CGAwxSVlYRtSW .image-shape .label,#mermaid-svg-eW7CGAwxSVlYRtSW .icon-shape .label{text-align:center;}#mermaid-svg-eW7CGAwxSVlYRtSW .node.clickable{cursor:pointer;}#mermaid-svg-eW7CGAwxSVlYRtSW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eW7CGAwxSVlYRtSW .arrowheadPath{fill:#333333;}#mermaid-svg-eW7CGAwxSVlYRtSW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eW7CGAwxSVlYRtSW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eW7CGAwxSVlYRtSW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eW7CGAwxSVlYRtSW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eW7CGAwxSVlYRtSW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eW7CGAwxSVlYRtSW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster text{fill:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW .cluster span{color:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eW7CGAwxSVlYRtSW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eW7CGAwxSVlYRtSW rect.text{fill:none;stroke-width:0;}#mermaid-svg-eW7CGAwxSVlYRtSW .icon-shape,#mermaid-svg-eW7CGAwxSVlYRtSW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eW7CGAwxSVlYRtSW .icon-shape p,#mermaid-svg-eW7CGAwxSVlYRtSW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eW7CGAwxSVlYRtSW .icon-shape .label rect,#mermaid-svg-eW7CGAwxSVlYRtSW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eW7CGAwxSVlYRtSW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eW7CGAwxSVlYRtSW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eW7CGAwxSVlYRtSW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} O(1) 常数阶
O(log n) 对数阶
O(n) 线性阶
O(n log n) 线性对数阶
O(n²) 平方阶

从左到右,数据量一大,性能差距是天壤之别。n = 100 万时,O(log n) 约 20 次,O(n) 是 100 万次,O(n²) 直接爆炸。

O(n log n) --- 线性对数阶

java 复制代码
public void test05(int n) {
    int i = 0;
    for (; i <= n; i++) {   // O(n)
        test04(n);           // O(log n)
    }
}

外层 O(n) 套内层 O(log n),所以 O(n log n)。归并排序、快速排序的平均复杂度就是这个级别。

空间复杂度:额外占用的存储空间

空间复杂度描述的是算法在执行过程中额外占用的存储空间与数据规模之间的增长关系。"额外"两个字很关键------输入数据本身占的空间不算。

先看 O(1) 的例子:

java 复制代码
public void test(int n) {
    int i = 0;
    int sum = 0;
    for (; i < n; i++) {
        sum = sum + i;
    }
    System.out.println(sum);
}

这段代码不管 n 多大,只额外申请了两个 int 变量 i 和 sum,空间不随 n 增长。O(1)。

再看 O(n) 的例子:

java 复制代码
void print(int n) {
    int i = 0;
    int[] a = new int[n];  // 额外申请了长度为 n 的数组
    for (i; i < n; ++i) {
        a[i] = i * i;
    }
    for (i = n - 1; i >= 0; --i) {
        System.out.println(a[i]);
    }
}

额外存储空间跟 n 成正比,O(n)。常见的空间复杂度就三种:O(1)、O(n)、O(n²)。对数阶的空间复杂度几乎用不到,比时间复杂度简单得多。
#mermaid-svg-SFBQRsXw4qc73lzL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SFBQRsXw4qc73lzL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SFBQRsXw4qc73lzL .error-icon{fill:#552222;}#mermaid-svg-SFBQRsXw4qc73lzL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SFBQRsXw4qc73lzL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SFBQRsXw4qc73lzL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SFBQRsXw4qc73lzL .marker.cross{stroke:#333333;}#mermaid-svg-SFBQRsXw4qc73lzL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SFBQRsXw4qc73lzL p{margin:0;}#mermaid-svg-SFBQRsXw4qc73lzL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SFBQRsXw4qc73lzL .cluster-label text{fill:#333;}#mermaid-svg-SFBQRsXw4qc73lzL .cluster-label span{color:#333;}#mermaid-svg-SFBQRsXw4qc73lzL .cluster-label span p{background-color:transparent;}#mermaid-svg-SFBQRsXw4qc73lzL .label text,#mermaid-svg-SFBQRsXw4qc73lzL span{fill:#333;color:#333;}#mermaid-svg-SFBQRsXw4qc73lzL .node rect,#mermaid-svg-SFBQRsXw4qc73lzL .node circle,#mermaid-svg-SFBQRsXw4qc73lzL .node ellipse,#mermaid-svg-SFBQRsXw4qc73lzL .node polygon,#mermaid-svg-SFBQRsXw4qc73lzL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SFBQRsXw4qc73lzL .rough-node .label text,#mermaid-svg-SFBQRsXw4qc73lzL .node .label text,#mermaid-svg-SFBQRsXw4qc73lzL .image-shape .label,#mermaid-svg-SFBQRsXw4qc73lzL .icon-shape .label{text-anchor:middle;}#mermaid-svg-SFBQRsXw4qc73lzL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SFBQRsXw4qc73lzL .rough-node .label,#mermaid-svg-SFBQRsXw4qc73lzL .node .label,#mermaid-svg-SFBQRsXw4qc73lzL .image-shape .label,#mermaid-svg-SFBQRsXw4qc73lzL .icon-shape .label{text-align:center;}#mermaid-svg-SFBQRsXw4qc73lzL .node.clickable{cursor:pointer;}#mermaid-svg-SFBQRsXw4qc73lzL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SFBQRsXw4qc73lzL .arrowheadPath{fill:#333333;}#mermaid-svg-SFBQRsXw4qc73lzL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SFBQRsXw4qc73lzL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SFBQRsXw4qc73lzL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SFBQRsXw4qc73lzL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SFBQRsXw4qc73lzL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SFBQRsXw4qc73lzL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SFBQRsXw4qc73lzL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SFBQRsXw4qc73lzL .cluster text{fill:#333;}#mermaid-svg-SFBQRsXw4qc73lzL .cluster span{color:#333;}#mermaid-svg-SFBQRsXw4qc73lzL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SFBQRsXw4qc73lzL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SFBQRsXw4qc73lzL rect.text{fill:none;stroke-width:0;}#mermaid-svg-SFBQRsXw4qc73lzL .icon-shape,#mermaid-svg-SFBQRsXw4qc73lzL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SFBQRsXw4qc73lzL .icon-shape p,#mermaid-svg-SFBQRsXw4qc73lzL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SFBQRsXw4qc73lzL .icon-shape .label rect,#mermaid-svg-SFBQRsXw4qc73lzL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SFBQRsXw4qc73lzL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SFBQRsXw4qc73lzL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SFBQRsXw4qc73lzL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 复杂度分析
时间复杂度
O(1) 常对幂指阶
O(log n)
O(n)
O(n²)
O(n log n)
空间复杂度
O(1)
O(n)
O(n²)

数组:一段连续的内存空间

数组是一种用连续内存空间 存储相同类型数据的线性数据结构。

关键就在"连续"两个字。因为内存地址是连续的,所以给定任何一个下标,可以瞬间算出对应元素的地址。

text 复制代码
数组元素地址 = 首地址 + 索引 × 数据类型大小

比如 int[] array = {22, 33, 88, 66, 55, 25};
array[3] 的地址 = baseAddress + 3 × 4字节 = baseAddress + 12

用一个 mermaid 图来直观理解:
#mermaid-svg-sC9TwLzjIyuSq9Xo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sC9TwLzjIyuSq9Xo .error-icon{fill:#552222;}#mermaid-svg-sC9TwLzjIyuSq9Xo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sC9TwLzjIyuSq9Xo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .marker.cross{stroke:#333333;}#mermaid-svg-sC9TwLzjIyuSq9Xo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sC9TwLzjIyuSq9Xo p{margin:0;}#mermaid-svg-sC9TwLzjIyuSq9Xo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster-label text{fill:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster-label span{color:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster-label span p{background-color:transparent;}#mermaid-svg-sC9TwLzjIyuSq9Xo .label text,#mermaid-svg-sC9TwLzjIyuSq9Xo span{fill:#333;color:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .node rect,#mermaid-svg-sC9TwLzjIyuSq9Xo .node circle,#mermaid-svg-sC9TwLzjIyuSq9Xo .node ellipse,#mermaid-svg-sC9TwLzjIyuSq9Xo .node polygon,#mermaid-svg-sC9TwLzjIyuSq9Xo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .rough-node .label text,#mermaid-svg-sC9TwLzjIyuSq9Xo .node .label text,#mermaid-svg-sC9TwLzjIyuSq9Xo .image-shape .label,#mermaid-svg-sC9TwLzjIyuSq9Xo .icon-shape .label{text-anchor:middle;}#mermaid-svg-sC9TwLzjIyuSq9Xo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .rough-node .label,#mermaid-svg-sC9TwLzjIyuSq9Xo .node .label,#mermaid-svg-sC9TwLzjIyuSq9Xo .image-shape .label,#mermaid-svg-sC9TwLzjIyuSq9Xo .icon-shape .label{text-align:center;}#mermaid-svg-sC9TwLzjIyuSq9Xo .node.clickable{cursor:pointer;}#mermaid-svg-sC9TwLzjIyuSq9Xo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .arrowheadPath{fill:#333333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sC9TwLzjIyuSq9Xo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sC9TwLzjIyuSq9Xo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sC9TwLzjIyuSq9Xo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster text{fill:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo .cluster span{color:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sC9TwLzjIyuSq9Xo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sC9TwLzjIyuSq9Xo rect.text{fill:none;stroke-width:0;}#mermaid-svg-sC9TwLzjIyuSq9Xo .icon-shape,#mermaid-svg-sC9TwLzjIyuSq9Xo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sC9TwLzjIyuSq9Xo .icon-shape p,#mermaid-svg-sC9TwLzjIyuSq9Xo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sC9TwLzjIyuSq9Xo .icon-shape .label rect,#mermaid-svg-sC9TwLzjIyuSq9Xo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sC9TwLzjIyuSq9Xo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sC9TwLzjIyuSq9Xo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sC9TwLzjIyuSq9Xo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存地址
地址: base+0

值: 22

索引: 0
地址: base+4

值: 33

索引: 1
地址: base+8

值: 88

索引: 2
地址: base+12

值: 66

索引: 3
地址: base+16

值: 55

索引: 4
地址: base+20

值: 25

索引: 5

这个寻址公式解释了数组两个最重要的特点:随机访问 O(1) ,和插入删除 O(n)

为什么数组索引从 0 开始?

因为如果从 1 开始,寻址公式就变成了:

text 复制代码
baseAddress + (i - 1) × dataTypeSize

CPU 需要多做一次减法运算。从 0 开始,寻址公式是 baseAddress + i × dataTypeSize,少一条指令。这就是单纯为了性能。

数组操作的时间复杂度一览

操作 时间复杂度 备注
随机查询(按下标) O(1) 寻址公式直接计算
未知索引查找(无序) O(n) 需要遍历
未知索引查找(有序) O(log n) 可以二分查找
插入(尾部) O(1) 不需要移动元素
插入(头部/中部) O(n) 需要移动后续元素
删除(尾部) O(1) 不需要移动元素
删除(头部/中部) O(n) 需要移动后续元素

插入和删除之所以慢,就是因为数组要保持内存连续性,必须挪动后面的元素。
#mermaid-svg-47qDZdjx0EcfPDX2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-47qDZdjx0EcfPDX2 .error-icon{fill:#552222;}#mermaid-svg-47qDZdjx0EcfPDX2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-47qDZdjx0EcfPDX2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-47qDZdjx0EcfPDX2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-47qDZdjx0EcfPDX2 .marker.cross{stroke:#333333;}#mermaid-svg-47qDZdjx0EcfPDX2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-47qDZdjx0EcfPDX2 p{margin:0;}#mermaid-svg-47qDZdjx0EcfPDX2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster-label text{fill:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster-label span{color:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster-label span p{background-color:transparent;}#mermaid-svg-47qDZdjx0EcfPDX2 .label text,#mermaid-svg-47qDZdjx0EcfPDX2 span{fill:#333;color:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 .node rect,#mermaid-svg-47qDZdjx0EcfPDX2 .node circle,#mermaid-svg-47qDZdjx0EcfPDX2 .node ellipse,#mermaid-svg-47qDZdjx0EcfPDX2 .node polygon,#mermaid-svg-47qDZdjx0EcfPDX2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-47qDZdjx0EcfPDX2 .rough-node .label text,#mermaid-svg-47qDZdjx0EcfPDX2 .node .label text,#mermaid-svg-47qDZdjx0EcfPDX2 .image-shape .label,#mermaid-svg-47qDZdjx0EcfPDX2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-47qDZdjx0EcfPDX2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-47qDZdjx0EcfPDX2 .rough-node .label,#mermaid-svg-47qDZdjx0EcfPDX2 .node .label,#mermaid-svg-47qDZdjx0EcfPDX2 .image-shape .label,#mermaid-svg-47qDZdjx0EcfPDX2 .icon-shape .label{text-align:center;}#mermaid-svg-47qDZdjx0EcfPDX2 .node.clickable{cursor:pointer;}#mermaid-svg-47qDZdjx0EcfPDX2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-47qDZdjx0EcfPDX2 .arrowheadPath{fill:#333333;}#mermaid-svg-47qDZdjx0EcfPDX2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-47qDZdjx0EcfPDX2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-47qDZdjx0EcfPDX2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-47qDZdjx0EcfPDX2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-47qDZdjx0EcfPDX2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-47qDZdjx0EcfPDX2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster text{fill:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 .cluster span{color:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-47qDZdjx0EcfPDX2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-47qDZdjx0EcfPDX2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-47qDZdjx0EcfPDX2 .icon-shape,#mermaid-svg-47qDZdjx0EcfPDX2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-47qDZdjx0EcfPDX2 .icon-shape p,#mermaid-svg-47qDZdjx0EcfPDX2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-47qDZdjx0EcfPDX2 .icon-shape .label rect,#mermaid-svg-47qDZdjx0EcfPDX2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-47qDZdjx0EcfPDX2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-47qDZdjx0EcfPDX2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-47qDZdjx0EcfPDX2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数组插入中间位置
找到插入位置 O(1)
向后挪动所有后续元素 O(n)
放入新元素 O(1)
总复杂度 O(n)

数组有序时:二分查找 O(log n)

如果数组已经排好序,查找就不需要从头到尾遍历,可以用二分查找。每次取中间位置的值跟目标值比较,如果目标值更大就舍弃左半边,更小就舍弃右半边。每比较一次,搜索范围缩小一半。
#mermaid-svg-F67XhKcasa690zx4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F67XhKcasa690zx4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F67XhKcasa690zx4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F67XhKcasa690zx4 .error-icon{fill:#552222;}#mermaid-svg-F67XhKcasa690zx4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F67XhKcasa690zx4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F67XhKcasa690zx4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F67XhKcasa690zx4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F67XhKcasa690zx4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F67XhKcasa690zx4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F67XhKcasa690zx4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F67XhKcasa690zx4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F67XhKcasa690zx4 .marker.cross{stroke:#333333;}#mermaid-svg-F67XhKcasa690zx4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F67XhKcasa690zx4 p{margin:0;}#mermaid-svg-F67XhKcasa690zx4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-F67XhKcasa690zx4 .cluster-label text{fill:#333;}#mermaid-svg-F67XhKcasa690zx4 .cluster-label span{color:#333;}#mermaid-svg-F67XhKcasa690zx4 .cluster-label span p{background-color:transparent;}#mermaid-svg-F67XhKcasa690zx4 .label text,#mermaid-svg-F67XhKcasa690zx4 span{fill:#333;color:#333;}#mermaid-svg-F67XhKcasa690zx4 .node rect,#mermaid-svg-F67XhKcasa690zx4 .node circle,#mermaid-svg-F67XhKcasa690zx4 .node ellipse,#mermaid-svg-F67XhKcasa690zx4 .node polygon,#mermaid-svg-F67XhKcasa690zx4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F67XhKcasa690zx4 .rough-node .label text,#mermaid-svg-F67XhKcasa690zx4 .node .label text,#mermaid-svg-F67XhKcasa690zx4 .image-shape .label,#mermaid-svg-F67XhKcasa690zx4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-F67XhKcasa690zx4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-F67XhKcasa690zx4 .rough-node .label,#mermaid-svg-F67XhKcasa690zx4 .node .label,#mermaid-svg-F67XhKcasa690zx4 .image-shape .label,#mermaid-svg-F67XhKcasa690zx4 .icon-shape .label{text-align:center;}#mermaid-svg-F67XhKcasa690zx4 .node.clickable{cursor:pointer;}#mermaid-svg-F67XhKcasa690zx4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-F67XhKcasa690zx4 .arrowheadPath{fill:#333333;}#mermaid-svg-F67XhKcasa690zx4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F67XhKcasa690zx4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F67XhKcasa690zx4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F67XhKcasa690zx4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-F67XhKcasa690zx4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F67XhKcasa690zx4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-F67XhKcasa690zx4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F67XhKcasa690zx4 .cluster text{fill:#333;}#mermaid-svg-F67XhKcasa690zx4 .cluster span{color:#333;}#mermaid-svg-F67XhKcasa690zx4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F67XhKcasa690zx4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-F67XhKcasa690zx4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-F67XhKcasa690zx4 .icon-shape,#mermaid-svg-F67XhKcasa690zx4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F67XhKcasa690zx4 .icon-shape p,#mermaid-svg-F67XhKcasa690zx4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-F67XhKcasa690zx4 .icon-shape .label rect,#mermaid-svg-F67XhKcasa690zx4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F67XhKcasa690zx4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-F67XhKcasa690zx4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-F67XhKcasa690zx4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

在已排序数组中找 55
取中间位置 mid = 3

array3 = 33
55 > 33?
舍弃左半边

搜索范围缩小到 4, 6
取新中间 mid = 5

array5 = 55
55 == 55?
找到,结束

比较次数就是数组长度不断除以 2 直到找到目标,所以时间复杂度 O(log n)。比如 100 万个元素的有序数组,二分查找最多比较约 20 次就能找到,而暴力遍历最坏要 100 万次。这就是为什么在已排序数据上二分查找比线性查找快了几个数量级。

链表:非连续存储,靠指针串联

链表每个结点有两个部分:数据域 (存数据)和指针域(存下一个结点的地址)。

单向链表

#mermaid-svg-jvcNWgKFRnNiwK2U{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jvcNWgKFRnNiwK2U .error-icon{fill:#552222;}#mermaid-svg-jvcNWgKFRnNiwK2U .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jvcNWgKFRnNiwK2U .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jvcNWgKFRnNiwK2U .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jvcNWgKFRnNiwK2U .marker.cross{stroke:#333333;}#mermaid-svg-jvcNWgKFRnNiwK2U svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jvcNWgKFRnNiwK2U p{margin:0;}#mermaid-svg-jvcNWgKFRnNiwK2U .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster-label text{fill:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster-label span{color:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster-label span p{background-color:transparent;}#mermaid-svg-jvcNWgKFRnNiwK2U .label text,#mermaid-svg-jvcNWgKFRnNiwK2U span{fill:#333;color:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U .node rect,#mermaid-svg-jvcNWgKFRnNiwK2U .node circle,#mermaid-svg-jvcNWgKFRnNiwK2U .node ellipse,#mermaid-svg-jvcNWgKFRnNiwK2U .node polygon,#mermaid-svg-jvcNWgKFRnNiwK2U .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jvcNWgKFRnNiwK2U .rough-node .label text,#mermaid-svg-jvcNWgKFRnNiwK2U .node .label text,#mermaid-svg-jvcNWgKFRnNiwK2U .image-shape .label,#mermaid-svg-jvcNWgKFRnNiwK2U .icon-shape .label{text-anchor:middle;}#mermaid-svg-jvcNWgKFRnNiwK2U .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jvcNWgKFRnNiwK2U .rough-node .label,#mermaid-svg-jvcNWgKFRnNiwK2U .node .label,#mermaid-svg-jvcNWgKFRnNiwK2U .image-shape .label,#mermaid-svg-jvcNWgKFRnNiwK2U .icon-shape .label{text-align:center;}#mermaid-svg-jvcNWgKFRnNiwK2U .node.clickable{cursor:pointer;}#mermaid-svg-jvcNWgKFRnNiwK2U .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jvcNWgKFRnNiwK2U .arrowheadPath{fill:#333333;}#mermaid-svg-jvcNWgKFRnNiwK2U .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jvcNWgKFRnNiwK2U .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jvcNWgKFRnNiwK2U .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jvcNWgKFRnNiwK2U .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jvcNWgKFRnNiwK2U .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jvcNWgKFRnNiwK2U .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster text{fill:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U .cluster span{color:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jvcNWgKFRnNiwK2U .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jvcNWgKFRnNiwK2U rect.text{fill:none;stroke-width:0;}#mermaid-svg-jvcNWgKFRnNiwK2U .icon-shape,#mermaid-svg-jvcNWgKFRnNiwK2U .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jvcNWgKFRnNiwK2U .icon-shape p,#mermaid-svg-jvcNWgKFRnNiwK2U .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jvcNWgKFRnNiwK2U .icon-shape .label rect,#mermaid-svg-jvcNWgKFRnNiwK2U .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jvcNWgKFRnNiwK2U .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jvcNWgKFRnNiwK2U .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jvcNWgKFRnNiwK2U :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Node A

data: 22

next: →
Node B

data: 33

next: →
Node C

data: 88

next: →
Node D

data: null

next: null

Java 代码长这样:

java 复制代码
private static class Node<E> {
    E item;
    Node<E> next;

    Node(E element, Node<E> next) {
        this.item = element;
        this.next = next;
    }
}

单向链表的问题很明显:只能往一个方向走。你想找前一个节点,对不起,只能从头再遍历一遍。

双向链表

双向链表加了一个前驱指针 prev,两个方向都能走:

java 复制代码
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

#mermaid-svg-k8m9Um36x33OKTH3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-k8m9Um36x33OKTH3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k8m9Um36x33OKTH3 .error-icon{fill:#552222;}#mermaid-svg-k8m9Um36x33OKTH3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k8m9Um36x33OKTH3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k8m9Um36x33OKTH3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k8m9Um36x33OKTH3 .marker.cross{stroke:#333333;}#mermaid-svg-k8m9Um36x33OKTH3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k8m9Um36x33OKTH3 p{margin:0;}#mermaid-svg-k8m9Um36x33OKTH3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k8m9Um36x33OKTH3 .cluster-label text{fill:#333;}#mermaid-svg-k8m9Um36x33OKTH3 .cluster-label span{color:#333;}#mermaid-svg-k8m9Um36x33OKTH3 .cluster-label span p{background-color:transparent;}#mermaid-svg-k8m9Um36x33OKTH3 .label text,#mermaid-svg-k8m9Um36x33OKTH3 span{fill:#333;color:#333;}#mermaid-svg-k8m9Um36x33OKTH3 .node rect,#mermaid-svg-k8m9Um36x33OKTH3 .node circle,#mermaid-svg-k8m9Um36x33OKTH3 .node ellipse,#mermaid-svg-k8m9Um36x33OKTH3 .node polygon,#mermaid-svg-k8m9Um36x33OKTH3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k8m9Um36x33OKTH3 .rough-node .label text,#mermaid-svg-k8m9Um36x33OKTH3 .node .label text,#mermaid-svg-k8m9Um36x33OKTH3 .image-shape .label,#mermaid-svg-k8m9Um36x33OKTH3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-k8m9Um36x33OKTH3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-k8m9Um36x33OKTH3 .rough-node .label,#mermaid-svg-k8m9Um36x33OKTH3 .node .label,#mermaid-svg-k8m9Um36x33OKTH3 .image-shape .label,#mermaid-svg-k8m9Um36x33OKTH3 .icon-shape .label{text-align:center;}#mermaid-svg-k8m9Um36x33OKTH3 .node.clickable{cursor:pointer;}#mermaid-svg-k8m9Um36x33OKTH3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-k8m9Um36x33OKTH3 .arrowheadPath{fill:#333333;}#mermaid-svg-k8m9Um36x33OKTH3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k8m9Um36x33OKTH3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k8m9Um36x33OKTH3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k8m9Um36x33OKTH3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-k8m9Um36x33OKTH3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k8m9Um36x33OKTH3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-k8m9Um36x33OKTH3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k8m9Um36x33OKTH3 .cluster text{fill:#333;}#mermaid-svg-k8m9Um36x33OKTH3 .cluster span{color:#333;}#mermaid-svg-k8m9Um36x33OKTH3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-k8m9Um36x33OKTH3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-k8m9Um36x33OKTH3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-k8m9Um36x33OKTH3 .icon-shape,#mermaid-svg-k8m9Um36x33OKTH3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k8m9Um36x33OKTH3 .icon-shape p,#mermaid-svg-k8m9Um36x33OKTH3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-k8m9Um36x33OKTH3 .icon-shape .label rect,#mermaid-svg-k8m9Um36x33OKTH3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k8m9Um36x33OKTH3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-k8m9Um36x33OKTH3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-k8m9Um36x33OKTH3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Node A

data: 22

prev: null

next: →
Node B

data: 33

prev: ←

next: →
Node C

data: 88

prev: ←

next: null

双向链表多占内存(多存一个指针),但多了两个关键能力:

  • 给定节点找前驱 O(1),单向链表是 O(n)
  • 给定节点删除自身 O(1),单向链表需要先找到前驱才能删

链表操作的时间复杂度

操作 单向链表 双向链表
查头结点 O(1) O(1)
查尾结点 O(n) 无尾指针 / O(1) 有 O(1)
查中间结点 O(n) O(n)
头结点增删 O(1) O(1)
尾结点增删 O(n) / O(1) O(1)
中间结点增删 O(n) O(n)
给定结点增删 O(n) O(1)

注意最后一行:双向链表的"给定节点增删 O(1)"是很重要的优势。因为 prev 指针让你不需要遍历就能找到前驱。

数组 vs 链表:第一印象

#mermaid-svg-MPwYAJcbDYXYqTVR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MPwYAJcbDYXYqTVR .error-icon{fill:#552222;}#mermaid-svg-MPwYAJcbDYXYqTVR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MPwYAJcbDYXYqTVR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MPwYAJcbDYXYqTVR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MPwYAJcbDYXYqTVR .marker.cross{stroke:#333333;}#mermaid-svg-MPwYAJcbDYXYqTVR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MPwYAJcbDYXYqTVR p{margin:0;}#mermaid-svg-MPwYAJcbDYXYqTVR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster-label text{fill:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster-label span{color:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster-label span p{background-color:transparent;}#mermaid-svg-MPwYAJcbDYXYqTVR .label text,#mermaid-svg-MPwYAJcbDYXYqTVR span{fill:#333;color:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR .node rect,#mermaid-svg-MPwYAJcbDYXYqTVR .node circle,#mermaid-svg-MPwYAJcbDYXYqTVR .node ellipse,#mermaid-svg-MPwYAJcbDYXYqTVR .node polygon,#mermaid-svg-MPwYAJcbDYXYqTVR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MPwYAJcbDYXYqTVR .rough-node .label text,#mermaid-svg-MPwYAJcbDYXYqTVR .node .label text,#mermaid-svg-MPwYAJcbDYXYqTVR .image-shape .label,#mermaid-svg-MPwYAJcbDYXYqTVR .icon-shape .label{text-anchor:middle;}#mermaid-svg-MPwYAJcbDYXYqTVR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MPwYAJcbDYXYqTVR .rough-node .label,#mermaid-svg-MPwYAJcbDYXYqTVR .node .label,#mermaid-svg-MPwYAJcbDYXYqTVR .image-shape .label,#mermaid-svg-MPwYAJcbDYXYqTVR .icon-shape .label{text-align:center;}#mermaid-svg-MPwYAJcbDYXYqTVR .node.clickable{cursor:pointer;}#mermaid-svg-MPwYAJcbDYXYqTVR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MPwYAJcbDYXYqTVR .arrowheadPath{fill:#333333;}#mermaid-svg-MPwYAJcbDYXYqTVR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MPwYAJcbDYXYqTVR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MPwYAJcbDYXYqTVR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MPwYAJcbDYXYqTVR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MPwYAJcbDYXYqTVR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MPwYAJcbDYXYqTVR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster text{fill:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR .cluster span{color:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MPwYAJcbDYXYqTVR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MPwYAJcbDYXYqTVR rect.text{fill:none;stroke-width:0;}#mermaid-svg-MPwYAJcbDYXYqTVR .icon-shape,#mermaid-svg-MPwYAJcbDYXYqTVR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MPwYAJcbDYXYqTVR .icon-shape p,#mermaid-svg-MPwYAJcbDYXYqTVR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MPwYAJcbDYXYqTVR .icon-shape .label rect,#mermaid-svg-MPwYAJcbDYXYqTVR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MPwYAJcbDYXYqTVR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MPwYAJcbDYXYqTVR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MPwYAJcbDYXYqTVR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



选择数据结构
需要频繁随机访问?
数组 / ArrayList
频繁头尾增删?
链表 / LinkedList
看具体场景

数组赢在"查到",链表赢在"增删"。但这个结论有个前提:是指头部或给定节点的增删。如果在中间位置新增,链表需要先遍历到那个位置,遍历本身就是 O(n),增删是 O(1),所以整体还是 O(n)。这种情况下链表并不比数组快。

这个"反常识"在下一篇拆 ArrayList 和 LinkedList 的时候会详细展开。

复杂度口诀

再回头看一眼 PPT 给的记忆口诀:

text 复制代码
时间复杂度:常对幂指阶
空间复杂度:O(1)、O(n)、O(n²),其他几乎用不到

数组:查 O(1),增删 O(n),内存连续,寻址公式 baseAddress + i × dataSize
链表:查 O(n),头增删 O(1),内存不连续,靠指针串联
双向链表:多了 prev 指针,给定节点增删 O(1),但更占内存

这些是最底层的数据结构,ArrayList 底层是数组,LinkedList 底层是双向链表,HashMap 底层是数组+链表+红黑树。搞清楚这些,后面看源码会轻松很多。

面试怎么答

如果面试官问"讲一下你对数据结构和复杂度的理解",可以这样说:

常见的数据结构中,数组是一段连续的内存空间,支持按下标随机访问 O(1),寻址公式是首地址加索引乘元素大小。因为需要保持内存连续性,插入和删除中间元素需要移动数据,平均 O(n)。数组索引从 0 开始是为了让寻址公式少一条减法指令。

链表是非连续存储,每个节点存数据和指向下一个节点的指针。单向链表只有一个 next 指针,双向链表多了 prev 指针。链表头尾操作 O(1),查找需要遍历 O(n)。双向链表给定节点删除是 O(1),因为可以通过 prev 直接找到前驱。

时间复杂度描述的是执行时间随数据规模的增长趋势,大 O 表示法忽略低阶、常数和系数。常见复杂度记住"常对幂指阶"就行。空间复杂度描述额外存储空间与数据规模的关系,常见的就是 O(1)、O(n)、O(n²)。

实际开发中,ArrayList 底层是数组,LinkedList 底层是双向链表,HashMap 用了数组加链表加红黑树,每种数据结构的选择都是根据操作特点权衡的结果。

相关推荐
致Great1 小时前
Claude Code 上线 Dynamic Workflows:一句话调度 1000 个子智能体并行干活
java·linux·服务器
一个做软件开发的牛马1 小时前
Java 常用类:String不可变、新时间API与包装类陷阱
java·后端
yurenpai(27届找实习中)2 小时前
redis_点评(25.附件店铺—把数据库里的店铺按【类型分组】,批量导入Redis 的 GEO 地理位置结构)
java·redis·缓存
云烟成雨TD2 小时前
Spring AI Alibaba 1.x 系列【66】Graph 长期记忆
java·人工智能·spring
春日见2 小时前
五分钟入门 强化学习---Q-Learning算法与实现
人工智能·python·深度学习·算法·机器学习·计算机视觉
Javatutouhouduan2 小时前
Java面试大厂真题汇总!
java·java面试·java面试题·后端开发·java编程·java架构师·java八股文
maomao大哥闯天下2 小时前
K8s对象deployment、job、service应用详解
java·容器·kubernetes
闪电悠米2 小时前
黑马点评-优惠券秒杀-05_local_lock_cluster_problem
java·spring boot·redis·缓存
IronMurphy2 小时前
SSM拷打第二讲!!!
java·spring·mybatis