😀如果对你有帮助的话😊
🌺为博主点个赞吧 👍
👍点赞是对博主最大的鼓励😋
💓爱心发射~💓
数组
704. 二分查找——简单
给定一个 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
二分法
class Solution {
public int search(int[] nums, int target) {
if(target<nums[0]||target>nums[nums.length-1]){
return -1;
}
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = left + ((right-left)/2);
if(target == nums[mid]){
return mid;
}
else if(target>nums[mid]){
left = mid + 1;
}
else if(target < nums[mid]){
right = mid - 1;
}
}
return -1;
}
}
35. 搜索插入位置——简单
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = left + ((right-left)/2);
if(target==nums[mid]){
return mid;
}
if(target>nums[mid]){
left = mid + 1;
}
if(target<nums[mid]){
right = mid - 1;
}
}
return right + 1;
}
}
34. 在排序数组中查找元素的第一个和最后一个位置——中等
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
- 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
解法1
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length == 0) return new int[]{-1,-1};
int l = 0, r = nums.length - 1; //二分范围
while( l < r) //查找元素的开始位置
{
int mid = (l + r )/2;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if( nums[r] != target) return new int[]{-1,-1}; //查找失败
int L = r;
l = 0; r = nums.length - 1; //二分范围
while( l < r) //查找元素的结束位置
{
int mid = (l + r + 1)/2;
if(nums[mid] <= target ) l = mid;
else r = mid - 1;
}
return new int[]{L,r};
}
}
解法2
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftBorder = searchleft(nums,target);
int rightBorder = searchright(nums,target);
if(leftBorder==-2||rightBorder==-2){
return new int[]{-1,-1};
}
if(rightBorder-leftBorder>1){
return new int[]{leftBorder+1,rightBorder-1};
}
return new int[]{-1,-1};
}
public int searchleft(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int leftBorder = -2;
while(left<=right){
int mid = left + ((right-left)/2);
if(target<=nums[mid]){
right = mid - 1;
leftBorder = right;
}else{
left = mid +1;
}
}
return leftBorder;
}
public int searchright(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int rightBorder = -2;
while(left<=right){
int mid = left + ((right-left)/2);
if(target>=nums[mid]){
left = mid + 1;
rightBorder = left;
}else{
right = mid - 1;
}
}
return rightBorder;
}
}
69. x 的平方根 ——简单
给你一个非负整数 x ,计算并返回 x 的 算术平方根
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
牛顿迭代 三行
class Solution {
public int mySqrt(int a) {
long x = a;
while (x * x > a) x = (x + a / x) / 2;
return (int)x;
}
}
二分法
class Solution {
public int mySqrt(int x) {
if(x==0) return 0;
if(x==1) return 1;
int left = 1;
int right = x/2;
while(left < right){
int mid = left + (right - left+1)/2;
if (mid > x/mid ){
right = mid -1;
}else{
left = mid;
}
}
return left;
}
}
367. 有效的完全平方数——简单
给定一个 正整数 num
,编写一个函数,如果 num
是一个完全平方数,则返回 true ,否则返回 false
。
进阶:不要 使用任何内置的库函数,如 sqrt
。
示例 1:
输入:num = 16
输出:true
示例 2:
输入:num = 14
输出:false
1、暴力-long
class Solution {
public boolean isPerfectSquare(int num) {
long x = 1 , y = 1;
while(y<= num){
if(y==num){
return true;
}
++x;
y = x * x;
}
return false;
}
}
2、牛顿
class Solution {
public boolean isPerfectSquare(int num) {
double x0 = num;
while(true){
double x1 = (x0 + num/x0)/2;
if(x0-x1 < 1e-6){
break;
}
x0 = x1;
}
int x = (int) x0;
return x * x == num;
}
}
3、二分法-加上long
class Solution {
public boolean isPerfectSquare(int num) {
int left = 0;
int right = num;
while(left<= right){
int mid = left + (right - left)/2;
long square = (long) mid*mid;
if(square < num){
left = mid + 1;
}else if(square > num){
right = mid -1;
}else{
return true;
}
}
return false;
}
}
27. 移除元素——简单
给你一个数组 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:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
1、通用
class Solution {
public int removeElement(int[] nums, int val) {
int i = 0;
for(int x : nums){
if(x != val){
nums[i++] = x;
}
}
return i;
}
}
2、双指针1
class Solution {
public int removeElement(int[] nums, int val){
int j = nums.length - 1;
for(int i=0 ; i<=j; i++ ){
if(nums[i] == val){
swap(nums, i--, j--);
}
}
return j+1;
}
void swap(int[] nums, int i, int j){
int b = nums[i];
nums[i] = nums[j];
nums[j] = b;
}
}
3、双指针2
class Solution {
public int removeElement(int[] nums, int val) {
int fast = 0;
int slow;
for(slow = 0; fast <nums.length; fast++){
if(val != nums[fast]){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}
26. 删除有序数组中的重复项——简单
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums
的前 k
个位置后返回 k
。
不要使用额外的空间,你必须在 原地
修改输入数组 并在使用 O(1)
额外空间的条件下完成。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
双指针
class Solution {
public int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int a = 0;
int b = 1;
while(b < nums.length){
if(nums[a] != nums[b]){
nums[a+1] = nums[b];
a++;
}
b++;
}
return a+1;
}
}
283. 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
双指针
class Solution {
public void moveZeroes(int[] nums) {
if(nums==null){
return;
}
int slowIndex = 0;
for(int fastIndex=0; fastIndex<nums.length;fastIndex++){
if(nums[fastIndex] != 0){
int n = nums[fastIndex];
nums[fastIndex] = nums[slowIndex];
nums[slowIndex++] = n;
}
}
}
}
844. 比较含退格的字符串——简单
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。
示例 2:
输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 ""。
示例 3:
输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 "c",但 t 仍然是 "b"。
栈
class Solution {
public boolean backspaceCompare(String s, String t) {
return build(s).equals(build(t));
}
public String build(String str){
StringBuffer ret = new StringBuffer(); //new 栈
int length = str.length(); //输入字符串长
// 判断是否为#
for(int i =0; i<length; ++i){
char j = str.charAt(i); //取字符串一位
// 判断是否为#
if(j != '#'){
ret.append(j); // 不为#,入栈
}else{
if(ret.length() > 0){
ret.deleteCharAt(ret.length() - 1);// 为#,出栈一个
}
}
}
// 返回字符串
return ret.toString();
}
}
双指针
class Solution {
public boolean backspaceCompare(String s, String t) {
// 双指针
// 用i,j指针 指向s,t的最后位,从后往前走
// 用skipS,skipT来表示字符串中#的数量
// 先判断i,j大于0,
// 判断字符串中是否为 #,skipS等是否大于0
int i = s.length()-1, j = t.length()-1;
int skipS = 0, skipT = 0;
while(i>=0 || j>=0){
while(i>=0){
if(s.charAt(i) == '#'){
skipS++;
i--;
}else if(skipS>0){
skipS--;
i--;
}else{
break;
}
}
while(j>=0){
if(t.charAt(j) == '#'){
skipT++;
j--;
}else if(skipT>0){
skipT--;
j--;
}else{
break;
}
}
if(i>=0 && j>=0){
if(s.charAt(i) != t.charAt(j)){
return false;
}
}else{
if(i>=0 || j>=0){
return false;
}
}
i--;
j--;
}
return true;
}
}
977. 有序数组的平方——简单
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
暴力
class Solution {
public int[] sortedSquares(int[] nums) {
int a = nums.length;
int[] ans = new int[a]; // 定义数组
// 循环,做平方
for(int i =0; i<a; i++){
ans[i] = nums[i]*nums[i];
}
// 数组排序
Arrays.sort(ans);
return ans;
}
双指针
class Solution {
//双指针
public int[] sortedSquares(int[] nums) {
//定义左指针、右指针、存放的数组、数组指针
int k = nums.length-1;
int left = 0;
int right = nums.length-1;
int[] result = new int[nums.length];
//循环,大的存数组
while(left<=right){
if(nums[left]*nums[left]<nums[right]*nums[right]){
result[k--] = nums[right]*nums[right];
--right;
}else{
result[k--] = nums[left]*nums[left];
++left;
}
}
return result;
}
}
209. 长度最小的子数组——中等
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
滑动窗口
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;// 定义数组左
int sum = 0;// 定义数组右
int result = Integer.MAX_VALUE; // 定义结果初始值
// 判断数组左<右
for(int right=0; right<nums.length; ++right){
sum += nums[right];//数组的和
// 循环找最大长度的数组
while(sum >= target){
result = Math.min(result,right-left+1);
sum -= nums[left++]; // 数组的和
}
}
return result == Integer.MAX_VALUE?0:result;
}
}
904. 水果成篮——中等
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。
类似于滑动窗口的多指针解法
class Solution {
public int totalFruit(int[] fruits) {
if(fruits.length==0) return 0;
int first = 0, second = 0, temp =0, result = 0; //初始化
// 是否有第三种水果
for(int i =0; i<fruits.length; ++i){
// 是否有第三种水果
if(fruits[i] != fruits[first] && fruits[i] != fruits[second]){
// 当都不相等的时候,判断第一种与第二种一样吗
if(first != second){
first = temp;
}
second = i;
}
//更新最大值
result = Math.max(result,i-first+1);
if(fruits[temp] != fruits[i]){
temp = i;
}
}
return result;
}
}
双指针
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;//树的长度
if(n <= 2) return n;
int ans = 2,left = 0,right = 0;
int[] freq = new int[n];//计算篮中每种水果出现的次数。 因为提示中说明了水果的种类数是有限的 0 <= fruits[i] < fruits.length
int count = 0;//计算数量
while(right < n){ //右边界小于总数
freq[fruits[right]]++;//把水果入篮
if(freq[fruits[right]] == 1) count++; //如果等于1,说明第一次进入篮子,种类数+1
right++;//继续遍历树
while(count > 2){//如果当前种类大于2
freq[fruits[left]]--;//从左边开始对篮子里的水果数量-1
if(freq[fruits[left]] == 0) count--;// 等于0说明篮中已经没有fruits[left]水果,count减1
left++;
}
ans = Math.max(ans,right-left); //取窗口最大值,窗口里的值,就是在篮子里水果数量的总数
}
return ans;
}
}
76. 最小覆盖子串——困难(没写呢)
59. 螺旋矩阵 II——中等
给你一个正整数 n
,生成一个包含 1 到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
模拟
class Solution {
public int[][] generateMatrix(int n) {
int l=0, r=n-1, b=n-1, t=0;
int[][] res = new int[n][n];
int num=1, tar = n*n;
while(num <= tar){
for(int i = l; i <= r; i++){
res[t][i] = num++;
}
t++;
for(int i=t; i<=b; i++){
res[i][r] = num++;
}
r--;
for(int i=r; i>=l; i--){
res[b][i] = num++;
}
b--;
for(int i=b; i>=t; i--){
res[i][l] = num++;
}
l++;
}
return res;
}
}
双指针
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n]; //定义二维数组
int x=0,y=0; //循环圈的起始位置
int loop = n/2; //圈循环几次
int mid = n/2; //中间坐标
int offset = 1; //偏移量
int count = 1; //赋值
while(loop>0){
//从左到右
int i = x, j = y; //两个指针
for(; j < y+n-offset; ++j){
result[x][j] = count++;
}
//从上到下
for(; i < x+n-offset; ++i){
result[i][j] = count++;
}
//从右到左
for(; j > y; --j){
result[i][j] = count++;
}
//从下到上
for(; i > x; --i){
result[i][j] = count++;
}
loop--;
x += 1;
y += 1;
offset+=2;
}
if(n%2 == 1){
result[mid][mid] = count++;
}
return result;
}
}
54. 螺旋矩阵—中等
模拟
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<Integer>();
if(matrix==null || matrix.length==0) return res;
int m = matrix.length;
int n = matrix[0].length;
int l=0, r=n-1, t=0, b=m-1;
while(true){
for(int i=l; i<=r; i++){
res.add(matrix[t][i]);
}
t++;
if (t > b) break;
for(int i=t; i<=b; i++){
res.add(matrix[i][r]);
}
r--;
if(l > r) break;
for(int i=r; i>=l; i--){
res.add(matrix[b][i]);
}
b--;
if (t > b) break;
for(int i=b; i>=t; i--){
res.add(matrix[i][l]);
}
l++;
if(l > r) break;
}
return res;
}
}
剑指 Offer 29. 顺时针打印矩阵——简单
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
顺时针打印矩阵
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length==0) return new int[0];
int left = 0, right = matrix[0].length-1; // 左
int top = 0, below = matrix.length-1;
int count = 0;
int[] result = new int[(right+1) * (below+1)];
while(true){
// 从左到右
for(int j = left; j<= right; ++j){
result[count++] = matrix[top][j];
}
if(++top>below) break;
// 从上到下
for(int i = top; i<=below; ++i){
result[count++] = matrix[i][right];
}
if(--right<left) break;
// 从右到左
for(int j = right; j>= left; --j){
result[count++] = matrix[below][j];
}
if(--below<top) break;
// 从下到上
for(int i =below; i>= top; --i){
result[count++] = matrix[i][left];
}
if(++left>right) break;
}
return result;
}
}