leetcode5

本文探讨了多种算法技巧解决编程问题,如二分查找、二进制法、滑动窗口和动态规划,包括查找数组中唯一重复数字、字母异位词组合、最长回文子串、子数组最大和、无重复字符子串长度、中位数和整数数组排列等。通过实例解析和代码实现,展示了如何在常量空间内优化问题求解。
摘要由CSDN通过智能技术生成

287.给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

输入:nums = [1,3,4,2,2]
输出:2
示例 2:

输入:nums = [3,1,3,4,2]
输出:3
 

提示:

1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
 

进阶:

如何证明 nums 中至少存在一个重复的数字?
你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?

 1.二分法,抽屉思想,如果小于mid的数的数目超过mid的值,说明重复数一定在比mid大。注意标杆是下标不是数值。

class Solution {
    public int findDuplicate(int[] nums) {
       //二分法
       int length=nums.length;
       int left=0;
       int right=length;
       int mid=0;
       int count=0;
       while(left<right){
            mid=(left+right)/2;
            count=0;
           //统计小于mid的数的数目
           for(int i=0;i<length;i++){
               if(nums[i]<=mid){
                   count++;
               }
           }
           if(count>mid){
               right=mid;
           }else{
               left=mid+1;
           }
       }
       return left;
    }
}

2.二进制法

 1<<i,将1左移i位。

class Solution {
    public int findDuplicate(int[] nums) {
        //二进制法
        //统计1-n各列的1的个数,然后统计现有的,看哪几位不一样。
        int length=nums.length;
        int res=0;
        for(int i=0;i<32;i++){//int类型的二进制都在32位以内
            int k=(1<<i);//1左移i位,一个一个来
            int sum1=0;
            int sum2=0;
            for(int j=0;j<length;j++){
                if((nums[j]&k)!=0){
                    sum1++;
                }
            }
            for(int m=1;m<length;m++){
                if((m&k)!=0){
                    sum2++;
                }
            }
            //现在sum1的结果是原数组第i位为1的数的个数,sum2为1-n中第i位为1的数的个数
            if(sum1>sum2){
                res=res|k;
            }
        }
        return res;
    }
}

3.把数组看成链表,用快慢指针、、没太懂

class Solution {
    public int findDuplicate(int[] nums) {
        //把这个数组看成一个链表,她的值为next,
        //则这个链表一定有环,然后用快慢指针找环与直线的交点即可
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);//快慢相遇了,把慢的放到起点,然后继续走,再次相遇在交点。
        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}

49.给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:

输入: strs = [""]
输出: [[""]]
示例 3:

输入: strs = ["a"]
输出: [["a"]]
 

提示:

1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母

 getOrDefault方法:Map集合中有这个key时,就使用这个key对应的value值,如果没有就使用默认值defaultValue。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();//只能用string做key
        for (String str : strs) {
            char[] array = str.toCharArray();
            Arrays.sort(array);
            String key = new String(array);
            List<String> list = map.getOrDefault(key, new ArrayList<String>());//如果有就取出来当list,没有就new一个
            list.add(str);//在取出来的基础上加上当前这个,然后存进去
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

计数法:和上面那个基本相同,  但空间复杂度和时间复杂度应该都略高。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            int[] counts = new int[26];
            int length = str.length();
            for (int i = 0; i < length; i++) {
                counts[str.charAt(i) - 'a']++;
            }
            // 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 26; i++) {
                if (counts[i] != 0) {
                    sb.append((char) ('a' + i));
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

5.给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:

输入:s = "cbbd"
输出:"bb"
 

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母组成

1.暴力解法 -经典超时 

class Solution {
    public String longestPalindrome(String s) {
        //遍历所有字串
        String result=new String();
        int maxSize=0;
        int l=s.length();
        for(int i=0;i<l;i++){
            for(int j=i+1;j<=l;j++){
                //判断当前字串是不是回文串,如果是,且长度大于最大长度,用一个临时变量保存它
                String curr=s.substring(i,j);
                if(isPalindrome(curr)&&(j-i)>maxSize){
                    result=curr;
                    maxSize=j-i;
                }
            }
        }
        return result;
    }
    //判断一个字符串是不是回文字符串
    public boolean isPalindrome(String s){
        int length=s.length();
        for(int i=0;i<length/2;i++){
            if(s.charAt(i)!=s.charAt(length-1-i)){
                return false;
            }
        }
        return true;
    }
}

 2.反转求最大公共子串

---------------------------------------------------------------------------------------------------------------------------------

53.给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:

输入:nums = [1]
输出:1
示例 3:

输入:nums = [5,4,-1,7,8]
输出:23
 

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
 

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

class Solution {
    public int maxSubArray(int[] nums) {
        //原地修改数组
        for(int i=1;i<nums.length;i++){
            nums[i]=Math.max(nums[i]+nums[i-1],nums[i]);
        }
        int max=nums[0];
        for(int i=1;i<nums.length;i++){
            if(nums[i]>max){
                max=nums[i];
            }
        }
        return max;
    }
}

优化一下,只遍历一次

class Solution {
    public int maxSubArray(int[] nums) {
        //原地修改数组
        int max=nums[0];
        for(int i=1;i<nums.length;i++){
            nums[i]=Math.max(nums[i]+nums[i-1],nums[i]);
            max=Math.max(max,nums[i]);
        } 
        return max;
    }
}

分治,打死我也不写。

221.在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

示例 1:


输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4
示例 2:


输入:matrix = [["0","1"],["1","0"]]
输出:1
示例 3:

输入:matrix = [["0"]]
输出:0
 

提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 300
matrix[i][j] 为 '0' 或 '1'

 动态规划实牛!

class Solution {
    public int maximalSquare(char[][] matrix) {
        int maxSide = 0;
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return maxSide;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int[][] dp = new int[rows][columns];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                if (matrix[i][j] == '1') {
                    //构建两个边边,如果不是字符矩阵是数字矩阵,就方便很多。
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    }
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }
        return maxSide*maxSide;
    }
}

3.给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
 

提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

 滑动窗口法

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n=s.length();
        if(n==0||n==1){
            return n;
        }
        //用哈希集合?添加失败说明重复了
        Set<Character> hashSet=new HashSet<>();
        int temp=0;
        int j=0;
        for(int i=0;i<n;i++){
            if(i!=0){
                hashSet.remove(s.charAt(i-1));
            }
            while(j<n&&!hashSet.contains(s.charAt(j))){
                hashSet.add(s.charAt(j));
                j++;
            }
            temp=Math.max(temp,j-i);
        }
        return temp;
    }
}

