一、做一道题
-
首先我们看题 Leetcode 26
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums,使nums的前k个元素包含唯一元素,并按照它们最初在nums中出现的顺序排列。nums的其余元素与nums的大小不重要。 - 返回
k。
-
以下是题解
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
const len = nums.length;
if(len === 0) return 0;
let slow = 1;
let fast = 1;
while(fast < len) {
if(nums[fast] !== nums[fast-1]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
};
逻辑:
- 定义两个指针,快指针和慢指针,开始时都指向数组的第 2 个元素,第一个元素必然不会重复。
- 快指针负责遍历数组,慢指针负责指向最后一个不重复的元素。
- 遍历数组时,当快指针遇到和慢指针不同的元素时,就将该元素复制到慢指针的下一个位置,并将慢指针向前移动一位。
- 当快指针遍历完数组后,慢指针后面的所有位置都是重复元素,可以不考虑。返回慢指针的位置加一,即为非重复元素的数量。
时间复杂度:O(n)因为只需遍历一次数组。
空间复杂度:O(1)因为只是创建了额外的变量。
二、分析数据结构
-
介绍数组
数组是一种基本的数据结构,其特点是元素在内存中连续存储,每个元素可以通过索引直接访问。
-
适合使用数组的情况:
- 需要随机访问:当你需要频繁地通过索引访问元素时,数组是非常合适的,因为访问元素的时间复杂度是 O(1)。
- 元素数量已知:当你提前知道元素的数量,并且这个数量不会发生大的变化时,数组是一个不错的选择。
- 数据量不是特别大:因为数组是连续的内存块,如果数据量特别大,可能会遇到内存分配问题。
- 需要高效的缓存利用率:由于数组元素在内存中是连续存放的,它们可以高效地利用 CPU 缓存。
-
不太适合使用数组的情况:
- 元素数量经常变动:如果经常需要添加或删除元素,数组可能不是最佳选择,因为这样的操作会导致数组的重新分配或者移动大量的元素,从而带来高时间复杂度。
- 需要特殊操作:例如,频繁的插入、删除等操作,链表或其他数据结构可能更有优势。
- 不确定数据量大小:如果不确定会存储多少数据,动态数组(如 Python 中的 list 或 C++ 中的 vector)或其他数据结构可能更合适。
- 需要复杂的数据操作:如频繁查找、排序、合并等,特定的数据结构(如树、哈希表、集合等)可能更有优势。
三、分析算法
-
介绍快慢指针
快慢指针是一种常用的双指针技巧。它涉及到使用两个指针,一个移动得快(通常每次两步),另一个移动得慢(通常每次一步)。
-
适合使用快慢指针的情况:
- 检测循环:在链表中检测是否存在环。快指针每次移动两步,慢指针每次移动一步。如果存在环,两个指针最后就会相遇。
- 找中点:在链表中查找中间的节点。当快指针到末尾的时候,慢指针刚好在中点。
- 数组去重,就如上文的题目一样。
- 有序数组/链表合并:合并两个有序数组或链表时,可以使用两个指针分别指向两个数组/链表的档期那元素,然后逐个比较和合并。
- 查找满足条件的子数组或子串:例如在数组中找到满足某个条件的连续子数组。
-
通常不适合使用的情况
- 需要随机访问:例如需要频繁地查找、插入或删除数组/链表中的某个位置的元素。
- 深度优先搜索或广度优先搜索:这种情况下,通常使用递归或队列来实现。
- 数据结构为树或图:虽然有些树的问题可以用双指针解决(如判断一个树是不是对称的),但大多数树或图的问题不适合使用快慢指针。
-
边界条件
使用快慢指针时,要特别注意边界条件和可能出现的异常情况。例如:
- 快指针是否会超出数组的边界或链表的末尾。
- 是否需要先判断数组或链表是否为空。
- 是否存在其他特殊情况或特殊输入需要处理。
四、深度分析
-
数组的特点
- 技巧:可以直接访问,且空间是连续性的,这使得它能够高效得利用CPU的缓存。
- 流程:初始化时数组需要分配连续的内存块,使用索引读取或写入元素,要访问的时候可以用循环来遍历。
- 学科:数组对于内存管理和算法优化有着重要的作用。
- 本质:数组具有简约性和确定性。
-
快慢指针
- 技巧:利用两个指针速度的差异、相对位置形成一种关系从而解决问题。
- 流程:初始化时设置同一个或不同的起点,根据问题的需要决定移动策略是一快一慢,或者具有固定距离差异,当满足某些条件后终止。
- 学科:快慢指针是解决某些特定问题的算法策略,尤其在解决链表和数组问题时非常有用。
- 本质:通过两个元素的相对动作解决问题,体现了二元的思考模式;通常用于寻找某种平衡或者中点,体现了动态过程中的平衡思想。
五、小结与作业
相信大家对于快慢指针和数组有了一定的理解,有问题可以在评论区探讨。
作业:请解决 leetcode 141 问题