Leetcode刷题的一些记录(Java)

Leetcode刷题

一、基础理论:

1. 数组:

底层原理

https://programmercarl.com/数组理论基础.html

C++中二维数组在地址空间上是连续的

像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。

所以看不到每个元素的地址情况,这里我以Java为例,也做一个实验。

public static void test_arr() {
    int[][] arr = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}, {9,9,9}};
    System.out.println(arr[0]);
    System.out.println(arr[1]);
    System.out.println(arr[2]);
    System.out.println(arr[3]);
}

输出的地址为:

[I@7852e922
[I@4e25154f
[I@70dea4e
[I@5c647e05

这里的数值也是16进制,这不是真正的地址,而是经过处理过后的数值了,我们也可以看出,二维数组的每一行头结点的地址是没有规则的,更谈不上连续。所以Java的二维数组可能是如下排列的方式:

算法通关数组3

初始化

静态初始化:   
int arr1[]=new int[]{1,2,3}; 也就是  int arr1[]={1,2,3};
数据类型 []数组名=new 数据类型[]{1,2,3~};
简化:数据类型 []数组名={1,2,3~};

动态初始化:
int arr1[]=new int[3];
数据类型 []数组名=new 数据类型[数组长度];

java的数组无论用哪种方法初始化,一旦初始化长度,就不能改变了

常用方法

  • equals
System.out.println(arr1.equals(arr2));
方法返回boolean值,也可以通过Arrays调用该方法
//作用一
int[] arr1={1,9,3,7};
Arrays.sort(arr1);
System.out.println(Arrays.toString(arr1));//[1, 3, 7, 9]
//作用二
int[] arr2={1,9,3,7,2,5};
Arrays.sort(arr2,1,4);//范围包含1不包4
System.out.println(Arrays.toString(arr2));//[1, 3, 9, 7, 2, 5]
  • length方法(无括号):返回数组的长度,arr.length就可获取长度

        int arr[]={1,2,3};
        System.out.println(arr.length);//无括号
    
  • 给数组赋值:通过 fill 方法。

  • 对数组排序:通过 sort 方法,按升序。

  • 比较数组:通过 equals 方法比较数组中元素值是否相等。

  • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。

  • toString方法:返回对象的字符串表现形式,通过Arrays调用

            int arr[]={1,2,3};
            System.out.println(arr.length);//无括号
            String arrStr=Arrays.toString(arr);
            System.out.println(arrStr);
            System.out.println(arrStr.length());//有括号
            for(int i=0;i<arrStr.length();i++){
                System.out.print(arrStr.charAt(i)+" ");
            }
    

    image-20250112112016649

2. 二维数组

初始化

静态初始化:   
int [][] arr=new int [][] {{1,2,3},{4,5,6},{7,8,9}};  也就是 int [][] arr={{1,2,3},{4,5,6},{7,8,9}};
数据类型 []数组名=new 数据类型[]{1,2,3~};
简化:数据类型 []数组名={1,2,3~};

动态初始化:
int [][] arr2=new int [3][3];
数据类型 []数组名=new 数据类型[数组长度];

java的数组无论用哪种方法初始化,一旦初始化长度,就不能改变了

3. 哈希表

哈希表能解决什么问题呢,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。

哈希表1

但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。

而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。

而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

  • 那有同学可能问了,遇到哈希问题我直接都用set不就得了,用什么数组啊。

直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。

Hashset

// 引入 HashSet 类      
import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");  // 重复的元素不会被添加
        System.out.println(sites.contains("Taobao"));
    }
}

Hashmap

// 引入 HashMap 类      
import java.util.HashMap;

public class RunoobTest {
    public static void main(String[] args) {
        // 创建 HashMap 对象 Sites
        HashMap<Integer, String> Sites = new HashMap<Integer, String>();
        // 添加键值对
        Sites.put(1, "Google");
        Sites.put(2, "Runoob");
        Sites.put(3, "Taobao");
        Sites.put(4, "Zhihu");
        System.out.println(Sites);
    }
}

4. 常见数学运算

次方

导入Math类, 调用Math.pow(x, y)方法,其中x为底数,y为指数

import java.lang.Math; // 导入Math类

public class Main {
    public static void main(String[] args) {
        double base = 2; // 定义底数
        double exponent = 3; // 定义指数
        double result = Math.pow(base, exponent); // 计算次方
        System.out.println("2的3次方等于:" + result); // 输出结果
    }
}

绝对值

开根号

取整

数字转换为string

String str = n+"";

num[i]=str.charAt(i)-'0';//各个位的数字

5. 输入输出:

Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
System.out.println(a);//ln 自动换行

import

import java.util.*;

6. String

参考:https://cloud.tencent.com/developer/article/1633054

1)字符修改上的区别(主要)
String:不可变字符串;
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
(2)初始化上的区别,String可以空赋值,后者不行,报错
①String
StringBuffer s = null;
StringBuffer s = “abc”;
②StringBuffer
StringBuffer s = null; //结果警告:Null pointer access: The variable result can only be null at this location
StringBuffer s = new StringBuffer();//StringBuffer对象是一个空的对象
StringBuffer s = new StringBuffer(“abc”);//创建带有内容的StringBuffer对象,对象的内容就是字符串”
小结:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。

差距对比String StringBuilder :

image-20250112200750269

image-20250112200623034

split

public class test {
    public static void main(String[] args) {
        String str=" abcdef   ghi ";
        String[] list=str.split(" ");
        for (String s:list) {
            System.out.print(s+";");//;abcdef;;;ghi;  空字符串也算  最前面含有一个空字符串,但是最后的空字符串自动删除了
        }
    }
}

substring

左闭右开

public String substring(int beginIndex)public String substring(int beginIndex, int endIndex)

img

二、常见技巧以及注意事项

1. 防止溢出

  • 对数组元素之和进行取余,不要所有的加载一起之后再取余,边加边取余

  • 以及求两数之和除以2的时候:(2+4)/2=3 = 2+(4-2)/2=3

int mid = left + ((right - left) >> 1);// 防止溢出 等同于(left + right)/2

2. 双指针法

双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

3. 滑动窗口

https://programmercarl.com/0209.长度最小的子数组.html#算法公开课

209.长度最小的子数组 leetcode_209

三、题目之:leetcode热题100

1. 两数之和

image-20250109210228094

暴力解

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int res[]=new int[2];
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    res[0]=i;
                    res[1]=j;
                    return res;
                }
            }
        }
        return res;//这里只要有返回就行 null也可以
    }
}

