LeeCode刷题简记(三)

LeeCode数据结构

Solution1两数之和、Solution15三数之和、Solution26删除排序数组中的重复项、Solution27移除元素、Solution80删除有序数组的重复项Ⅱ、Solution88合并两个有序数组、Solution167两数之和Ⅱ输入有序数组、Solution53最大子数组和、Solution121买卖股票的最大收益、Solution217存在重复元素、Solution242有效字母异位词、Solution349两数组交集、

Solution1两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

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

 两种思路   暴力解法,双指针循环嵌套直到找到结果;

                  哈希表让要找的数减当前的数,拿他们的值在已有哈希表中找对应值,有返回无继续。

public class S1两数之和 {
    //哈希表
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            if (i == 0) {
                map.put(nums[i], i);
            } else {
                if (map.containsKey(target - nums[i])) {
                    return new int[]{map.get(target - nums[i]), i};
                } else {
                    map.put(nums[i], i);
                }
            }
        }
        return null;
    }
    
    //双指针  循环
    public int[] twoSum01(int[] nums, int target) {
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return null;
    }
}

Solution15三数之和

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

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

双指针   按排序左小右大计算当前首位加次位加末位数是否为零进行操作

需要考虑的细节太多!!

public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length;i++){
            if (nums[i] > 0){//判正负
                return lists;
            }
            if(i > 0 && nums[i] == nums[i - 1]){//判重复
                continue;
            }
            //每次外循环结束LR都会重置
            int cur = nums[i];
            int L = i + 1;
            int R = nums.length -1;
            while (L < R) {//相遇自动跳出
                int temp = cur + nums[L] + nums[R];
                if (temp == 0) {//传输当前组合
                    List<Integer> list = new ArrayList<>();
                    list.add(cur);
                    list.add(nums[L]);
                    list.add(nums[R]);
                    lists.add(list);
                    L++;
                    R--;
                    while (L < R && nums[L] == nums[L - 1]){
                        L++;
                    }
                    while (L < R && nums[R] == nums[R + 1]){
                        R--;
                    }
                }else if (temp > 0){//正减小  负增大
                    R--;
                }else {
                    L++;
                }
            }
        }
        return null;
    }

Solution26删除排序数组中的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

使用双指针法完成,一个指针向后遍历,一个指针原地重写数组即可。

public int removeDuplicates(int[] nums) {
        int n = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[n] != nums[i]){
                n += 1;
                nums[n] = nums[i];
            }
        }
        return n + 1;
    }

Solution27移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

双指针前验后替

//给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
public class S27移除元素 {
    public int removeElement(int[] nums, int val) {
        int n = -1;//防止第一个就是要删的
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != val){
                n++;
                nums[n] = nums[i];
            }
        }
        return n + 1;
    }
}

Solution80删除有序数组的重复项Ⅱ

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

首先前两个直接跳过,重不重复都无意义,始终拿后指针遍历到的位置元素与当前指针前两位元素比较,需要添加就替换当前值。

public int removeDuplicates(int[] nums) {
        int index = 2;
        for (int i = index; i < nums.length; i++) {
            if (nums[i] > nums[index - 2]){//不同就替换当前位置
                nums[index] = nums[i];
                index++;
            }
        }
        return index;
    }

Solution88合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

 直接连接然后排序

public void merge(int[] nums1, int m, int[] nums2, int n) {
        for (int i = 0; i != n; ++i) {
            nums1[m + i] = nums2[i];
        }
        Arrays.sort(nums1);
    }

奇怪的解法

public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m--+--n;

        while(n>=0) {
            nums1[i--] = m>=0 && nums1[m]>nums2[n] ? nums1[m--] : nums2[n--];
        }
    }

 Solution167两数之和Ⅱ输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

二分查找利用原本就升序的数组,双指针把数值范围确定出来然后进行操作。

时间复杂度Onlogn略高。

public int[] twoSum01(int[] numbers, int target) {
        for (int i = 0; i < numbers.length; i++) {
            int low = i + 1;
            int high = numbers.length - 1;
            if (target - numbers[i] > numbers[high]){
                continue;//差比最大数还大就跳过
            }
            if (target - numbers[i] < numbers[low]){
                break;//差比当前还小直接退出
            }
            while (low <= high){//二分查找
                int mid = (high - low) / 2 + low;
                if (numbers[mid] == target - numbers[i]){
                    return new int []{i + 1, mid + 1};
                } else if(numbers[mid] > target - numbers[i]){
                    high = mid - 1;
                }else {
                    low = mid + 1;
                }
            }
        }
        return new int[]{-1,-1};
    }

