LeetCode 刷题题目总结

前言

前段时间参加学校的IT节,然后紧急去刷LeetCode好给自己一个好点点点的排名,结果发现刷题AC还是很有快感的,再加上之前学习了MarkDown就顺带写个刷LeetCode的刷题记录,把自己刷过的希望后面能回顾学习的题目都写下来,好在之后复习复习,现在还是简单中等题为主,嘿嘿,祝自己好运!
有许多题还是勉强过的,还有很多优化空间,所以请各位大佬别在意。

LeetCode 总结

整数反转

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例一:

输入: 123

输出: 321

示例二:

输入: -123

输出: -321

示例三:

输入: 120

输出: 21

​ 这题我的思路是首先将字符串转换为String类型,判断是否为负数,如果是负数的话将符号取出并作标记,然后反转String,最后利用Integer.valueOf()转换回去。

这里踩了个坑,题目输入的是一个 32 位整数,当你输入的这个数足够大,反转后的数可能会大于int的最大值,也可能会小于int的最小值(Integer.MAX_VALUE = -215 Integer.MIN_VALUE = 215 - 1),所以我们在反转后不能直接将其转换为int类型。

AC代码:

class Solution {
   
    public int reverse(int x) {
   
        //将x转换为String类型再进行处理
        String numStr = x + "";
        boolean bool = false;
      	//判断是否为负数
        if (numStr.charAt(0) == '-') {
   
            numStr = numStr.substring(1);
            bool = true;
        }
        numStr = reStr(numStr);
        
      	//先将其转换为double类型,判断是否超出int的取值范围
        double flag = Double.valueOf(numStr);
        if(flag > Integer.MAX_VALUE || flag < Integer.MIN_VALUE)    return 0;
    
        return bool ? -Integer.valueOf(numStr) : Integer.valueOf(numStr);
    }
    
    public static String reStr(String str) {
   
        char[] charArr = str.toCharArray();
        
        int left = 0, right = str.length() - 1;
        while (left < right) {
   
            char temp = charArr[left];
            charArr[left] = charArr[right];
            charArr[right] = temp;
            left++;
            right--;
        }
        
        return String.valueOf(charArr);
    }
}

官方代码:

class Solution {
   
    public int reverse(int x) {
   
        int rev = 0;
        while (x != 0) {
   
            int pop = x % 10;
            x /= 10;
            if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
            if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
            rev = rev * 10 + pop;
        }
        return rev;
    }
}

字符串中的第一个唯一字符

给定一个字符串,找到它的第一个不重复的字符,并返回它的__索引__。如果不存在,则返回 -1。

示例一:

输入: s = “leetcode”

输出: 0

示例二:

输入: s = “loveleetcode”

输出: 2

注意事项: 可以假定该字符串只包含小写字母。

​ 这题我的思路是有Map来储存各个字符的数目,最后再遍历一遍Map找出数量为 1 的字符的返回其下标,否则返回 -1。

AC代码:

class Solution {
   
    public int firstUniqChar(String s) {
   
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        
        //map.getOrDefault(key->value, default)
        for (int i = 0; i < s.length(); i++)    
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
        
        for (int i = 0; i < s.length(); i++)
            if(map.get(s.charAt(i)) == 1)
                return i;
        
        return -1;
    }
}

有效的字母异位词

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

示例一:

输入: s = “anagram”, t = “nagaram”

输出: true

示例二:

输入: s = “rat”, t = “car”

输出: false

说明: 可以假设字符串只包含小写字母。

​ 如果 s 和 t 是字母异位词的话,那么它们的各字母个数一定是相等的。

​ 假设字符串中只包含小写字母,创建一个大小为 26 的数组来统计 s 中的各字符个数,再用 t 中的各字符个数减去对应的字符的值,如果最后数组中任意一个值不为 0 ,则说明 s 和 t 不是字母异位词。

AC代码:

class Solution {
   
    public boolean isAnagram(String s, String t) {
   
        if (s.length() != t.length())   return false;
        
        int[] cnt = new int[26];
        for (int i = 0; i < s.length(); i++) {
   
            cnt[s.charAt(i) - 'a']++;
            cnt[t.charAt(i) - 'a']--;
        }
        
        for (int i = 0; i < 26; i++)
            if (cnt[i] != 0) return false;
        
        return true;
    }
}

字符串转换整数

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

  • 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
  • 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
  • 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

示例一:

输入: “42”

输出: 42

示例二:

输入: " -42"

输出: -42

示例三:

输入: “4193 with words”

输出: 4193