哈希

image-20250111172831952

image-20250111221200128

class Solution {
    // 使用哈希表
    public int[] twoSum(int[] nums, int target) {
        int []res=new int[2];
        Map<Integer,Integer> map=new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int tmp = target - nums[i];
            if(map.containsKey(tmp)){
                res[0]=i;
                res[1]=map.get(tmp);
            }
            map.put(nums[i],i);
        }
        return res;
    }
}

2. 两数相加

image-20250109210444197

image-20250109210452426

四、题目之:代码随想录

https://programmercarl.com/

(1) 代码随想录:数组

704. 二分查找

class Solution {
    public int search(int[] nums, int target) {
        if(target<nums[0] || target>nums[nums.length-1]){
            return -1;
        }
        int left = 0,right = nums.length-1;
        while(left<=right){
            int mid = left + (right-left)/2;
            if(nums[mid]==target){
                return mid;
            }
            else if(nums[mid]>target){
                right = mid-1;
            }
            else{
                left = mid+1;
            }
        }
        return -1;
    }
}

27. 移除元素

image-20250109220455986

暴力解:

27.移除元素-暴力解法

class Solution {
    public int removeElement(int[] nums, int val) {
        int n=nums.length;
        for(int i = 0; i < n; i++){
            if(nums[i] == val){
                for(int j = i+1; j < n; j++){
                    nums[j-1] = nums[j];
                }
                i--;//注意这里的更新
                n--;
            }
        }
        return n;
    }
}

双指针:

27.移除元素-双指针法

class Solution {
    public int removeElement(int[] nums, int val) {
        // 快慢指针
        int slowIndex = 0;
        //基本思想:slowIndex  : 已经删除val元素的新数组的下标的位置
        //fastIndex : 寻找新数组的元素 ,新数组就是不含有目标元素的数组
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {//如果原数组中的元素不等于val,那么就是属于新数组的元素
                //复制到新数组中的对应的位置
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;
    }
}

977. 有序数组的平方

image-20250110142741780

暴力解

class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] * nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}

双指针

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        int left = 0, right = n - 1, index = n - 1;
        while (left <= right) {
            if (nums[left] * nums[left] > nums[right] * nums[right]) {
                res[index--] = nums[left] * nums[left];
                ++left;
            } else {
                res[index--] = nums[right] * nums[right];
                --right;
            }
        }
        return res;
    }
}

209. 长度最小的子数组

