数组 链表 跳表
数组、链表、跳表
Array
Java, C++: int a[100];
Python: list = []
JavaScript: let x = [1, 2, 3]
Java 里面是 ArrayList
直接访问任意一个元素时间复杂度都是O(1)
Array 插入 、删除元素时间复杂度都是O(n),效率偏低
Linked List
Java里面的Linked List是一个双向列表结构
Linked List增加节点
![image-20200803152251310](/Users/janexie/Library/Application Support/typora-user-images/image-20200803152251310.png)
前继节点的next指针指向新节点,新节点的指针指向后一节点,只操作一次,O(1)
Linked List删除节点
![截屏2020-08-03 下午3.24.20](/Users/janexie/Documents/Java/算法/截屏2020-08-03 下午3.24.20.png)
跳过中间节点
Linked List时间复杂度
- prepend O(1) 从头节点增加
- append O(1) 从尾节点增加
- lookup O(n)
- insert O(1)
- delete O(1)
Array 时间复杂度
-
prepend O(1) 从头节点增加
注意:正常情况下数组的prepend操作的时间复杂度是O(n),但是可以进行特殊优化到O(1)。采用的方法是申请稍大一些的内存空间,然后在数组最开始预留一部分空间,然后prepend的操作则是把头下标前移一个位置即可。
-
append O(1) 从尾节点增加
-
lookup O(n)
-
insert O(n)
-
delete O(n)
链表元素有序的时候怎么查找更高效
跳表的特点
注意:只能用于元素有序的情况
所以,跳表(skip list)对标的是平衡树(AVL Tree)和二分查找,是一种 插入/删除/搜索 都是 O(log n)的数据结构。1989年出现。
它最大的优势是原理简单、容易实现、方便扩展、效率更高。因此在一些热门的项目里用来代替平衡树,如Redis、levelDB等。
如何给有序的链表加速
一维的数据结构要加速的话经常采用的方式就是升维,也就是说变成二维。多一层维度就可以多一级信息,多一级的信息就可以帮助你很快地得到以为里面必须挨个走才能走到的那些元素。
添加第一级索引
添加第二级索引
添加多级索引
跳表查询的时间复杂度分析
n/2、n/4、n/8、第k级索引结点的个数就是n/(2^k)
假设索引有h级,最高级的索引有2个节点。n/(2^h) = 2, h=log2(n)-1
现实中跳表的形态
增加和删除的时间复杂度也是log n
跳表的空间复杂度分析
原始链表大小为n,每2个结点抽一个,每层索引的结点数
收敛的数列,空间复杂度是O(n)
小结
- 数组、链表、跳表的原理和实现
- 三者的时间复杂度、空间复杂度 ☆
- 工程
- 核心思想:1.升维 2. 空间换时间
实战题目解析
练习步骤
-
5-10分钟:读题和思考
-
有思路:自己开始做和写代码;不然,马上看题解!
-
默写背诵,熟练
-
自己写,闭卷!
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保证非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
// 1. loop ,move zeros
// 2. 不考虑限制条件的话,新建一个数组
// 3. index操作 技术就是新建一个下标j
class Solution {
public void moveZeros(int[] nums) {
int j = 0;
for (int i = 0; i < nums.length; ++i) {
if (nums[i] !=0) {
nums[j] = nums[i];
if (i != j) {
nums[i] = 0;
}
j++;
}
}
}
}
- 盛水最多的容器