示例四:

输入: “words and 987”

输出: 0

示例五:

输入: “-91283472332”

输出: -2147483648

提示:

  • 本题中的空白字符只包括空格字符 ' '
  • 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (-231 − 1)INT_MIN (−231)

​ 我们分情况讨论:

  1. 第一个非空字符非数字同时也不是正负号 -> Next。
  2. 第一个非空字符为正负号 -> 记录符号。
  3. 第一个非空字符为正负数 -> 开始转换。

​ 这里需要注意,在进行转换前总要进行数字的校验,查看转换后数字是否大于 Integer.MAX_VALUE 或小于 Integer.MIN_VALUE ,这里的处理和**「整数反转」**中官方代码的处理方法是一样的。

AC代码:

class Solution {
   
    public int myAtoi(String str) {
   
        str = str.trim();
        if (str.length() == 0)  return 0;
        if ((str.charAt(0) < '0' || str.charAt(0) > '9') && str.charAt(0) != '-' && str.charAt(0) != '+') 
            return 0;
        
        boolean bool = false;
        if (str.charAt(0) == '-'){
   
            bool = true;
            str = str.substring(1);
        }
        else if (str.charAt(0) == '+')
            str = str.substring(1);
        
        int ans = 0;
        for (int i = 0; i < str.length(); i++) {
   
            if (str.charAt(i) < '0' || str.charAt(i) > '9') break;
            int pop = bool ? -Integer.valueOf(str.charAt(i) - '0') : Integer.valueOf(str.charAt(i) - '0');
            if (ans > Integer.MAX_VALUE / 10 || (ans == Integer.MAX_VALUE / 10 && pop > 7)) return Integer.MAX_VALUE;
            if (ans < Integer.MIN_VALUE / 10 || (ans == Integer.MIN_VALUE / 10 && pop < -8)) return Integer.MIN_VALUE;
            ans = ans * 10 + pop;
        }
        return ans;
    }
}

实现 strStr()

实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1

示例一:

输入: haystack = “hello”, needle = “ll”
输出: 2

示例二:

输入: haystack = “aaaaa”, needle = “bba”

输出: -1

说明:

needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

滑动窗口。设一个大小为 needle.length()的窗口,从 haystack 的头部开始移动,每次移动都遍历一次,如果满足条件则返回窗口最左端的下标,否则返回 -1。

​ 这里要注意的是如果 needle = “”,则能匹配任何的 haystack 。

AC代码:

class Solution {
   
    public int strStr(String haystack, String needle) {
   
        if (needle.length() == 0)   return 0;
        if (haystack.length() < needle.length() || haystack.length() == 0)    return -1;
        
        int left = 0, right = needle.length();
        while (right <= haystack.length()) {
   
            for (int i = left; i <= right; i++) {
   
                if (haystack.charAt(i) != needle.charAt(i - left))
                    break;
                if (haystack.charAt(i) == needle.charAt(i - left) && i == right - 1)
                    return left;
            }
            
            left++;
            right++;
        }
        
        return -1;
    }
}

跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例一:

输入: [2,3,1,1,4]

输出: true

示例二:

输入: [3,2,1,0,4]

输出: false

​ 遍历数组,标记你能去到的最右端。

AC代码:

class Solution {
   
    public boolean canJump(int[] nums) {
   
        if (nums.length == 1)   return true;
        int last = nums.length - 1;

        int index = nums[0];
        for (int i = 0; i <= index; i++) {
   
            for (int j = i; j <= i + nums[i]; j++){
   
                if (j == i) continue;
                if (j + nums[j] >= last)    return true;
                index = Math.max(j + nums[j], index);
            }
        }
        return false;
    }
}

官方代码:

public class Solution {
   
    public boolean canJump(int[] nums) {
   
        int n = nums.length;
        int rightmost = 0;
        for (int i = 0; i < n; ++i) {
   
            if (i <= rightmost) {
   
                rightmost = Math.max(rightmost, i + nums[i]);
                if (rightmost >= n - 1) {
   
                    return true;
                }
            }
        }
        return false;
    }
}

盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

示例一:

输入: [1,8,6,2,5,4,8,3,7]

输出: 49

示例图

嵌套循环双指针

​ 双指针。定义一个 left 指针和一个 right 指针,移动高度更低的一端。

AC代码:

class Solution {
   
    public int maxArea(int[] height) {
   
        int ans = 0;
        int left = 0, right = height.length - 1;

        while (left < right) {
   
            ans = Math.max(ans, (right - left) * Math.min(height[left], height[right]));
            if (height[left] <= height[right])   left++;
            else   right--;
        }

        return ans;
    }
}
class Solution {
   
