1. 什么是数据结构?
- 逻辑结构:数据元素之间的抽象关系
- 物理结构:数据在计算机内存中的实际存储方式
为什么需要数据结构?
2. 数据结构的物理存储方式
2.1 顺序存储结构
- 相同数据类型的元素集合
- 内存空间连续
- 创建时必须确定大小
- 创建后大小不可变
数组的创建方式
|
创建方式 |
语法示例 |
说明 |
|
指定大小 |
数据类型[] arr = new 数据类型[size]; |
创建指定大小的空数组 |
|
直接初始化 |
数据类型[] arr = {数据1, 数据2, 数据3}; |
创建并初始化数组 |
|
混合方式 |
数据类型[] arr = new 数据类型[]{数据1, 数据2, 数据3}; |
创建并初始化数组 |
示例代码:
int[] arr1 = new int[5]; // [0, 0, 0, 0, 0]
int[] arr2 = {1, 2, 3, 4, 5}; // [1, 2, 3, 4, 5]
float[] arr3 = new float[5]; // 浮点数数组
String[] arr4 = new String[5]; // 字符串数组
数组的存储原理
假设有数组 {5, 7, 4, 9, 0, 3, 1, 6},在内存中的存储形式如下:
|
索引 |
值 |
内存位置(假设int为32位) |
|
0 |
5 |
0~31位 |
|
1 |
7 |
32~63位 |
|
2 |
4 |
64~95位 |
|
3 |
9 |
96~127位 |
|
4 |
0 |
128~159位 |
|
5 |
3 |
160~191位 |
|
6 |
1 |
192~223位 |
|
7 |
6 |
224~255位 |
查找第n个元素的位置公式:起始位置 + n × 数据类型大小
数组操作示例
插入操作:在数组{1, 2, 3, 4, 5}的下标3位置插入6
import java.util.Arrays;
public class ArrayInsert {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int index = 3;
int insert = 6;
// 创建比原数组大1的新数组
int[] newArr = new int[arr.length + 1];
// 复制插入点前的元素
for (int i = 0; i < index; i++) {
newArr[i] = arr[i];
}
// 插入新元素
newArr[index] = insert;
// 复制插入点后的元素
for (int i = index; i < arr.length; i++) {
newArr[i + 1] = arr[i];
}
System.out.println("插入后的数组: " + Arrays.toString(newArr));
// 输出: [1, 2, 3, 6, 4, 5]
}
}
删除操作:删除刚刚插入的元素6
// 续接上面的代码
// 删除刚刚插入的元素6
int deleteIndex = 3;
for (int i = deleteIndex; i < newArr.length - 1; i++) {
newArr[i] = newArr[i + 1];
}
// 创建缩小后的数组
int[] finalArr = new int[newArr.length - 1];
for (int i = 0; i < finalArr.length; i++) {
finalArr[i] = newArr[i];
}
System.out.println("删除后的数组: " + Arrays.toString(finalArr));
// 输出: [1, 2, 3, 4, 5]
2.2 链式存储结构
- 可以随时增加、插入数据
- 不需要预先指定大小
- 当链表过长时,查找时间复杂度会增加至O(n)
3. 算法与时间复杂度
3.1 什么是算法?
- 累加法求1+2+3+...+n
- 等差数列求和公式:S = n(n+1)/2
3.2 时间复杂度分析
常见时间复杂度关系
|
数学关系 |
时间复杂度 |
说明 |
|
y = a |
O(1) |
常数时间复杂度 |
|
y = ax + b |
O(n) |
线性时间复杂度 |
|
y = ax² + bx + c |
O(n²) |
平方时间复杂度 |
|
y = 2^x |
O(log n) |
对数时间复杂度 |
|
y = n log n |
O(n log n) |
线性对数时间复杂度 |
时间复杂度比较
时间复杂度从小到大排序:
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!)
3.3 时间复杂度计算示例
示例1:循环嵌套
int sum = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= n; j++) {
sum++;
}
}
时间复杂度:O(n²)
示例2:二分查找
// 二分查找的时间复杂度分析
查找次数: 数据范围:
第1次查找: n个元素
第2次查找: n/2个元素
第3次查找: n/4个元素
...
第k次查找: n/2^(k-1)个元素
当 n/2^(k-1) = 1 时,k = log₂n + 1
时间复杂度:O(log n)
示例3:递归算法
T(n) = { 1, n = 1
{ 2T(n/2) + n, n > 1
通过分析可得:时间复杂度:O(n log n)
3.4 真题解析
题目1:
x = 0;
while (n > (x+1)*(x+1))
x = x+1;
答案:O(n^(1/2)) - 循环次数为√n级别
题目2:
int sum = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= n; j++)
sum++;
4. 提高查询效率的方法
4.1 排序+二分查找
- 先对数组排序,然后使用二分查找
- 前提:数组内数据必须有序
- 时间复杂度:O(log n)
4.2 利用数组特性
- 通过下标直接获取数据:O(1)
- 缺点:空间浪费,大小不可变
4.3 哈希表(拉链法)
- 结合数组和链表的优点
- 可以随时增加数据
- 最坏情况下时间复杂度退化为O(n)
4.4 树结构
有序二叉树特性:
- 左子树的所有节点值比父节点小
- 右子树的所有节点值比父节点大
- 数据无序时查询时间复杂度:O(log n)

代码:
graph TD
A[8] --> B[3]
A --> C[10]
B --> D[1]
B --> E[6]
C --> F[9]
C --> G[14]
E --> H[4]
E --> I[7]
时间复杂度分析:
|
数据状态 |
时间复杂度 |
说明 |
|
数据无序 |
O(log n) |
理想情况,树基本平衡 |
|
数据有序 |
O(n) |
退化为链表,性能最差 |
- 数据越无序,有序二叉树的查询效率越高(时间复杂度接近O(log n))
- 数据越有序,有序二叉树的查询效率越低(时间复杂度接近O(n))
- 这种查询方法的时间复杂度不稳定,取决于数据的分布情况
5. 总结
- 数组适合频繁按索引访问、数据量固定的场景
- 链表适合频繁插入删除、数据量变化的场景
- 树结构适合需要高效查找的场景,特别是二叉搜索树和平衡二叉树
- 时间复杂度分析是评估算法效率的重要工具
- 实际应用中需要根据具体需求在时间和空间复杂度之间权衡

被折叠的 条评论
为什么被折叠?



