对数组和链表的一些理解

本文详细介绍了数组和链表两种数据结构,分析了它们的内存分配、优缺点以及操作效率。数组提供随机访问且访问速度快,但插入和删除操作相对较慢;链表则在插入和删除上具有优势,但访问速度慢。数组从0开始编号是为了优化性能,减少一次减法操作。链表则由非连续内存空间组成,适合动态内存分配。两者的对比突显了各自在不同场景下的适用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

是什么?
数组是用一组连续的内存空间,来存储一组具有相同类型的数据的线性表

优缺点:
优点:
随机访问速度快,时间复杂度是O(1)

缺点:

  • 删除、插入速度慢,因为需要从前往后遍历到指定位置,时间复杂度是O(n)

  • 需要事先设置内存空间大小
    因为数组的空间在编译阶段就需要进行确定(在运行阶段是不允许改变的)
    所以在使用前需要提前申请所占内存的大小,如果提前不知道需要的空间大小,预先申请就可能会浪费内存空间,即数组的空间利用率较低

插入时间复杂度分析(删除和插入一样)

  • 末尾插入元素,那就不需要移动数据了,这时的时间复杂度为 O(1)

  • 在数组的开头插入元素,那所有的数据都需要依次往后移动一位,所以最坏时间复杂度是 O(n)

  • 因为我们在每个位置插入元素的概率是一样的,所以平均时间复杂度(1+2+...n)/n=O(n)

为什么数组从 0 开始编号?

首先说结论:为了极致的性能优化

下面进行分析:
从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移”。a[0]就是偏移为 0 的位置,也就是首地址,a[k]就表示偏移 k 的位置

在这里插入图片描述

对比两个公式,我们不难发现,从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令
数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从 0 开始编号,而不是从 1 开始。

链表

是什么?
链表是可以用一组非连续的内存空间,来存储一组具有相同类型数据的线性表
数据元素的逻辑顺序是通过链表中的指针链接实现的。

链表由一系列结点组成,结点可以在运行时动态生成。每个结点包括两个部分:

  • 一个是存储数据元素的数据域
  • 另一个是存储下一个结点地址的指针域

数组和链表的对比

内存分配

  • 数组的大小固定,一经声明就要占用整块连续内存空间。

  • 链表本身没有大小的限制,但并不是无限的

2)连续

  • 数组是用一块连续的内存空间,来存储相同类型的一组数据。
    随机访问快,增删慢
    可以借助 CPU 的缓存机制+空间局部性原理,预读数组中的数据,所以访问效率更高

  • 链表在内存中并不是连续存储
    访问慢,增删快
    对 CPU 缓存不友好,没办法有效预读。

