已经坚持四天了,今天没有布置题目,可以总结一下这四天刷过的数组和链表。1.
1. 数组
1.1 理论基础
数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标对应的数据。
需要两点注意的是
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的
正是因为数组在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
数组的元素是不能删的,只能覆盖。
二维数组在内存的空间地址不一定连续,不同编程语言的内存管理不一样:
- 在C/C++/python(NumPy库)/Go/Rust中二维数组在地址空间上是连续;
- Java中的二维数组是数组的数组,行与行之间可能在内存中分散存储,地址不完全连续。
Java中
所以Java的二维数组在内存中不是 3*4
的连续地址空间,而是四条连续的地址空间组成!
1.2 经典题目
二分法
middle = left + (right - left) // 2
注意左闭右闭/左闭右开
- 暴力解法时间复杂度:O(n)
- 二分法时间复杂度:O(logn)
双指针法(快慢指针法)
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 暴力解法时间复杂度:O(n^2)
- 双指针时间复杂度:O(n)
慢指针是为了查找和目标值不等的索引,快指针是为了到数组末尾。
滑动窗口
不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
- 暴力解法时间复杂度:O(n^2)
- 滑动窗口时间复杂度:O(n)
模拟行为
不涉及到什么算法,就是单纯的模拟,注意循环不变量原则
前缀和
前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
链表
2.1 理论基础
链表的分类:
单链表:每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。
双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
循环链表:环状,链表首尾相连。
链表的存储方式:
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
定义链表
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
数组和链表对比
2.2 经典题型
虚拟头节点
链表的基本操作
链表增删改查
反转链表
双指针法/递归法
while cur:
temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur.next = pre #反转
#更新pre、cur指针
pre = cur
cur = temp
删除倒数第N个节点
快慢两个指针,fast先走n+1步,这样同时移动的时候slow指向删除节点的上一个节点(方便做删除操作)
链表相交
求长度,然后调节指针在同一起点上(末尾位置对齐)
环形链表
1. 集合法,记录已经走过的
2. 快慢指针法,首先要想清楚:
- 快指针走两步,慢指针走一步,如果有环,快慢指针一定会在环内相遇
- 如果有环,环的入口位置是多少
- 链表从起点到环的入口距离为 x。
- 环的长度为 r。
- 快慢指针相遇时,慢指针已经移动了 x + y 步,其中 y 是从环入口到相遇点的距离。
- 可得x=k⋅r−y,其中k是整数,从相遇点再走r−y步回到环的起点,而链表起点到环的入口正好也是x步。