Leetcode 1-10刷题笔记(非困难题目)

1.两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解题思路

1.暴力枚举,不做介绍,时间复杂度O(N^2)

2.hash运算,也是一种分治法思想,将两数之和的运算可以拆分成在选择一个数的前提下,再寻找是否存在target - num[i]的值(这种思想运用在三数之和上可能更明显)。


class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashMap.containsKey(target - nums[i])) {
                return new int[]{hashMap.get(target - nums[i]), i};
            }
            hashMap.put(nums[i], i);
        }
        return new int[0];
    }
}

2.链表的两数之和

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0开头。

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

解题思路

这道题主要的难点就在于
1.进位的考虑,所以我们需要额外定义一个 int 用于存储进位。
2.链表长度可能存在差异,所以根据这个需要对更长的链表在第一次循环结束后再次进行循环遍历。(第一次循环使得较短的链表遍历完毕)

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = new ListNode();
        ListNode temp = head;
        int carry = 0;
        while(l1 != null && l2 != null) {
            temp.next = new ListNode((l1.val + l2.val + carry) % 10, null);
            System.out.println(carry);
            carry = (l1.val + l2.val + carry) / 10;
            temp = temp.next;
            l1 = l1.next;
            l2 = l2.next;
        }

        while(l1 != null) {
            temp.next = new ListNode((l1.val + carry) % 10,null);
            carry = (l1.val + carry) / 10;
            temp = temp.next;
            l1 = l1.next;
        }

        while(l2 != null) {
            temp.next = new ListNode((l2.val + carry) % 10,null);
            carry = (l2.val + carry) / 10;
            temp = temp.next;
            l2 = l2.next;
        }

        if(carry > 0) {
            temp.next = new ListNode(carry,null);
        }
        return head.next;
    }
}

class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

3.最长非重复子串

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

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

解题思路

本题需要考虑的点是
1.字符不重复性
2.最长子串

首先我们通过阅读题目可以大概直接这一定是一个滑动窗口问题,因为这个子串有很多个,然后我们需要求解的是最长不重复子串,说明这个我们需要定义一个可变子串(可伸长,可收缩)。

所以我们需要定义一个滑动窗口(start = 0 和 end = -1 ),我这里end = -1 代表我这个窗口是个双闭区间,即两个端点的元素都可以算在子串中。

现在我们需要考虑的是当遇到重复字符时如何移动滑动窗口
保证字符唯一性我们可以用Map,Set集合,当遇到重复字符时,我们需要知道上一个出现该字符的下标位置,我们从该下标的下一位开始计数,那么就算包含当前字符也仍是非重复子串。

例如:abcad
当前子串为abc,当遇到最后一个a 时,发现重复,则获取到上一次出现a的下标为0,则从 0的下一位 1 开始计算子串,即此时子串为bca。
接着往下匹配则d也进入子串。
最终的最长非重复子串为bcad,length = 4。

所以我们可以初步的出改变窗口的伪代码

  1. 遇到重复
  2. int index = map.get(重复字符) //此处获取上一次出现重复字符的下标
  3. start = index + 1;
  4. end = 当前遍历的下标(即当前重复的字符下标)

但是这样写也会出现问题:例如
abba
子串变化 ab - > b ->当遍历到最后一个a时候,我们发现我们此时获取的index = 0, 那么按照上述伪代码此时start = 1,则此时子串为bba,发生重复。

此处的原因在于我们保留的上一次a的下标在重复字符的前面,所以导致窗口包含了重复部分。

此时的我们需要明白一点!!!!
我们定义的[start,end]窗口是绝对的无重复子串,在start前面的部分说明都存在重复部分,需要舍弃。

所以此时伪代码只需要改一个部分
start = index + 1 -> start = max(index + 1,start);

至此难点解析结束

import java.util.*;

