理论:
练习:
704. 二分查找
1、题目
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
2、解题
public int Search(int[] nums, int target)
{
int n = nums.Length;
int left = 0;
int right = n - 1;
while(left <= right)
{
int middle = left + (right - left)/2;
if(target == nums[middle])
{
return middle;
}
else if(target < nums[middle])
{
right = middle - 1;
}
else
{
left = middle + 1;
}
}
return -1;
}
3、总结
二分查找(Binary Search)
条件:
顺序存储的有序线性表
思路:
两种写法:左闭右闭和左闭右开,解题代码为左闭右闭
Key:包含target的区间,[left,right]则left = right 有意义;[left,right)则left = right 无意义
27. 移除元素
1、题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) { print(nums[i]);}
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
2、解题
双指针法:
public int RemoveElement(int[] nums, int val)
{
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.Length; fastIndex ++)
{
if(nums[fastIndex] != val)
{
nums[slowIndex] = nums[fastIndex];
slowIndex ++;
}
}
return slowIndex;
}
暴力解法:
嵌套for循环
3、总结
自己做这道题是没有双指针的概念,只是想到构造一个新数组。
977. 有序数组的平方
1、题目
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
2、解题
双指针法:
public class Solution {
public int[] SortedSquares(int[] nums)
{
int[] result = new int[nums.Length];
int i = 0;
int j = nums.Length - 1;
for(int k = nums.Length -1; k >= 0; k --)
{
if(nums[i] * nums[i] >= nums[j] * nums[j] )
{
result[k] = nums[i] * nums[i];
i ++;
}
else
{
result[k] = nums[j] * nums[j];
j --;
}
}
return result;
}
}
3、总结
第一反应是先平方再排序, 时间复杂度为O(n + nlogn)
思考双指针法的时候想不出指针改指向哪里,总是陷入寻找最值的思路,也有意识到两边的一定比中间的平方值大,但没有想到双指针可以相向而行。
209. 长度最小的子数组
1、题目
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
2、解题
public class Solution {
public int MinSubArrayLen(int target, int[] nums)
{
int result = nums.Length + 1;
int sumOut = 0;
for(int i = 0; i < nums.Length; i ++)
{
sumOut += nums[i];
int sumIn = 0;
int min = nums.Length + 1;
if(sumOut >= target)
{
min = i + 1;
for(int j = 0; j < i; j ++) //循环计算的其实是从j+1个元素起到i的子数组的和
{ //因此无法包含从0开始的子数组
sumIn += nums[j];
if(sumOut - sumIn >= target)
{
min = i - j;
}
}
}
if(min <= result)
{result = min;}
}
if(result <= nums.Length)
{
return result;
}
else
{
return 0;
}
}
}
3、总结
1)暴力解法
直觉解法;
易错点:
以为子数组长度为 i - j + 1;
没有考虑从头开始的子数组;
2)滑动窗口
59. 螺旋矩阵II
1、题目
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
2、解题
public class Solution {
public int[][] GenerateMatrix(int n)
{
int[][] nums = new int[n][];
for (int k = 0; k < n; k++)
{
nums[k] = new int[n];
}
int loop = n/2;
int startx = 0;
int starty = 0;
int count = 1;
int offset = 1;
int i;
int j;
while(loop > 0)
{
i = startx;
j = starty;
for(j = starty; j < n - offset; j ++)
{
nums[startx][j] = count;
count ++;
}
for(i = startx; i < n - offset; i ++)
{
nums[i][n - offset] = count;
count ++;
}
for(j = n - offset; j > starty; j --)
{
nums[n - offset][j] = count;
count ++;
}
for(i = n - offset; i > startx; i --)
{
nums[i][starty] = count;
count ++;
}
startx ++;
starty ++;
offset ++;
loop --;
}
if(n%2 == 1)
{
nums[n/2][n/2] = n*n;
}
return nums;
}
}
3、总结
for循环的时候注意区间的开闭范围;