力扣算法1-10题总结

官方代码均来自力扣力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

1.两数之和

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

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

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

我的代码:

暴力枚举,通过两次循环,枚举数组,寻找target-i

class Solution {
    public int[] twoSum(int[] nums, int target) {
        for(int i =0;i

官方优解:

使用哈希表,根据题目要求只需找出一组结果,那么就遍历一遍数组,每存一个数x判断哈希表中是否存在target-x,如果没有就将x存入哈希表,如果有就直接返回new数组返回target-x和x。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map hashtable = new HashMap();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }
}
2.两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

根本没做出来,不会。直接上官方题解,看代码注释。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(0);
        //创建完预先指针 需要一个移动指针来遍历链表
        ListNode cur = pre;
        //开始遍历两个链表 当两个链表都是空值时停止循环
        int carry=0;//carry是全局变量 需要while循环外判断最后是否进位
        while (l1!=null||l2!=null){
            //定义int类型保存两个结点相加的数据 sum表示和 carry表示进位
            int x =l1==null?0:l1.val;//需要判断此时结点是否为空值 为空的话就需要给0 不为空即value值
            int y =l2==null?0:l2.val;
            int sum = x+y+carry;
            carry=sum/10;//存进位
            sum=sum%10;//存和
            cur.next = new ListNode(sum);//创建第三条链表的求和结点 并把结点地址保留给当前结点的next地址
            cur=cur.next;//移动指针
            if(l1!=null){
                l1=l1.next;
            }
            if(l2!=null){
                l2=l2.next;
            }
        }
        //如果最后的进位是1 就要再创建一个新结点存1
        if(carry==1){
            cur.next=new ListNode(1);
        }
        //体现预先指针的作用了 因为移动指针会使结点地址丢失  所以用预先指针用next指向头节点的地址
        return pre.next;
    }
}

3.无重复字符的最长字串

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

我的代码:

class Solution {
   public static int lengthOfLongestSubstring(String s) {
        char[] chars = s.toCharArray();//拆分字符串 获取字符数组
        HashSet hashSet = new HashSet();//用set集合去重 找到所有不重复的元素 这时set的size就是长度的可能最大值
        for (char aChar : chars) {
            hashSet.add(aChar);
        }//将字符数组存入set集合
        int time ;//定义变量保存需要遍历size的次数
        if("".equals(s)){
            return 0;
        }//空字符串直接返回0       
 //因为此时无法重复的字串长度 最大就是size 所以外循环就从size开始逐渐缩小一 
//内循环就从字符数组的第一个字符开始每次截取字符串的size长度 
//只需要遍历到数组长度-size长度的数组下标停止 
//在内循环中 再用set集合去一次重 如果新set集合的size和截取的字符串长度不一样 那么就说明此次截取的字符串里有重复字符 不符合要求 
//相反 如果长度一样 就说明截取的字符串就是最长的无重复字符的字串
        for(time=hashSet.size();time>0;time--){
            for(int i=0;i<=s.length()-time;i++){
                char[] chars1 = s.substring(i, i + time ).toCharArray();
                HashSet hashSet1 = new HashSet();
                for (char c : chars1) {
                    hashSet1.add(c);
                }
                System.out.println(hashSet1.toString());
                if(hashSet1.size()!=time){

                }else if(hashSet1.size()==time){
                    return hashSet1.size();
                }
            }
        }
        return 1;
    }
}

官方代码: 运用滑动窗口(双指针算法) 

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set occ = new HashSet();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                // 不断地移动右指针
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }
}
4.寻找两个有序数组的中位数

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

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

我的代码:两个数组存入第三个数组 再用冒泡排序排顺序算中位数 

class Solution {
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int nums3[] = new int[nums1.length+nums2.length];
        for (int i=0;inums3[j+1]){
                    max=nums3[j];
                    nums3[j]=nums3[j+1];
                    nums3[j+1]=max;
                }
            }
        }
        double result=0;
        if(nums3.length%2==0){
            result = (nums3[nums3.length/2-1]+nums3[nums3.length/2])/2.0;
        }else if(nums3.length%2!=0){
            result= (nums3[nums3.length/2]);
        }
        return result;
    }
}

官方代码:

class Solution {
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if(nums1.length>nums2.length){
            int temp[]=nums1;
            nums1=nums2;
            nums2=temp;
        }//nums1为长度小的数组 nums2为长度大的数组
        int m=nums1.length;
        int n=nums2.length;
        int size = (m+n+1)/2;//定义分割线左边的元素数量 不用讨论奇偶性 奇数左边多一 偶数一样