image-20250110163455186

暴力解

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int sum = 0;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) {
            sum=0;
            for (int j = i; j < nums.length; j++) {
                sum += nums[j];
                if (sum >= target) {
                    res = (j - i + 1) < res ? (j - i + 1) : res;
                    break;
                }
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;//如果res没有被赋值,说明数组元素的综合没有超过target
    }
}

滑动窗口:

209.长度最小的子数组

leetcode_209
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int slow = 0,sum=0,res=Integer.MAX_VALUE;//slow 滑动窗口起始位置
        for(int fast = 0;fast<nums.length;fast++){
            sum+=nums[fast];
            while(sum>=target){// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
                res=Math.min(res,fast-slow+1);
                sum-=nums[slow++];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置).可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
            }
        }
        return res==Integer.MAX_VALUE?0:res;
    }
}

59. 螺旋矩阵 II


img

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int loop = 0, left = 0, right = n - 1, top = 0, bottom = n - 1, cnt = 1;
        while (loop <= n / 2) {
            for (int i = left; i <= right - 1; i++) {
                matrix[top][i] = cnt++;
            }
            for (int i = top; i <= bottom - 1; i++) {
                matrix[i][right] = cnt++;
            }
            for (int i = right; i >= left+1; i--) {
                matrix[bottom][i] = cnt++;
            }
            for (int i = bottom; i >= top+1; i--) {
                matrix[i][left] = cnt++;
            }
            loop++;
            left++;
            right--;
            bottom--;
            top++;
        }
        if(n%2!=0){
            matrix[n/2][n/2] = cnt;
        }
        return matrix;
    }
}

(3) 代码随想录:哈希表

242. 有效的字母异位词

image-20250110182651980

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length())
            return false;
        int[] record = new int[26];
        for (int i = 0; i < s.length(); i++) {
            record[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < t.length(); i++) {
            record[t.charAt(i) - 'a']--;
        }
        for (int count : record) {
            if (count != 0) { // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        return true;
    }
}

349. 两个数组的交集

在这里插入图片描述

用数组来做哈希表

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int []hash1 = new int[1001];
        int []hash2 = new int[1001];
        for (int i = 0; i < nums1.length; i++) {
            hash1[nums1[i]]++;
        }
        for (int i = 0; i < nums2.length; i++) {
            hash2[nums2[i]]++;
        }
        ArrayList<Integer> resTmp = new ArrayList<>();//ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
        for (int i = 0; i < hash1.length; i++) {
            if (hash1[i] >0&&  hash2[i]>0) {//出现一次或者多次的,都记录其中了
                resTmp.add(i);
            }
        }
        int[] res = new int[resTmp.size()];
        for (int i = 0; i < res.length; i++) {
            res[i] = resTmp.get(i);
        }
        return res;
    }
}

Hashset

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set1 = new HashSet<>();
        for (int num : nums1) {
            set1.add(num);
        }
        
        Set<Integer> set2 = new HashSet<>();
        for (int num : nums2) {
            if (set1.contains(num)) {
                set2.add(num);
            }
        }
        
        int[] result = new int[set2.size()];
        int index = 0;
        for (int num : set2) {
            result[index++] = num;
        }
        
        return result;
    }
}

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

暴力解法

n转成str获取需要创建的数组的长度 n置0,

然后用str.charAt(i)-'0'来获取每一位的数字, 累加至n 循环;

判断无限循环的方法:设置loop如果loop过大就直接返回false。(仅适用于骗取测试点)

class Solution {
    public boolean isHappy(int n) {
        int loop=0;
        while(n!=1){
            String str = n+"";
            int length=str.length();
//        System.out.println(length);
            int []num=new int[length];
            n=0;
            for(int i=0;i<length;i++){
                num[i]=str.charAt(i)-'0';
                n+=num[i]*num[i];
            }
            loop++;
            if(loop==999){
                return false;
            }
        }
        return true;
    }
}

哈希

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要

(可能后面n会一直重复出现,或者几个数重复出现,不然不可能陷入无限的循环)

哈希主要为了判断是否陷入了无限循环

class Solution {
    public boolean isHappy(int n){
        Set<Integer> set = new HashSet<>();
        while(n!=1 && !set.contains(n)){
            set.add(n);
            n=getNextNum(n);//更新n
        }
        return n==1;//这个思想很重要
    }
    private int getNextNum(int n){
        int res=0;//需要注意的是,需要用到各个位数的时候,不需要将各个位存储到数组里面,直接拆然后更新n就行
        while(n!=0){
            int tmp=n%10;
            res += tmp*tmp;
            n=n/10;
        }
        return res;
    }
}