    public int maxArea(int[] height) {
   
        int ans = 0;

        for (int left = 0; left < height.length; left++)
            for (int right = left + 1; right < height.length; right++)
                ans = Math.max(ans, (right - left) * Math.min(height[left], height[right]));

        return ans;
    }
}

统计「优美子数组」

给你一个整数数组 nums 和一个整数 k。

如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

示例一:

输入: nums = [1,1,2,1,1], k = 3

输出: 2

解释: 包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。

示例二:

输入: nums = [2,4,6], k = 1

输出: 0

示例三:

输入: nums = [2,2,2,1,2,2,1,2,2,2], k = 2

输出: 16

​ 其实就是算 k 个奇数数字两边的偶数数字数目,然后将两边的偶数数字数目 + 1再相乘。因为不能确定长度,所以这里用动态数组记录奇数数字两边的偶数数字个数,最后遍历相乘。

AC代码:

class Solution {
   
    public int numberOfSubarrays(int[] nums, int k) {
   
        ArrayList<Integer> arr = new ArrayList<>();

        int cnt = 0;
        for (int i = 0; i < nums.length; i++) {
   
            if (nums[i] % 2 == 0)   ++cnt;
            else {
   
                arr.add(cnt + 1);
                cnt = 0;
            }
        }
        arr.add(cnt + 1);		//单独处理最后的cnt,不然最后一组的右边偶数数字会漏掉

        int ans = 0;
        int index = 0;
        while (index + k < arr.size()) {
   
            ans += arr.get(index) * arr.get(index + k);
            index++;
        }

        return ans;
    }
}

在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例一:

输入: nums = [5, 7, 7, 8, 8, 10], target = 8

输出: [3, 4]

示例二:

输入: nums = [5, 7, 7, 8, 8, 10], target = 6

输出: [-1, -1]

​ 二分查找的变形。因为是已经按照升序排序的数组,所以二分查找很大的降低时间复杂度。

AC代码:

class Solution {
   
    public int[] searchRange(int[] nums, int target) {
   
        int left = 0, right = nums.length - 1;

        while (left <= right) {
   
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
   
                int minIndex = mid, maxIndex = mid;
                while (nums[minIndex] == target){
   
                    minIndex--;
                    if (minIndex < 0)   break;	//	判断是否会越界
                }
                while (nums[maxIndex] == target) {
   
                    maxIndex++;
                    if (maxIndex >= nums.length)    break;	//	判断是否会越界
                }
                return new int[]{
   minIndex + 1, maxIndex - 1};
            }   else if (nums[mid] < target) {
   
                left = mid + 1;
            }   else if (nums[mid] > target) {
   
                right = mid - 1;
            }
        }

        return new int[]{
   -1, -1};
    }
}

搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

示例一:

输入:

matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]

target = 3

输出: true

示例二:

输入:

matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13

输出: false

​ 根据这个二维矩阵的两个特性去遍历,尽量做到遍历次数最少。注意要先排除数组的特殊情况再进行下面操作,不然可能会数组越界等等问题。

  1. 先判断这个数在哪一行,并记录是第几行。

    1. 再遍历相应的那一行找到该数

AC代码:

class Solution {
   
    public boolean searchMatrix(int[][] matrix, int target) {
   
        if (matrix.length == 0 || matrix[0].length == 0) return false;

        int row = matrix.length, col = matrix[0].length;
        int cur = 0;

      	//	找到对应的行
        for (int i = 0; i < row; i++) {
   
            if (target > matrix[i][0] && target < matrix[i][col - 1]){
   
                cur = i;
                break;
            }   else if (target == matrix[i][0] || target == matrix[i][col - 1]) {
   
                return true;
            }
        }
				//	根据行号找target
        for (int i = 0; i < col; i++)
            if (matrix[cur][i] == target)   return true;

        return false;
    }
}

相对名次

给出 N 名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌。前三名运动员将会被分别授予 “金牌”,“银牌” 和“ 铜牌”(“Gold Medal”, “Silver Medal”, “Bronze Medal”)。

(注:分数越高的选手,排名越靠前。)

示例一:

输入: [5, 4, 3, 2, 1]

输出: [“Gold Medal”, “Silver Medal”, “Bronze Medal”, “4”, “5”]

解释: 前三名运动员的成绩为前三高的,因此将会分别被授予 “金牌”,“银牌”和“铜牌” (“Gold Medal”, “Silver Medal” and “Bronze Medal”).
余下的两名运动员,我们只需要通过他们的成绩计算将其相对名次即可。