        int left=0;//起始左边
        int right=m;//起始右边 (需要遍历nums1数组防止越界)


        while(leftnums2[j]){//分割线nums1左边元素大于nums2右边元素的话 就缩小找下一个区间[0,right(i-1)]
                right=i-1;
            }else{//找到了的话就让left等于查找到的i下标(也就是分割线的位置)
                left=i;
            }
        }
        int i=left;//(上分割线下标)
        int j=size-i;//(下分割线下标)
        int nums1LeftMax=i==0?Integer.MIN_VALUE:nums1[i-1];//(nums1分割线左边如果没有数就取整型最小 让他在下面比较函数中不会被选择)
        int nums1RightMax=i==m?Integer.MAX_VALUE:nums1[i];//(nums1分割线右边如果没有数就取整型最大 让他在下面比较函数中不会被选择)  
        int nums2LeftMax=j==0?Integer.MIN_VALUE:nums2[j-1];;//(nums2分割线左边如果没有数就取整型最小 让他在下面比较函数中不会被选择)
        int nums2RightMax=j==n?Integer.MAX_VALUE:nums2[j];//(nums2分割线右边如果没有数就取整型最大 让他在下面比较函数中不会被选择)
        if((m+n)%2==1){//判断奇偶性 奇数比较nums1和nums2左边最大的数 返回最大值
            return Math.max(nums1LeftMax,nums2LeftMax);
        }else {//偶数比较左边两数组最大和右边两数最小 求平均值返回
            return (double)(Math.max(nums1LeftMax,nums2LeftMax)+Math.min(nums1RightMax,nums2RightMax))/2;
        }
    }
}
5.最长回文子串

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

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串

我的代码: 暴力解法,两层循环  如果i和j相等 把里面的字符串反转 如果反转后的字符串和原字符串一样 就存长度和截取字符串 遍历所有的情况找最长的回文子串  学会了一种方法就是用stringbuilder的反转方法然后通过string里面的contentequals方法和stringbuilder比较内容是否相同。

 public static String longestPalindrome(String s) {
        String rs = null;
        int max =0;

        int flag=0;
        for (int i = 0; i < s.length(); i++) {
            for(int j=s.length()-1;j>i;j--) {
                if(s.charAt(i) == s.charAt(j)){
                    if (s.substring(i, j+1).contentEquals((new StringBuilder(s.substring(i, j+1)).reverse())) && s.substring(i, j+1).length() > max) {
                        max = s.substring(i, j+1).length();
                        rs = s.substring(i, j+1);
                        flag=1;
                    }
                }
            }
        }
        if(flag==0){
            return s.substring(0,1);
        }
        return rs;
    }

官方代码:动态规划dp数组  学了好长时间才能看懂

class Solution {
   public static String longestPalindrome(String s) {
       if(s.length()<2){
           return s;
       }
       char chars[] = s.toCharArray();
       boolean dp[][] = new boolean [s.length()][s.length()];//动态规划dp数组
       int maxlength=1;//回文子串最大长度
       int begin=0;//最长回文子串起始下标
       for(int i=0;imaxlength){//如果是回文子串并且大于之前长度 那么就记录长度和下标
               maxlength=j-i+1;
               begin=i;
                }
           }
       }
       return s.substring(begin,begin+maxlength);//截取字符串 返回最长回文子串
    }
}
6. N 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

我的代码: 一开始理解二维数组以为x就是横坐标 y就是纵坐标 其实不然

x表示行数 y表示列数  不过我用xy横纵坐标一个个往里填数 暴力求解

class Solution {
   public static String convert(String s, int numRows) {
        int n = 0;
        int flag = 0;
        int j = 0;
        int ii = 0;
        char chars[][] = new char[numRows][s.length()];
        if (numRows == 1) {
            return s;
        } else if (numRows == 2) {
            while (n < s.length()) {
                for(int x=0;x=s.length()){
                            break;
                        }
                        chars[y][x]=s.charAt(n);
                        n++;
                    }
                }
            }
        } else {
            while (n < s.length()) {
                if (flag == 0) {
                    for (int i = 0; i < numRows; i++) {
                        if (n == s.length()) {
                            break;
                        }
                        chars[i][j] = s.charAt(n);
                        ii = i;
                        n++;
                        flag = 1;
                    }
                    j++;
                } else if (flag == 1) {
                    if (ii - 1 == 1) {
                        chars[ii - 1][j] = s.charAt(n);
                        j++;
                        n++;
                        flag = 0;
                    } else if (ii - 1 != 1) {
                        chars[ii - 1][j] = s.charAt(n);
                        j++;
                        n++;
                        ii--;
                        flag = 1;
                    }
                }
            }
        }
        String ss = "";
        for (int x = 0; x < numRows; x++) {
            for (int y = 0; y < s.length(); y++) {
                if (chars[x][y] != '\u0000') {
                    ss = ss + "" + chars[x][y];
                }
            }
        }
        return ss;
    }
}

