LeetCode刷题笔记 简单题优秀题解集合(500~800)

501. 二叉搜索树中的众数

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

总结
二叉搜索树的中序遍历是递增顺序,把问题转换为求递增数组众数即可。

class Solution {
    int preVal = 0, curTimes = 0, maxTimes = 0;
    ArrayList<Integer> list = new ArrayList<Integer>();
    
    public int[] findMode(TreeNode root) {
        traversal(root);
        //list转换为int[]
        int size = list.size();
        int[] ans = new int[size];
        for(int i = 0; i < size; i++)
            ans[i] = list.get(i);
        
        return ans;
    }
    public void traversal(TreeNode root){
        if(root == null) return;
        traversal(root.left);
        //判断当前值与上一个值的关系, 更新 curTimes 和 preVal
        if(preVal == root.val){
            curTimes++;
        }else{
            preVal = root.val;
            curTimes = 1;
        }
        //判断当前数量与最大数量的关系, 更新 list 和 maxTimes
        if(curTimes == maxTimes){
            list.add(root.val);
        }else if(curTimes > maxTimes){
            list.clear();
            list.add(root.val);
            maxTimes = curTimes;
        }
        traversal(root.right);
    }
}

541. 反转字符串 II

给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。

示例:
输入: s = “abcdefg”, k = 2
输出: “bacdfeg”

总结
像写诗一样写代码就是这样吧

class Solution {
    public String reverseStr(String s, int k) {
        char[] a = s.toCharArray();
        for (int start = 0; start < a.length; start += 2 * k) {
            int i = start, j = Math.min(start + k - 1, a.length - 1);
            while (i < j) {
                char tmp = a[i];
                a[i++] = a[j];
                a[j--] = tmp;
            }
        }
        return new String(a);
    }
}

594. 最长和谐子序列

和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是1。

现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度。

示例:
输入: [1,3,2,2,5,2,3,7]
输出: 5
原因: 最长的和谐数组是:[3,2,2,2,3]

public class Solution {
    public int findLHS(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int res = 0;
        for (int num: nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
            if (map.containsKey(num + 1))
                res = Math.max(res, map.get(num) + map.get(num + 1));
            if (map.containsKey(num - 1))
                res = Math.max(res, map.get(num) + map.get(num - 1));
        }
        return res;
    }
}
class Solution {
    public int findLHS(int[] nums) {
        Arrays.sort(nums);
        int start = 0, end = 0;
        int res = 0;
        for(int i = 0; i < nums.length-1; i++) {
            if(nums[i] == nums[i+1]) {
                end++;
            }else if(nums[i+1] - nums[i] == 1) {
                if(i+1 == nums.length-1) {  // 到尾部了
                    end++;
                    res = Math.res(res, end-start+1);
                }else {                     // 直接找到于当前数相等的最后一位
                    int tmp = i+1;
                    end++;
                    i++;
                    while(i < nums.length-1 && nums[i] == nums[i+1]) {
                        end++;
                        i++;
                    }
                    res = Math.res(res, end-start+1);
                    start = tmp;
                    i--;
                }
            }else {     // [..., 2, 2, 4, ...]
                start = i+1;
                end = i+1;
            }
        }
        return res;
    }
}

645. 错误的集合

集合 S 包含从1到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复。

给定一个数组 nums 代表了集合 S 发生错误后的结果。你的任务是首先寻找到重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

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

总结
一种针对特殊情况的排序算法

class Solution {
    public int[] findErrorNums(int[] nums) {
        for(int i = 0; i < nums.length; i++){
            while(nums[i] != (i + 1) && nums[i] != nums[nums[i] - 1])
                swap(nums, nums[i] - 1, i);
        }
        
        for(int i = 0; i < nums.length; i++){
            if(nums[i] != i + 1) 
            	return new int[]{nums[i], i + 1};
        }
        return null;
    }
    