4.给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
 

提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m=nums1.length;
        int n=nums2.length;
        //二分查找
        int k=m+n;
        if(k==0){
            return 0.0;
        }
        if((k&1)==1){
            return findIt(nums1,nums2,k/2+1);
        }else{
            return (findIt(nums1,nums2,k/2)+findIt(nums1,nums2,k/2+1))/2.0;
        }
    }
    public double findIt(int[] nums1,int[] nums2,int it){
        //寻找第it个元素
        int l1=nums1.length;
        int l2=nums2.length;
        int index1=0;
        int index2=0;
        while(true){
            //1.边界情况
            //为什么不是0的判断?因为中途可能会有一个数组已经满了,不仅仅是一开始
            if(index1==l1){
                return nums2[index2+it-1];
            }
            if(index2==l2){
                return nums1[index1+it-1];
            }
            if(it==1){
                return Math.min(nums1[index1],nums2[index2]);
            }
            //2.正常情况
            int temp=it/2;
            int n1=Math.min(index1+temp,l1)-1;
            int n2=Math.min(index2+temp,l2)-1;
            int pivot1=nums1[n1];
            int pivot2=nums2[n2];
            if(pivot1<=pivot2){
                it-=(n1-index1+1);
                index1=n1+1;
            }else{
                it-=(n2-index2+1);
                index2=n2+1;
            }
        }
    }

 31.整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]
 

提示:

1 <= nums.length <= 100
0 <= nums[i] <= 100

class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        //找不逆序的第一个数
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        //找比这个数大的数并交换
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            swap(nums, i, j);
        }
        //反转i后面的序列
        for(int m=i+1,n=nums.length-1;m<n;m++,n--){
            swap(nums,m,n);
        }
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

 10.给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

 
示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:

输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
 

提示:

1 <= s.length <= 20
1 <= p.length <= 30
s 只包含从 a-z 的小写字母。
p 只包含从 a-z 的小写字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符

动态规划-直接用字符串匹配会出现经典超时!!!

class Solution {
    public boolean isMatch(String s, String p) {
        //浅写一个动态规划,f[i][j]代表这s的前i个字符和p的前j个字符是否匹配
        //如果p[j]是普通字符,当s[i]与p[j]匹配,f[i][j]=f[i-1][j-1],否则返回false
        //如果p[j]是.,f[i][j]=f[i-1][j-1]
        //如果p[j]是*,考虑几种情况,如果p[j-1]与s[i]匹配,那就f[i][j]=f[i-1][j],
        //如果不匹配,那就用*消去这个不匹配的,相当于f[i][j]=f[i][j-2]
        int m=s.length();
        int n=p.length();
        boolean[][] res=new boolean[m+1][n+1];
        res[0][0]=true;
        //单纯的i或j为0都是false。
        for(int i=0;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p.charAt(j-1)=='*'){
                    if(matches(s,p,i,j-1)){
                        res[i][j]=res[i][j-2]||res[i-1][j];//attention!!!!
                    }else{
                        res[i][j]=res[i][j-2];
                    }
                }else{
                    if(matches(s,p,i,j)){
                        res[i][j]=res[i-1][j-1];
                    }
                }
            }
        }
        return res[m][n];
    }
    public boolean matches(String s, String p, int i, int j) {
        if (i == 0) {
            return false;
        }
        if (p.charAt(j - 1) == '.') {
            return true;
        }
        return s.charAt(i - 1) == p.charAt(j - 1);
    }
}

 15、给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:

输入:nums = []
输出:[]
示例 3:

输入:nums = [0]
输出:[]
 

提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

 

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n=nums.length;
        Arrays.sort(nums);
        List<List<Integer>> res=new ArrayList<>();
         List<Integer> l=new ArrayList<>();
        for(int i=0;i<n;i++){
            //如果第一个数字重复,就走到下一个第一个数字
            if(i>0&&nums[i]==nums[i-1]){
                continue;
            }
            int k=n-1;
            int target=-nums[i];
            for(int j=i+1;j<n;j++){
                //如果第二个数字重复,就走到下一个第二个数字
                if(j>i+1&&nums[j]==nums[j-1]){
                    continue;
                }
                //找到j和k满足和为target。
                //要求j在k左边
                while(j<k&&nums[j]+nums[k]>target){
                    k--;
                }
                if(j==k){
                    break;
                }
                if(nums[j]+nums[k]==target){
                    l=new ArrayList<>();
                    l.add(nums[i]);
                    l.add(nums[j]);
                    l.add(nums[k]);
                    res.add(l);
                }
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值