官方代码: 先找规律 将变换字符转化为周期问题 设行数numRow就是r 那么一个周期填的字符就是r+r-2个 填的列数为r-1  

class Solution {
   public static String convert(String s, int numRows) {
       int n=s.length();
       int r=numRows;
       if(r>=n||r==1){//如果只有一行或者字符串长度小于行数 直接返回字符串
           return s;
       }
       int t=r+r-2;//求一个周期有多少字符
       int c=(n+t-1)/t*(r-1);//求一共有多少列 用周期*每个周期列数 (n+t-1)可以确保最后一个不完整的周期也算一个周期
       char mat[][] = new char[r][c];
       for(int i=0,x=0,y=0;i
7.整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

我的代码: 纯是一坨 转来转去没什么意义

class Solution {
     public static int reverse(int x) {
        String s = String.valueOf(x);
        StringBuilder sb = new StringBuilder(s);
        long n;
        int i;
        if(sb.charAt(0)=='-'){
            sb.deleteCharAt(0);
            StringBuilder reverse = sb.reverse();
            String sb1 = "-"+reverse;
            n = Long.parseLong(sb1);
        }else {
            StringBuilder reverse = sb.reverse();
            String sb1=reverse+"";
            n = Long.parseLong(sb1);
        }
        if(n>2147483647||n<-2147483648)
        {
            return 0;
        }else{
            i=(int)n;
        }
        return i;
    }
}

官方代码:

class Solution {
     public static int reverse(int x) {
        int rev =0;//反转的整数
        int last=0;//末尾数字
        while(x!=0){//算完最后一位截止
            last=x%10;//求末尾数字
            x=x/10;//每次循环去一位
            if(rev>Integer.MAX_VALUE/10||rev

8. 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [−2^31,  2^31 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31 − 1 的整数应该被固定为 2^31 − 1 。
  6. 返回整数作为最终结果

我的代码:还是纯纯滴一坨屎

class Solution {
    public static int myAtoi(String s) {
        String ns = s.trim();
        int pflag=0;
        int rflag=0;
        int flag=0;
        int x=0;//chars1下标
        String ls="" ;//去零前的字符串
        String dls="" ;//去零后的字符串
        int n=0;//返回的int
        char[] chars = ns.toCharArray();
        char[] chars1 = new char[chars.length];
        char[] chars2 = new char[chars1.length];
        ArrayList list1 = new ArrayList();//去0集合
        for(int i=0;i47&&chars[i]<58||(chars[i]==43)||chars[i]==45){
                if(chars[i]==43){
                    if(chars1[0]!='\u0000'){
                        break;
                    }
                    ls="+";
                    pflag++;
                    continue;
                }else if(chars[i]==45){
                    if(chars1[0]!='\u0000'){
                        break;
                    }
                    ls="-";
                    rflag++;
                    continue;
                }
                chars1[x]=chars[i];
                x++;
            }else{
                break;
            }
        }
        if(rflag>1||pflag>1||(rflag==1&&pflag==1)){
            return 0;
        }
        for (char c : chars1) {
            ls=ls+c;
        }
        ls=ls.trim();

        chars2 = ls.toCharArray();
        for (char c : chars2) {
            list1.add(c);
        }
        int size = list1.size();
        for(int i=0;i11){
            if(dls.charAt(0)=='-'){
                return -2147483648;
            }else{
                return 2147483647;
            }
        }
        dls=dls.trim();
        if(dls.equals("")){
            return 0;
        }
        long l = Long.parseLong(dls);
        if(l>2147483647){
            return 2147483647;
        }else if(l<-2147483648){
            return -2147483648;
        } else{
            n=(int) l;
        }
        return n;
    }
}

官方代码:

package leetcode.onetoten;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

class Eighth {
    public static void main(String[] args) {
        String s = "+-42";
        System.out.println(myAtoi(s));
    }
    public static int myAtoi(String s) {
        int n=s.length();
        int index=0;
        int sign =1;
        int res=0;
        while(indexInteger.MAX_VALUE/10||(res==Integer.MAX_VALUE/10&&last>7)){//越界处理
                    return sign==1?Integer.MAX_VALUE:Integer.MIN_VALUE;
                }else{//推入处理
                    res =res*10+last;
                    index++;
                }
            }else{
                break;
            }
        }
        return res*sign;
    }
}

另一种方法:自动机  就是一个个字符传进去在自动机里面判断 挺好理解 就是里面的hashmap初始化用了双大括号初始化 是匿名内部类的一种方法 一开始看了好久没看懂 也就这个比较难理解

用字符来判断状态 然后通过状态改变自动机里面的属性 

class Solution {
    public int myAtoi(String str) {
        Automaton automaton = new Automaton();
        int length = str.length();
        for (int i = 0; i < length; ++i) {
            automaton.get(str.charAt(i));
        }
        return (int) (automaton.sign * automaton.ans);
    }
}

class Automaton {
    public int sign = 1;
    public long ans = 0;
    private String state = "start";
    private Map table = new HashMap() {{
        put("start", new String[]{"start", "signed", "in_number", "end"});
        put("signed", new String[]{"end", "end", "in_number", "end"});
        put("in_number", new String[]{"end", "end", "in_number", "end"});
        put("end", new String[]{"end", "end", "end", "end"});
    }};

    public void get(char c) {
        state = table.get(state)[get_col(c)];
        if ("in_number".equals(state)) {
            ans = ans * 10 + c - '0';
            ans = sign == 1 ? Math.min(ans, (long) Integer.MAX_VALUE) : Math.min(ans, -(long) Integer.MIN_VALUE);
        } else if ("signed".equals(state)) {
            sign = c == '+' ? 1 : -1;
        }
    }

    private int get_col(char c) {
        if (c == ' ') {
            return 0;
        }
        if (c == '+' || c == '-') {
            return 1;
        }
        if (Character.isDigit(c)) {
            return 2;
        }
        return 3;
    }
}
9.回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是

我的代码: 没用数学办法 用的builder里面的翻转求得

class Solution {
     public static boolean isPalindrome(int x) {
        StringBuilder stringBuilder = new StringBuilder();
        int i=0;
        int n=x;
        if(x<0){
            return false;
        }
        while(x/10!=0){
            i=x%10;
            x=x/10;
            stringBuilder.append(i);
        }
        stringBuilder.append(x);
        long l = Long.parseLong(String.valueOf(stringBuilder));
        if(l==n){
            return true;
        }else {
            return false;
        }
    }
}

官方代码: 给的代码解释通俗易懂 挺简单的

class Solution {
    public boolean isPalindrome(int x) {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int revertedNumber = 0;
        while (x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }

        // 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
        // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
        // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
        return x == revertedNumber || x == revertedNumber / 10;
    }
}
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 <= 20
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 . 和 *
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

一开始做不会dp 用自己脑子想情况太多做不出来 实在是无力 直接看官方代码

这道题最坑的地方感觉是在玩文字游戏  *号匹配0个一个和多个的时候是可以包括*前面的字符的

也就是说 c*可以是0个字符也可以是c也可以是多个c  

看b站视频讲的清楚一些 推荐这个up主【剑指Offer最优解】19. 正则表达式匹配 | 估计虐了不少人_哔哩哔哩_bilibili

官方代码:

class Solution {
    public boolean isMatch(String s, String p) {
        if(s==null||p==null){
            return true;
        }
        int n =s.length();
        int m =p.length();

        boolean dp[][] = new boolean[n+1][m+1];

        dp[0][0]=true;
        for(int j=2;j<=m;j++){
            if(p.charAt(j-1)=='*'){
                dp[0][j]=dp[0][j-2];
            }
        }

        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(p.charAt(j-1)!='*'){
                    if(p.charAt(j-1)=='.'||p.charAt(j-1)==s.charAt(i-1)){
                        dp[i][j]=dp[i-1][j-1];
                    }
                }else{
                    if(p.charAt(j-2)!=s.charAt(i-1)&&p.charAt(j-2)!='.'){
                        dp[i][j]=dp[i][j-2];
                    }else{
                        dp[i][j]=dp[i][j-1]||dp[i-1][j]||dp[i][j-2];
                    }
                }
            }
        }
        return dp[n][m];
    }
}

你可能感兴趣的:(leetcode,算法)