玩转算法面试第三章——数组中的问题其实最常见
3-1.从二分查找法看如何写出正确的程序
排序: 选择排序;插入排序;归并排序;快速排序
查找:二分查找法
数据结构:栈;队列;堆
- 二分查找法
比数组中查找最大值最小值要难一些
但是没有实现快速排序法那么复杂。
考虑好l和r的区间
区间为闭区间 [ l…r ]:
3-2.改变变量定义,依然可以写出正确的算法
如果l=0,r=n
区间为前闭后开:[l…r)
有个小bug,存在整型溢出
3-3.在LeetCode上解决第一个问题 Move Zeros
思路:
把非0元素填补到前面
后面全填0
代码:
时间复杂度:O(n)
空间复杂度:O(n)
不是原地操作,另外开辟了空间
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
void moveZeroes(vector<int>& nums) {
vector<int> noZeroElement;
for(int i = 0; i < nums.size() ;i++) {
if (nums[i]!=0)
{
noZeroElement.push_back(nums[i]);
}
}
for(int i = 0; i < noZeroElement.size() ; i++){
nums[i] = noZeroElement[i];
}
for(int i = noZeroElement.size(); i < nums.size(); i++)
{
nums[i] = 0;
}
}
};
int main()
{
int arr[] = {1,3,0,4,0,0,5,0,7};
vector<int> vec(arr,arr + sizeof(arr)/sizeof(int));
Solution().moveZeroes(vec);
for (int (i) = 0; (i) < vec.size(); ++(i)) {
cout << vec[i]<< " ";
}
cout << endl;
}
3-4.即使简单的问题,也有很多优化的思路
时间复杂度:O(1)
空间复杂度:O(1)
class Solution1 {
public:
void moveZeroes(vector<int>& nums) {
int k = 0 ; // nums中,[0。。。k)的元素均为非0元素
//遍历到第i个元素后,保证[0…i]中所有非0元素都按照顺序排列在[0…k)中
for(int i = 0 ; i < nums.size() ; i++)
{
if(nums[i] != 0)
{
nums[k++] = nums[i];
}
}
for(int i = k ; i < nums.size() ; i++)
{
nums[i] = 0;
}
}
};
上面是先把非0提到前面然后后面全置位0 ,
我们还可以通过非0元素提前的同时交换0:
class Solution2 {
public:
void moveZeroes(vector<int>& nums) {
int k = 0 ; // nums中,[0。。。k)的元素均为非0元素
//遍历到第i个元素后,保证[0…i]中所有非0元素都按照顺序排列在[0…k)中
for(int i = 0 ; i < nums.size() ; i++)
{
if(nums[i] != 0)
{
//swap( nums[k++] , nums[i]);
//面对特殊的测试用例,避免全0的数组,我们只是扫了一遍指针
if( i != k)
{
swap( nums[k++] , nums[i]);
}
else
{
k++;
}
}
}
}
};
27题
26题
80题
3-5.三路快排partition思路的应用 Sort Color
75题
所有元素的取值只有 0、1、2
计数排序
可以统计每个元素各有多少个,在放回数组
有没有方法只扫描一遍就完成排序操作
三路快速排序
88题
215题
3-6.对撞指针 Two Sum II - Input Array is Sorted
167题
解法如下:通过两个索引,如果小于target就i++
i++
如果大于target
j–
通过两个索引向中间不断前行,在这个路径上找到答案。
前面提到的三路快排也可以理解成对撞指针。
125题
(ctype头文件)
tolower 大写字母转换成小写
isalnum 判断一个字符是否是字母或数字
344题
对撞指针去解决。
345题
原音:a e i o u
11题
在一个数组中找到两个墙,构成的水是最大值。
3-7.滑动窗口 Minimum Size Subarray Sum
上一节所介绍的对撞指针是双索引
还有一种双索引的技术是滑动窗口
209
什么叫子数组(不要求他们联系):
连续子数组:
思考题:
优化其sum和,时间复杂度到On2
滑动窗口算法思路:
不断的移动 j 直到遇到大与s的位置,记录长度s;
在缩小 i 的长度,sum和会少一些
如果和又小于s,又可以 j 往后移动,以此类推。
代码如下:
刚开始窗口里面应该不包含任何元素r = -1
int res = nums.size() + 1 刚开始把初始值设为一个最大值
更新res的最小值。
3-8.在滑动窗口中做记录 Longest Substring Without Repeating Characters
3题
如果j+1位置与 滑动窗口里面重复,当前子串就无法拓展了
在i从左边红色处++,重复的部分已经从数组里去除了。
关键问题是如何判断没有重复的字符呢?
通过一个数组就可以通过O1的时间复杂度判断是否重复
如果当前右边界的字符出现的次数为0,就往后移动一位,把这个数的频率++;
如果右边超出边界,缩小左边的窗口
438题