    private void swap(int[] nums, int i, int j){
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

665.非递减数列

给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。

示例:
输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

总结
这题其实并不简单,如何处理[3,4,2,3]是个难点

class Solution {
    public boolean checkPossibility(int[] nums) {
        int cnt = 0;
        for (int i = 1; i < nums.length && cnt < 2; i++) {
            if (nums[i-1] <= nums[i]) 
                continue;

            cnt++;
            if (i-2>=0 && nums[i-2] > nums[i])
                nums[i] = nums[i-1];
        }
        return cnt <= 1;
    }
}

671. 二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1

class Solution {
    public int findSecondMinimumValue(TreeNode root) {
        return myfun(root, root.val);
    }

    public int myfun(TreeNode root, int val) {
        if (root == null)
            return -1;

        if (root.val > val)
            return root.val;

        int l = myfun(root.left, val);
        int r = myfun(root.right, val);
        if (l > val && r > val)
            return Math.min(l,r);

        return Math.max(l,r);
    }
}

680. 验证回文字符串 Ⅱ

给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

示例:
输入: “abca”
输出: True
解释: 你可以删除c字符。

总结
代码虽然重复了,但是是一种很有一种启发性的写法

class Solution {
    public boolean validPalindrome(String s) {
        int i = 0, j = s.length() - 1;
        while(i < j){
            if(s.charAt(i) != s.charAt(j))
                return isValid(s, i + 1, j) || isValid(s, i, j - 1);
            i++;
            j--;
        }
        return true;
    }
    
    public boolean isValid(String s, int i, int j){
        while(i < j){
            if(s.charAt(i) != s.charAt(j))
                return false;
            i++;
            j--;
        }
        return true;
    }
}

693. 交替位二进制数

给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。

示例:
输入: 5
输出: True
解释:
5的二进制数是: 101

class Solution {
    public boolean hasAlternatingBits(int n) {
        int temp = n^(n>>1); 
        return (temp&(temp+1)) == 0;
        // return (n & (n>>1)) == 0;    // 不能这样写, 因为有 1000 这种情况
    }
}

696. 计数二进制子串

给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。

重复出现的子串要计算它们出现的次数。

示例:
输入: “00110011”
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。

请注意,一些重复出现的子串要计算它们出现的次数。

另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

class Solution {
    public int countBinarySubstrings(String s) {
        int last = 0, cur = 1, res = 0;
        for(int i = 1; i < s.length(); i++) {
            if(s.charAt(i) == s.charAt(i-1)) 
                cur++;
            else {
                last = cur; 
                cur = 1;
            }
            if(last >= cur) res++;  // 0011 包含 01
        }   
        return res;
    }
}

717. 1比特与2比特字符

有两种特殊字符。第一种字符可以用一比特0来表示。第二种字符可以用两比特(10 或 11)来表示。

现给一个由若干比特组成的字符串。问最后一个字符是否必定为一个一比特字符。给定的字符串总是由0结束。

示例 1:
输入:
bits = [1, 0, 0]
输出: True
解释:
唯一的编码方式是一个两比特字符和一个一比特字符。所以最后一个字符是一比特字符。

示例 2:
输入:
bits = [1, 1, 1, 0]
输出: False
解释:
唯一的编码方式是两比特字符和两比特字符。所以最后一个字符不是一比特字符。

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int i = 0;
        while (i < bits.length - 1)
            i += bits[i] + 1;
        return i == bits.length - 1;
    }
}
class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int i = bits.length - 2;
        while (i >= 0 && bits[i] > 0) i--;
        return (bits.length - i) % 2 == 0;
    }
}

724. 寻找数组的中心索引

给定一个整数类型的数组 nums,请编写一个能够返回数组“中心索引”的方法。

我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

示例:
输入:
nums = [1, 7, 3, 6, 5, 6]
输出: 3
解释:
索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。
同时, 3 也是第一个符合要求的中心索引。

class Solution {
    public int pivotIndex(int[] nums) {
        int sum = 0;
        int leSum = 0;
        for(int i : nums)
            sum += i;
        for(int i = 0; i < nums.length; i++) {
            if(leSum * 2 + nums[i] == sum)
                return i;
            leSum += nums[i];
        }
        return -1;
    }
}

ERROR CODE
遇到有负数(或者全非正数)的不能处理