还可以用双指针,更快更强

public int[] twoSum(int[] numbers, int target) {
        int low = 0;
        int high = numbers.length - 1;
        while (low < high) {
            int sum = numbers[low] + numbers[high];
            if (sum < target){
                low++;
            }else if (sum > target){
                high--;
            }else{
                return new int[]{low + 1, high + 1};
            }
        }
        return null;
    }

 Solution53最大子数组和

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

思路:res作为历史最佳解,sum作为当前最佳解,每一次遍历nums数组时,都去动态更新res和sum。 动态更新的逻辑为: 如果sum为正数,在有res记录历史最佳值的条件下,可以有恃无恐地继续累加,创造新高;如果sum为负数,不管下一次遍历值是多少累加后都不会大于它,见风使舵果断取下一个遍历值为当前最佳解。 每一轮遍历结束后,如果当前最佳解优于历史最佳解,就会升任历史最佳解。       动!!态!!规!!划!!

//如果sum为正数,那么继续向后累加并记录下sum的最大值,继续向后累加的原因是sum有变大的可能。
//如果sum为负数,那么sum+nums[i]必然小于nums[i],所以sum = nums[i].
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0)
                sum += num;
            else
                sum = num;
            res = Math.max(res, sum);
        }
        return res;
    }

 Solution121买卖股票的最大收益

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 

 动态规划 前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格}

//低价买高价卖
//记录【今天之前买入的最小值】
//计算【今天之前最小值买入,今天卖出的获利】,也即【今天卖出的最大获利】
//比较【每天的最大获利】,取最大值即可
public class S121买卖股票 {
    public int maxProfit(int[] prices) {
        if(prices.length <= 1)
            return 0;
        int min = prices[0], max = 0;
        for(int i = 1; i < prices.length; i++) {
            max = Math.max(max, prices[i] - min);
            min = Math.min(min, prices[i]);
        }
        return max;
    }
}

Solution217存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false

方法一 数组排序

排序在对数字从小到大排序之后,数组的重复元素一定出现在相邻位置中。因此,我们可以扫描已排序的数组,每次判断相邻的两个元素是否相等,如果相等则说明存在重复的元素。

    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = 0; i < n - 1; i++) {
            if (nums[i] == nums[i + 1]) {
                return true;
            }
        }
        return false;
    }

方法二 HashSet

时间空间复杂度都是O(n).

//HashSet  利用哈希表不会存储两个相同元素的特性
    public boolean containsDuplicate01(int[] nums) {
        HashSet<Integer> hashSet = new HashSet<>();
        for (int num : nums) {
            if (hashSet.add(num) == false) {
                return true;
            }
        }
        return false;
    }

Solution242有效字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

import java.util.Arrays;

//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
public class S242有效的符号 {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        char[] sChars = s.toCharArray();
        char[] tChars = t.toCharArray();
        Arrays.sort(sChars);
        Arrays.sort(tChars);
        return Arrays.equals(sChars, tChars);
    }
}

Solution349两数组交集

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

总结   先排序,然后是用双指针先遍历两容器,重复则插入到新容器

时间复杂度:O(m \log m+n \log n)O(mlogm+nlogn)

细节:对两个数组进行排序,然后使用两个指针遍历两个数组。可以预见的是加入答案的数组的元素一定是递增的,为了保证加入元素的唯一性,我们需要额外记录变量 \textit{pre}pre 表示上一次加入答案数组的元素。

初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,且该数字不等于 \textit{pre}pre ,将该数字添加到答案并更新 \textit{pre}pre 变量,同时将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。

public int[] intersection(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int length1 = nums1.length, length2 = nums2.length;
        int[] intersection = new int[length1 + length2];
        int index = 0, index1 = 0, index2 = 0;
        while (index1 < length1 && index2 < length2) {
            int num1 = nums1[index1], num2 = nums2[index2];
            if (num1 == num2) {
                // 保证加入元素的唯一性
                if (index == 0 || num1 != intersection[index - 1]) {
                    intersection[index++] = num1;
                }
                index1++;
                index2++;
            } else if (num1 < num2) {
                index1++;
            } else {
                index2++;
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值