454. 四数相加 II

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

暴力

直接超时

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int count=0;
        for(int i=0;i<nums1.length;i++){
            for(int j=0;j<nums2.length;j++){
                for(int k=0;k<nums3.length;k++){
                    for(int l=0;l<nums4.length;l++){
                        if(nums1[i]+nums2[j]+nums3[k]+nums4[l]==0){
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }
}

哈希

https://leetcode.cn/problems/4sum-ii/solutions/65894/chao-ji-rong-yi-li-jie-de-fang-fa-si-shu-xiang-jia/

Java HashMap getOrDefault() 方法:https://www.runoob.com/java/java-hashmap-getordefault.html

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> map=new HashMap<>();
        for(int num1: nums1){
            for(int num2: nums2){
                int sum=num1+num2;
                map.put(sum,map.getOrDefault(sum,0)+1);
                //值为sum的数出现的次数,次数为 map.getOrDefault(sum,0)+1
                //map.getOrDefault(sum,0) 值为sum的数出现的次数,次数为键值对sum(key)对应的value
                //map.getOrDefault(sum,0) 作用是如果map里面没有key==sum的对,那么取默认值0;防止java丢出异常
            }
        }
        int res=0;
        for(int num3: nums3){
            for(int num4: nums4){
                res+=map.getOrDefault(-(num3+num4),0);
            }
        }
        return res;
    }
}

383. 赎金信

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = "a", magazine = "b"
输出:false

示例 2:

输入:ransomNote = "aa", magazine = "ab"
输出:false

示例 3:

输入:ransomNote = "aa", magazine = "aab"
输出:true

数组哈希

一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        if(ransomNote.length() > magazine.length()) return false;
        int[] hash = new int[26];//默认初始全为0
        for (int i = 0; i < magazine.length(); i++) {
            hash[magazine.charAt(i) - 'a']++;
        }
        for (int i = 0; i < ransomNote.length(); i++) {
            hash[ransomNote.charAt(i) - 'a']--;
            if (hash[ransomNote.charAt(i) - 'a'] < 0) return false;
        }
        return true;
    }
}

15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

双指针

15.三数之和

https://leetcode.cn/problems/3sum/solutions/12307/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/

class Solution {
    public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList();
        int len = nums.length;
        if(nums == null || len < 3) return ans;
        Arrays.sort(nums); // 排序,nums变成递增数组
        for (int i = 0; i < len - 2 ; i++) {// i < nums.length - 2是为了保证后面还能存在两个数字
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重  针对i指针(第一个数)  1112356
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]) L++; // 去重   针对L指针(第二个数)1222356  
                    while (L<R && nums[R] == nums[R-1]) R--; // 去重   针对R指针(第三个数)1235666
                    L++;
                    R--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
        }        
        return ans;
    }
}

18. 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

双指针

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length < 4) {
            return res;
        }
        for (int i = 0; i < nums.length - 3; i++) {
            if (nums[i] > target && nums[i+1] >= 0) {
                break;
            } // 剪枝  如果第一个数已经大于target 而且后面的数不是负数,则终止
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            } // 去重
            for (int j = i + 1; j < nums.length - 2; j++) {
                if (nums[i] + nums[j] > target && nums[j+1] >= 0) {
                    break;
                } // 剪枝  如果前两个数之和已经大于target 而且后面的数不是负数,则终止
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                } // 去重
                int L = j + 1;
                int R = nums.length - 1;
                while (L < R) { //******  注意这里不要忘记   *******  
                    int sum = nums[i] + nums[j] + nums[L] + nums[R];
                    if (sum == target) {
                        res.add(Arrays.asList(nums[i], nums[j], nums[L], nums[R]));
                        while (L < R && nums[L] == nums[L + 1])
                            L++;
                        while (L < R && nums[R] == nums[R - 1])
                            R--;
                        L++;
                        R--;
                    } else if (sum < target)
                        L++;
                    else if (sum > target)
                        R--;
                }
            }
        }
        return res;
    }
}

541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2
输出:"bacd"

提示:

  • 1 <= s.length <= 104
  • s 仅由小写英文组成
  • 1 <= k <= 104