class Solution {
    public int pivotIndex(int[] nums) {
        int le = 0, ri = nums.length-1;
        int lum = 0, rum = 0;
        boolean isL = false;
        while(le < ri) {
            if(lum < rum) {
                isL = false;
                lum += nums[le++];
            }else {
                isL = true;
                rum += nums[ri--];
            }
        }
        if(lum == rum) return isL ? le : ri;
        return -1;
    }
}

747. 至少是其他数字两倍的最大数

在一个给定的数组nums中,总是存在一个最大元素 。

查找数组中的最大元素是否至少是数组中每个其他数字的两倍。

如果是,则返回最大元素的索引,否则返回-1。

示例:
输入: nums = [3, 6, 1, 0]
输出: 1
解释: 6是最大的整数, 对于数组中的其他整数,
6大于数组中其他元素的两倍。6的索引是1, 所以我们返回1.
总结
仔细想想,为什么我们要将最大值与所有元素进行比较呢?如果我们找到第二大的元素,将它的两倍的值与最大值进行比较,不就能证明最大值是否大于所有元素两倍的值吗?

by: Pushy

class Solution {
    public int dominantIndex(int[] nums) {
        int max = 0, idx = 0, less = 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > max) {
                less = max;
                max = nums[i];
                idx = i;
            } else if (nums[i] > less) {
                less = nums[i];
            }
        }
        return max >= (less * 2) ? idx : -1;
    }
}

754. 到达终点数字

在一根无限长的数轴上,你站在0的位置。终点在target的位置。

每次你可以选择向左或向右移动。第 n 次移动(从 1 开始),可以走 n 步。

返回到达终点需要的最小移动次数。

示例:
输入: target = 2
输出: 3
解释:
第一次移动,从 0 到 1 。
第二次移动,从 1 到 -1 。
第三次移动,从 -1 到 2

总结
假设我们移动了 k 次,每次任意地向左或向右移动,那么最终到达的位置实际上就是将 1, 2, 3, …, k 这 k 个数添加正号(向右移动)或负号(向左移动)后求和的值。并且如果最终到达的位置为 t 且 t < 0,那么我们可以将这 k 个数的符号全部取反,这样求和的值为 -t > 0。因此我们只考虑题目中 target > 0 的情况。

我们沿用上面的符号,设 k 为最小的满足 S = 1 + 2 + … + k >= target 的正整数,如果 S == target 那么答案显然是 k。如果 S > target,那么我们需要将一些正号变为负号,使得最后求和的值等于 target。当前比 target 多出了 delta = S - target,那么我们需要在 1, 2, …, k 中找到若干个数变成负号,并且它们的和为 delta / 2。可以证明一定能找到和为 delta / 2 的若干个数:如果 delta / 2 <= k,那么只需要选出 delta / 2 即可;如果 delta / 2 > k,那么先选出 k,再从 1, 2, 3, …, k - 1 中选出若干个数使得它们的和为 delta / 2 - k,这样就把原问题变成了一个规模更小的子问题。显然 delta / 2 <= 1 + 2 + … + k,因此一定能选出若干个数。

上面只适用于 delta 为偶数的情况。若 delta 为奇数,则 delta / 2 不为整数,因此无法选出。此时我们只能考虑 k + 1 和 k + 2,求出 1 到 k + 1 的和以及 1 到 k + 2 的和,它们中必有一个和 1 到 k 的和的奇偶性不同,此时 delta 变为偶数,可以选出若干个数。

class Solution {
    public int reachNumber(int target) {
        int tar = Math.abs(target);
        int idx = 0;
        int sum = 0;
        
        while(sum < tar){
            idx++;
            sum += idx;  
        }
        int dt = sum - tar;
        if(dt % 2 == 0)
            return idx;
        else{
            if(idx % 2 == 0)
                return idx+1;
            else
                return idx+2;
        }
    }
}
class Solution {
    public int reachNumber(int target) {
        target = Math.abs(target);
        int k = 0;
        while (target > 0)
            target -= ++k;
        return target % 2 == 0 ? k : k + 1 + k%2;
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/reach-a-number/solution/dao-da-zhong-dian-shu-zi-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(LeetCode笔记)