数组、链表
📚 数组、链表
数组
分配一块连续的内存,每个元素只需要通过索引就可以访问,效率为
O(1)
.数组进行增删操作时,会涉及大量片段的复制群移元素,效率为O(n)
。链表
这时候用链表可以省去这些操作,它的移动修改操作效率为
O(1)
。同时也是因为这样的结构,要访问链表里的元素变得不那么简单,效率为O(n)
。
单向链表:链表 Linked List,每个元素有value
和next
,next 指向下一个元素。最后一个元素的 next 指向空 None
循环列表:头指针为 Head, 尾指针为 Tail, Tail 的 next 指向 Head
双向链表:有两个指针prev
和next
可以往前走,也可以往后走跳表
跳表对标的是平衡树和二分查找,是一种插入/删除/搜索都是O(log n)
的数据结构。
🤔 解题思路
- 暴力循环
双指针
双指针,是指用两个变量在线性结构上遍历而解决的问题。双指针算法是基于暴力解法的优化。- 对于数组,指两个变量在数组上相向移动解决的问题
- 对于链表,指两个变量在链表上同向移动解决的问题,也称为「快慢指针」问题
- 左右指针夹逼
左右指针分别指向左右两端,根据情况向中间移动。适用于两数之和,三数之和,四数之和, 盛最多水的容器这样的 LeetCode 题目,先对数组进行排序,然后左右夹逼求值。
- 快慢指针
不断维护一维数组的双指针来做一些事情
- 滑动窗口
🏫 经典题目
283.移动零(双指针 - 快慢指针)
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。必须在不复制数组的情况下原地对数组进行操作。
输入:[0,1,0,3,12]
输出: [1,3,12,0,0]
思路:for 循环双指针,i 指针指向遍历的元素,j 指针 0 元素的索引。nums[i] !== 0 时,nums[j] = nums[i],同时让 nums[i] = 0, j++。
套路:不断维护一维数组的双指针来做一些事情
时间复杂度:n
1 | var moveZeroes = function (nums) { |
11. 盛水最多的容器(双指针 - 左右夹击)
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
题解:左右指针夹逼,每次移动短的指针,因为移动长的指针,容器的高度不会增加,宽度会减少,所以面积只会减小。
1 | const maxArea = (height) => { |
15. 三数之和 (双指针 - 左右夹击)
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
题解:数组先排序,三指针,定住一个指针,剩下两个指针左右指针夹逼。
1 | var threeSum = function (nums) { |
239. 滑动窗口的最大值(滑动窗口)
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
1 | const maxSlidingWindow = (nums, k) => { |
3. 无重复字符的最长子串(滑动窗口)
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
滑动窗口
维护一个滑动窗口,窗口内都是没有重复的字符,尽可能去扩大窗口的大小
- 如果当前遍历到的字符从未出现,直接扩大右边界
- 如果当前遍历到的字符出现过,则缩小窗口(左边索引向右移动),继续观察
- 重复 1,2,知道左边索引无法再移动
- 维护 res,更新无重复字符的最长子串
1 | const lengthOfLongestSubstring = (s) => { |