class Solution {
    //最长子串
    //滑动窗口法
    public int lengthOfLongestSubstring(String s) {
        Map<Character,Integer> map = new HashMap<>();
        char[] chars = s.toCharArray();
        //开始字符
        int start = 0;
        //末尾字符
        int end = -1;
        int maxLength = 0;
        for(int i=0; i<chars.length; i++) {
            //存放对应位置的下标
            if(!map.containsKey(chars[i])) {
                map.put(chars[i],i);
                end += 1;
            }else {//出现重复
                //获取出现此字符的最近的一次位置
                int lastIndex = map.get(chars[i]);
                maxLength = Math.max(maxLength,end - start + 1);
                //start为重复字符的下一位,这里需要个最大值取值,
                // 原因:因为我们start代表是非重复部分,在start之前会有重复部分,
                // 所以如果我们获取的下标小于start,那[start,end]子串部分就会出现重复
                //举例 abba
                start = Math.max(lastIndex + 1,start);
                end = i;
                //清除第一次出现的重复字符
                map.put(chars[i],i);
            }
        }
        maxLength = Math.max(maxLength,end - start + 1);
        return maxLength;
    }

    public static void main(String[] args) {
        String s = "bbbb";
        Solution solution = new Solution();
        System.out.println(solution.lengthOfLongestSubstring(s));
    }
}

5.最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

解题思路
首先我们知道回文串的定义,即左右对称。

通过细致了解可以发现,一个回文串去掉两边的字符后,仍然是一个回文串,根据这个条件,我们似乎找到了一个状态转移方程,那么我们可以考虑使用动态规划解决。

我们定义一个boolean的二维数组,横坐标代表子串左边界,纵坐标代表子串右边界,这个二维数组用于表示由左右边界所代表的子串是否是回文子串。

我们在开始动态规划之前需要对这个二维数组进行一下初始化操作,因为我们知道单个字符也算是一种特殊的回文串。将数组对角线处(即left = right)的值赋值true。

状态转移方程
当子串左右字符相同时。

1.right - left + 1 >= 3(代表子串长度大于等于3)此时dp[left][right] = dp[left + 1][right - 1]
2.right - left + 1 == 2 dp[left][right] = true;
3.right - left + 1 == 1 (即left == right)

至此解题结束

import java.util.*;

class Solution {
    //最长回文子串
    //动态规划实现最佳
    //一个回文子串,去掉两边字符后仍然是回文子串
    //2中情况 abba型 和 aba型
    public String longestPalindrome(String s) {
        boolean[][] dp = new boolean[s.length()][s.length()];
        int maxLen = 0;
        String target = null;
        //初始化
        for(int i=0;i<s.length();i++) {
            dp[i][i] = true;
        }
        for(int right=0; right<s.length(); right++) {
            for(int left=0; left<=right; left++) {
                if(s.charAt(left) == s.charAt(right)) {
                    //代表当前字符串长度必须 >= 3
                    //如果当前子串长度 = 2 那么去掉两边以后,此时的left < right,对应的dp为false,而正确应该为true
                    if(right - left + 1 >= 3) {
                        dp[left][right] = dp[left + 1][right - 1];
                    }else {
                        dp[left][right] = true;
                    }
                    int targetLen = right - left + 1;
                    if(dp[left][right] && maxLen < targetLen) {
                        target = s.substring(left,right + 1);
                        maxLen = targetLen;
                    }
                }else {
                    dp[left][right] = false;
                }
            }
        }
        for(int i=0;i<s.length();i++) {
            for(int j=0;j<s.length();j++) {
                System.out.print(dp[i][j] + " ");
            }
            System.out.println("");
        }
        return target;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.longestPalindrome("cbbd"));
    }
}

6. Z字形变换

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

该题目是一个找规律的题目,没有什么好的算法,具体代码如下。