class Solution {
    public String reverseStr(String s, int k) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        for (int i = 0; i < len; i += 2 * k) {
            //注意从开始就要判断i与len-1的关系,因为有可能字符串本身就很短,取不到i += 2 * k
            if (i + k - 1 > len - 1) {// 判断尾数够不够k个来决定end指针的位置
                reverse(chars, i, len - 1);
                return new String(chars);
            } else if (i + 2 * k - 1 > len - 1 && i + k - 1 >= len - 1) {// i+k-1<len-1<i+2k-1,判断len-1的范围 来决定end指针的位置
                reverse(chars, i, i + k - 1);
                return new String(chars);
            } else {
                reverse(chars, i, i + k - 1);
            }
        }
        return new String(chars);
    }

    private void reverse(char ch[], int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = ch[i];
            ch[i] = ch[j];
            ch[j] = temp;
        }
    }
}

优化判断逻辑:

class Solution {
    public String reverseStr(String s, int k) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        for (int i = 0; i < len; i += 2 * k) {
            // 注意从开始就要判断i与len-1的关系,因为有可能字符串本身就很短,取不到i += 2 * k
            if (i + k - 1 > len - 1) {// 判断尾数够不够k个来决定end指针的位置
                reverse(chars, i, len - 1);
                return new String(chars);
            }
            reverse(chars, i, i + k - 1);
        }
        return new String(chars);
    }

    private void reverse(char ch[], int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = ch[i];
            ch[i] = ch[j];
            ch[j] = temp;
        }
    }
}

151. 反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。 

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s至少存在一个 单词

split分割

class Solution {
    public String reverseWords(String s) {
        String[] words = s.split(" ");
        String res = "";
        for (int i = words.length - 1; i >= 0; i--) {
            if (!words[i].equals(" ") && !words[i].equals("")) {
                res += words[i]+" ";
            }
        }
        return res.substring(0, res.length()-1);
    }
}
class Solution {
    public String reverseWords(String s) {
        String[] strs = s.trim().split(" ");        // 删除首尾空格,分割字符串
        StringBuilder res = new StringBuilder();
        for (int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
            if (strs[i].equals("")) continue;        // 遇到空单词则跳过
            res.append(strs[i] + " ");              // 将单词拼接至 StringBuilder
        }
        return res.toString().trim();               // 转化为字符串,删除尾部空格,并返回
    }
}

作者:Krahets
链接:https://leetcode.cn/problems/reverse-words-in-a-string/solutions/2361551/151-fan-zhuan-zi-fu-chuan-zhong-de-dan-c-yb1r/

不使用split

参考:https://leetcode.cn/problems/reverse-words-in-a-string/solutions/2810292/javapython3cshuang-zhi-zhen-mo-ni-ni-xu-lbdrk/

class Solution {
    public String reverseWords(String s) {
        StringBuilder res = new StringBuilder();    // 结果字符
        int i = 0;              // 用于表示首个非空字符的位置
        int j = s.length() - 1;   // 用于表示最后一个非空字符的位置
        while(i < j && s.charAt(i) == ' ')i++;     // 找到首个非空字符
        while(i < j && s.charAt(j) == ' ')j--;     // 找到最后一个非空字符
        for(;j >= i;){
            // 逆序反转单词[j+1, k]
            int k = j;      // 标记一个单词的结尾位置
            while(j >= i && s.charAt(j) != ' ')j--;    // 找到单词的起点
            res.append(s.substring(j + 1, k + 1));      // 截取单词加到结果上
            if(j >= i)res.append(' ');       // 如果非首个单词,之间用空格间隔
            while(j >= i && s.charAt(j) == ' ')j--;    // 找到下一个单词的结尾位置
        }
        return res.toString(); 
    }
}

28. 找出字符串中第一个匹配项的下标(KMP)

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

提示:

  • 1 <= haystack.length, needle.length <= 104
  • haystackneedle 仅由小写英文字符组成

年轻人不讲武德:return haystack.indexOf(needle); 结束睡觉保养头发

image-20250112203228357

return haystack.indexOf(needle);

459. 重复的子字符串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

示例 1:

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

输入: s = "aba"
输出: false

示例 3:

输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
class Solution {
   public boolean repeatedSubstringPattern(String s) {
        String str = s + s;
        return str.substring(1, str.length() - 1).contains(s);
}
}

作者:Goodlucky
链接:https://leetcode.cn/problems/repeated-substring-pattern/solutions/114572/jian-dan-ming-liao-guan-yu-javaliang-xing-dai-ma-s/
来源:力扣(LeetCode)
posted @   借我杀死庸碌的情怀  阅读(27)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示