2 数组
2.0 数组题目总结
- 双指针
- 一个遍历,一个指向更新位置
- 覆盖操作
- 如果可以不用在乎某些值,可以直接覆盖,而不是交换。
- 区域指针
- 对问题进行区域划分,使用不同指针指向区域的更新位置。
- 总结声明:
- 总结起来,其实就是区域指针+遍历指针,能解决绝大多数数组题目。
- 涉及到原地操作的,就是要直接覆盖。
2.1 移动0(LC-283)
- 问题描述:给定一个数目nums,编写一个函数将所有0移动到数组末尾,同时保持非0元素的相对位置。(原地操作)
- 解题思路:双指针
- 遍历指针遍历数组,位置指针指向更新位置。(覆盖操作)
- 初始化时,快慢指针都指向0位置。
- 遍历指针遍历数组,当遍历指针指向的数不为0时,和位置指针互换,并让遍历指针指向下一个位置。
- 如果不含0,那么两个指针同时会超过数组长度。
- 如果有0,那么让位置指针指向的位置及以后都赋值为0。
- 时间复杂度O(n),空间复杂度O(1)
2.2 颜色分类(LC-75)
- 问题描述:原地操作数组元素的三种颜色(红、白、蓝),使得数组中相同颜色的相邻,而且保证红、白、蓝的顺序。
- 解题思路:三指针:三个区域的划分使用:两个区域指针(left和right)+遍历指针(index)
- 移动index指针遍历,left初始指向最左边(它左边都是红色),right初始指向最右边(它右边都是蓝色)
- index遍历值:为红色则和left所指交换,为蓝色则和right所指进行交换,非两边则不动。
- 伪代码:
- index = left = 0;right = nums.length-1;
- while:index小于right时
- if:nums[index]等于0
- swap(nums[index],nums[left])
- index++
- right++
- else if2:nums[index]等于1
- index++
- else2 if3:nums[index]等于2
- swap(nums[index],nums[right])
- right–
- if:nums[index]等于0
- 时间复杂度O(n),空间复杂度O(1)
- 衍生问题
- 对于红色来说,因为遍历的起始位置是从红色那头开始,所以无需再判断交换后的值。
- 对于蓝色来说,换过来的值并没有进行检查,所以要停留一轮检查一下。
2.3 删除有序数组中的重复项(LC-26)
- 问题描述:对升序的数组原地删除重复的元素,使得每个元素只出现一次,放回删除后数组的新长度,元素的相对顺序保持一致。
- 解题思路:区域指针+遍历指针(覆盖操作)
- 快指针遍历数组,慢指针指向更新位置。
- 伪代码:
- end=0;cur=1;
- while:cur小于nums.length
- if:nums[end]==nums[cur]
- cur++
- else:
- nums[++end] = nums[cur]
- if:nums[end]==nums[cur]
- 时间复杂度O(n),空间复杂度O(1)
2.4 合并两个有序数组(LC-88)
- 问题描述:对两个有序数组nums1和nums2进行合并,合并在nums1成为一个有序数组。假设nums1为m+n(初始有效元素为m个),nums2为n。
- 解题思路:三指针,一个遍历指针,两个位置指针。
- 从尾部遍历合并
- end1从nums1的有效尾部遍历,end2从nums2的有效尾部遍历。end指向nums1的真正尾部。
- 不断比较end1和end2的大小,大的放入end。
- 最后如果nums2没遍历完,则把nums2剩下的继续放入nums1
- 伪代码:
- end=m+n-1;end1=m-1;end2=n-1
- while:end1和end2都大于等于0
- if:nums1[end1]>nums2[end2]
- nums1[end] = nums1[end1]
- end–
- end1–
- else:
- nums1[end] = nums2[end2]
- end–
- end2–
- if:nums1[end1]>nums2[end2]
- if:end2大于等于0
- while(end2>=0)
- nums1[end] = nums2[end2]
- end–
- end2–
- while(end2>=0)
- 时间复杂度O(m+n),空间复杂度O(m+n)