<think>好的,用户现在想要一个用Java实现归并排序的示例。首先,我需要回顾一下之前用户的对话历史,确保这次回答符合他们的需求。之前的对话中,用户已经询问过归并排序二分排序的区别,我给出了详细的解释,包括算法原理、时间复杂度代码示例。现在用户需要具体的Java数组归并排序示例,可能希望看到完整的代码,而不仅仅是核心方法。 用户可能是一个正在学习排序算法的学生,或者是一个需要在实际项目中应用归并排序的开发者。他们可能需要一个可以直接运行测试的示例,以便更好地理解归并排序的实现过程。因此,我应该提供一个完整的Java类,包括main方法,这样用户可以复制代码并运行,观察结果。 接下来,我需要确保代码的正确性可读性。归并排序通常分为分割合并两个步骤。分割部分需要递归地将数组分成两半,直到每个子数组只有一个元素。合并部分需要将两个有序数组合并成一个有序数组。在之前的回答中,我已经给出了mergeSortmerge方法的框架,现在需要补充完整,确保合并逻辑正确。 要注意代码中的边界条件,比如递归终止条件left < right是否正确,合并时临时数组的创建复制是否正确。同时,需要处理数组下标,避免越界错误。例如,在merge方法中,临时数组temp的使用,以及如何将临时数组中的数据复制回原数组。 另外,用户可能需要注释来理解代码的每一步。因此,在代码中添加中文注释是有必要的,尤其是对关键步骤,比如分割点mid的计算、递归调用mergeSort,以及合并过程中的元素比较复制。 还需要考虑示例数组的选择,应该用一个简单的无序数组,比如{8, 4, 5, 7, 1, 3, 6, 2},这样用户能清晰地看到排序前后的变化。在main方法中,打印排序前后的数组,方便用户验证结果。 同时,需要指出归并排序的时间复杂度是O(n log n),并且是稳定排序。但空间复杂度较高,需要O(n)的额外空间。这些信息在之前的对比中已经提到,但可能在示例中再次强调,帮助用户理解适用场景。 最后,检查代码是否符合Java语法规范,比如方法调用的正确性,数组的索引是否正确,System.arraycopy的使用是否正确。确保提供的代码没有语法错误,用户可以直接运行。 总结一下,我需要提供一个完整的Java类,包含main方法,详细的注释,正确的归并排序实现,以及示例输出。这样用户不仅能得到代码,还能理解每个步骤的作用,并且能够自行测试验证。</think>以下是一个使用 Java 实现归并排序的完整示例代码,包含详细注释示例输出: --- ### **完整代码实现** ```java import java.util.Arrays; public class MergeSortExample { public static void main(String[] args) { int[] arr = {8, 4, 5, 7, 1, 3, 6, 2}; System.out.println("原始数组: " + Arrays.toString(arr)); mergeSort(arr, 0, arr.length - 1); System.out.println("排序后数组: " + Arrays.toString(arr)); } // 归并排序入口方法 public static void mergeSort(int[] arr, int left, int right) { if (left < right) { int mid = left + (right - left) / 2; // 防止整数溢出 mergeSort(arr, left, mid); // 递归左半部分 mergeSort(arr, mid + 1, right); // 递归右半部分 merge(arr, left, mid, right); // 合并两个有序子数组 } } // 合并两个子数组的逻辑 private static void merge(int[] arr, int left, int mid, int right) { // 创建临时数组存储合并结果 int[] temp = new int[right - left + 1]; int i = left; // 左子数组起始索引 int j = mid + 1; // 右子数组起始索引 int k = 0; // 临时数组索引 // 比较两个子数组的元素,按顺序放入临时数组 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } // 将剩余元素拷贝到临时数组 while (i <= mid) temp[k++] = arr[i++]; while (j <= right) temp[k++] = arr[j++]; // 将临时数组数据拷贝回原数组 System.arraycopy(temp, 0, arr, left, temp.length); } } ``` --- ### **代码运行结果** ``` 原始数组: [8, 4, 5, 7, 1, 3, 6, 2] 排序后数组: [1, 2, 3, 4, 5, 6, 7, 8] ``` --- ### **关键步骤解析** 1. **分割阶段** (`mergeSort` 方法): - 通过递归将数组不断二分,直到子数组长度为 1。 - 使用 `mid = left + (right - left) / 2` 计算中点,避免整数溢出。 2. **合并阶段** (`merge` 方法): - **创建临时数组**:存储合并后的有序结果。 - **双指针遍历**:比较左右子数组元素,按顺序放入临时数组。 - **处理剩余元素**:将未遍历完的子数组剩余元素直接拷贝。 - **回写原数组**:通过 `System.arraycopy` 将临时数组数据覆盖原数组对应区间。 --- ### **时间复杂度分析** - **分割次数**:共 $\log n$ 层递归。 - **每层合并时间**:每层需遍历 $n$ 个元素。 - **总时间复杂度**:$O(n \log n)$,**稳定高效**,适合大规模数据。 --- ### **实际应用场景** - **大数据排序**:Java 内置的 `Arrays.sort()` 对对象排序时使用类似归并排序的优化算法(如 `TimSort`)。 - **链表排序**:归并排序天然适合链表结构(无需随机访问)。 - **外部排序**:处理超出内存限制的超大文件(如多路归并)。 如果需要进一步优化,可对小规模子数组改用插入排序(减少递归开销),或使用迭代替代递归(避免栈溢出风险)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值