提示:

  1. N 是一个正整数并且不会超过 10000。

  2. 所有运动员的成绩都不相同。

    二分查找降低时间复杂度。先复制一个数组然后排序,用二分查找去查找名次。

AC代码:

class Solution {
   
    public String[] findRelativeRanks(int[] nums) {
   
        int[] sort = nums.clone();
        Arrays.sort(sort);
        String[] ans = new String[nums.length];

        int index = 0;
        for (int i = 0; i < nums.length; i++) {
   
            int rank = binSearch(sort, nums[i]) + 1;
            if (rank == nums.length)   ans[index++] = "Gold Medal";
            else if (rank == nums.length - 1)   ans[index++] = "Silver Medal";
            else if (rank == nums.length - 2)   ans[index++] = "Bronze Medal";
            else    ans[index++] = nums.length - rank + 1 + "";
        }

        return ans;
    }
    int binSearch(int[] sort, int target) {
   
        int left = 0, right = sort.length - 1;
        while (left <= right) {
   
            int mid = (left + right) / 2;
            if (sort[mid] == target)    return mid;
            else if (sort[mid] < target)    left = mid + 1;
            else if (sort[mid] > target)    right = mid - 1;
        }

        return -1;
    }
}

旅行终点站

给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。

题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。

示例一:

输入: paths = [[“London”,“New York”],[“New York”,“Lima”],[“Lima”,“Sao Paulo”]]

输出: “Sao Paulo”

解释: 从 “London” 出发,最后抵达终点站 “Sao Paulo” 。本次旅行的路线是 “London” -> “New York” -> “Lima” -> “Sao Paulo” 。

示例二:

输入: paths = [[“B”,“C”],[“D”,“B”],[“C”,“A”]]

输出: “A”

解释:

所有可能的线路是:
“D” -> “B” -> “C” -> “A”.
“B” -> “C” -> “A”.
“C” -> “A”.
“A”.

示例三:

输入: paths = [[“A”,“Z”]]

输出: “Z”

找到一个没有出度的节点。用一个Set把所有起点站存进去,然后遍历所有终点站,如果这个站点不包含在Set里,那这个站就是终点站。

AC代码:

class Solution {
   
    public String destCity(List<List<String>> paths) {
   
        Set<String> city = new HashSet<>();
        for (List<String> start: paths)
            city.add(start.get(0));

        for (List<String> end: paths)
            if (!city.contains(end.get(1)))  return end.get(1);

        return "";
    }
}

保护城市天际线

在二维数组grid中,grid[i][j]代表位于某处的建筑物的高度。 我们被允许增加任何数量(不同建筑物的数量可能不同)的建筑物的高度。 高度 0 也被认为是建筑物。

最后,从新数组的所有四个方向(即顶部,底部,左侧和右侧)观看的“天际线”必须与原始数组的天际线相同。 城市的天际线是从远处观看时,由所有建筑物形成的矩形的外部轮廓。 请看下面的例子。

建筑物高度可以增加的最大总和是多少?

示例一:

输入: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]]

输出: 35

解释:

The grid is:
[ [3, 0, 8, 4],
[2, 4, 5, 7],
[9, 2, 6, 3],
[0, 3, 1, 0] ]

从数组竖直方向(即顶部,底部)看“天际线”是:[9, 4, 8, 7]
从水平水平方向(即左侧,右侧)看“天际线”是:[8, 7, 9, 3]

在不影响天际线的情况下对建筑物进行增高后,新数组如下:

gridNew = [ [8, 4, 8, 7],
[7, 4, 7, 7],
[9, 4, 8, 7],
[3, 3, 3, 3] ]

说明:

  • 1 < grid.length = grid[0].length <= 50。
  • grid[i][j] 的高度范围是: [0, 100]。
  • 一座建筑物占据一个grid[i][j]:换言之,它们是 1 x 1 x grid[i][j] 的长方体。

​ 用两个数组分别存储水平和竖直方向的最高高度,最后遍历作差。

AC代码:

class Solution {
   
    public int maxIncreaseKeepingSkyline(int[][] grid) {
   
        int[] row = new int[grid.length], col = new int[grid[0].length];

        for (int i = 0; i < grid.length; i++) {
   
            int max = 0;
            for (int j = 0; j < grid[i].length; j++)
                max = Math.max(max, grid[i][j]);
            row[i] = max;
        }
        for (int i = 0; i < grid[0<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值