目录
- 反转数组
- 1、倒序压栈
- 2、双指针反转
- 3、总结
- 时间复杂度与空间复杂度对比
- 哪个更好?
- 结论
反转数组
本文将使用两种方式讲述如何高效反转一个数组,同样的算法和概念也适用于字符串的反转。
什么?你说使用一个reverse
函数不就实现了,来人,把这人叉出去🤧。
1、倒序压栈
倒序压栈:通过从后面遍历数组再进行压栈,实现数组反转。
这个方法简单易懂,但时间和空间复杂度都是O(n)
const arr = [9,8,7,6,5,4,3,2,1,0]
// 单指针倒序压栈实现
const reversal = (arr) => {
const newArr = []
for (let i = arr.length - 1; i >= 0; i--) {
newArr.push(arr[i])
}
return newArr
}
2、双指针反转
双指针反转:通过首尾交换来实现数组反转。
时间复杂度是O(n),因为没有用到一个新数组来存储,空间复杂度是O(1)。
这里如果是小的数值,可以采用以下方法交换,如果数值较大或者可能是对象,则需要一个临时变量实现交换。
const reversal2 = (arr) => {
for (let i = 0, mid = Math.floor(arr.length / 2); i < mid; i++) {
const lastIndex = arr.length - i - 1
arr[i] += arr[lastIndex] // 如果数字较大,两者加法可能出现溢出的风险,可以采用临时变量来赋值交换。
arr[lastIndex] = arr[i] - arr[lastIndex]
arr[i] = arr[i] - arr[lastIndex]
}
}
// 细心的同学会发现,上面函数有一个问题,就是改变了原数组,那么我们怎么才能做到不改变原数组同时反转呢?
const reversal2 = (arr) => {
arr = [...arr] // 浅拷贝元素
for (let i = 0, mid = Math.floor(arr.length / 2); i < mid; i++) {
const lastIndex = arr.length - i - 1
arr[i] += arr[lastIndex]
arr[lastIndex] = arr[i] - arr[lastIndex]
arr[i] = arr[i] - arr[lastIndex]
}
return arr
}
3、总结
时间复杂度与空间复杂度对比
- 时间复杂度:两个方法的时间复杂度都是 (O(n)),所以在时间上是相同的。
- 空间复杂度:
- 双指针逆序 是原地算法,不需要额外的空间,因此空间复杂度为 (O(1))。
- 单指针倒序压栈 需要使用栈来存储所有元素,因此空间复杂度为 (O(n)),这比双指针方法消耗更多的内存。
哪个更好?
- 双指针逆序 的空间复杂度明显更好,因为它是原地操作,不需要额外的存储空间。因此,双指针逆序通常是更优的选择,尤其在需要处理大量数据时,能够减少内存消耗。
- 单指针倒序压栈 虽然时间复杂度相同,但它的额外栈空间使得它不适合内存有限的环境或大规模数据操作。
结论
从效率和资源消耗的角度来看,双指针逆序是更好的方法。