class Solution {
    public String convert(String s, int numRows) {

        if (numRows == 1) return s;

        StringBuilder ret = new StringBuilder();
        int n = s.length();
        int cycleLen = 2 * numRows - 2;

        for (int i = 0; i < numRows; i++) {
            for (int j = 0; j + i < n; j += cycleLen) {
                ret.append(s.charAt(j + i));
                if (i != 0 && i != numRows - 1 && j + cycleLen - i < n)
                    ret.append(s.charAt(j + cycleLen - i));
            }
        }
        return ret.toString();
    }
}

7.反转整数

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

输入: 123
输出: 321

注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

本题没太大难度,可能最后的注意部分需要注意一下,别的没有任何难度,这里直接上代码

import java.util.*;

class Solution {
    //整数反转
    public int reverse(int x) {
        int num = x > 0 ? x : -x;
        int target = 0;
        int carry = getSize(num) - 1;
        while(num > 0) {
            if(Integer.MAX_VALUE - target < Math.pow(10,carry)) {
                return 0;
            }
            target += (num % 10) * Math.pow(10,carry);
            carry--;
            num /= 10;
        }
        return x > 0 ? target : -target;
    }

    public int getSize(int x) {
        int size = 0;
        while(x > 0) {
            x /= 10;
            size++;
        }
        return size;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.reverse(-123));
    }
}

8.字符串转整数

请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。

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

解题思路
针对题目要求解读:
1.如果第一个非空字符为 + ,- ,数字则继续进行,如果为其他的则可以直接返回0。
2.对于数字字符后面的非数字字符可以不用管。
3.对于数字大小有限制,如果越界返回对应的极值。

针对这三点进行代码编写就行了,本题没有太多难度,唯一可能需要注意的就是在于越界部分如何判断需要使用比较技巧的办法。

技巧:我们在每次对target值 * 10 之前先与Integer.MAXVALUE / 10 判断。
1.如果小于那一定不会越界。
2.如果刚好等于时,则还需要增加条件,即即将加的数必须小于 8 ,如果 >= 8则代表越界。
3.如果大于那也是一定是越界的。

本体题解结束

import java.util.*
class Solution {
    //字符串转整数
    public int myAtoi(String s) {
        int target = 0;
        //去除开头多余空格
        s = s.trim();
        //标识符号(true代表整数,false代表负数,默认为正)
        boolean symbol = true;
        //标记开头第一个是否为符号
        boolean flag = false;
        //标识是否越界
        boolean outer = false;
        if(s.length() == 0) {
            return 0;
        }
        //标记符号
        if(s.charAt(0) == '+' || s.charAt(0) == '-') {
            symbol = s.charAt(0) == '+';
            flag = true;
        }
        //第一个字符如果为非数字或者非符号那么直接返回0
        if(!flag && (s.charAt(0) > '9' || s.charAt(0) < '0')) {
            return 0;
        }
        for(int i= flag?1:0; i<s.length(); i++) {
            if(s.charAt(i) > '9' || s.charAt(i) < '0') {
                break;
            }
            int nums = s.charAt(i) - 48;
            if(target > Integer.MAX_VALUE / 10 || (target == Integer.MAX_VALUE / 10  && nums > 7)) {
                outer = true;
                break;
            }
            target *= 10;
            target += nums;
        }

        if(outer) {
            return symbol ? Integer.MAX_VALUE : Integer.MIN_VALUE;
        }
        return symbol ? target : -target;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.myAtoi("2147483648"));
    }
}

9.回文数

本题比较简单。
1.对于负数部分是不可能是回文数的,直接返回false。
2.对于 0 算是回文数
3.对于整数的话我们可以选择将其转化成字符串进行双指针判断。

import java.util.*;

class Solution {
    public boolean isPalindrome(int x) {
        if(x < 0) {
            return false;
        }
        if(x == 0) {
            return true;
        }

        String num = x + "";
        int left = 0;
        int right = num.length() - 1;
        while(left <= right) {
            if(num.charAt(left) == num.charAt(right)) {
                left++;
                right--;
            }else {
                return false;
            }
        }
        return true;
        
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值