LeetCode力扣热题一百·自我解法记录(JAVA版本·仅代码)

本文介绍了如何使用哈希表解决'两数之和'问题,以及在链表操作中实现加法和进位的不同方法。探讨了模拟进位的复杂版本,以及如何优化最长不重复子串和最长回文子串的算法。还涉及了盛最多水的容器问题的暴力破解与官方高效解决方案对比。
摘要由CSDN通过智能技术生成

1. 两数之和·哈希表

题目链接:力扣·两数之和·简单

import java.util.HashMap;

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //创建哈希表
            HashMap<Integer,Integer> map = new HashMap<>();
        //向哈希表中录入元素
            for(int i=0;i<nums.length;i++)
            {
                map.put(nums[i],i);
            }
        //寻找固定值
            for(int j=0;j<nums.length;j++)
            {
                if(map.containsKey(target-nums[j]))//找到了,返回两个下标
                {
                    int index = map.get(target-nums[j]);
                    if(j!=index) return new int[]{j,index};
                }
            }  
        //要有一个出口(返回空数组)
        return new int[0];
    }
}

2. 两数之和

初始思路:把两个数读出来直接加,不行,太多错误,会导致无法进位(我他*&*()*反正就是不能进位,改成long , double都不行)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; } //赋值
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }//链接函数
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //读数l1,存入x
            int  i=1;
            int  x=0;
            while(l1 != null){
                x = x + l1.val*i;
                i = i*10;
                l1 = l1.next;
            }
        //读数l2,存入y
            i=1;
            int  y=0;
            while(l2 != null){
                 y= y + l2.val*i;
                 i = i*10;
                 l2 = l2.next;
            }
        //加和
            int z = x+y;
        //分离数位,尾插法建立链表
            ListNode head = null;//建立头结点
            ListNode tail = null;//建立尾结点
        //特殊处理:如果和为0
            if(z==0)
            {
                head = new ListNode(0);
            }
        //正常情况
            while(z!=0){
                if(head == null)
                {
                    head = tail = new ListNode(z%10);
                }
                else{
                    tail.next = new ListNode(z%10);
                    tail = tail.next;
                }
                z = z/10; 
            }
        //返回头指针
        return head;
    }
}

思路2·模拟进位

【个人超级复杂版进位操作(数字大的时候不行)】

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //建立和链表
        ListNode head = null;
        ListNode tail = null;
        //设定一个计数器i,和一个代表乘以几个10的计数器,以及一个进位记录jin
        int i = 0;
        int jin = 0;
        //同步从个位读取两个链表,直到其中一个停止
        while(l1!=null || l2!=null)
        {
            int x = l1.val + l2.val;
            int x0=x;

            //建立新链表
            if(head==null)
            {
                head = tail = new ListNode(x%10+jin);
            }
            else{
                tail.next = new ListNode(x%10+jin);
                tail = tail.next;
            }
            //计算下次是否需要进位
            if(x0>9)//需要进位
                jin = 1;
            else//不需要进位
                jin = 0;
            //继续循环
            l1 = l1.next;
            l2 = l2.next;
        }
        //两个链表其中一个先终止了,把剩下的那个链表直接接入结果链表
        if(l1!=null)
        {
        while(jin!=0 && l1!= null)
        {
            int x = l1.val + jin;
            int x0=x;

            tail.next = new ListNode(x%10);
            tail = tail.next;
            //判断下一位需不需要进位

            //计算下次是否需要进位
            if(x0>9)//需要进位
                jin = 1;
            else//不需要进位
                jin = 0;
            l1 = l1.next;
        }
        if(jin!=0 && l1==null)//进位进出多一位的情况
        {
            tail.next = new ListNode(1);
            tail = tail.next;
        }
        if(jin==0 && l1!=null)//需要把后续连入的情况
        {
            tail.next = l1;
        }
        }

        if(l2!=null)
        {
        while(jin!=0 && l2!= null)
        {
            int x = l2.val + jin;
            int x0=x;

            tail.next = new ListNode(x%10);
            tail = tail.next;
            //判断下一位需不需要进位

            //计算下次是否需要进位
            if(x0>9)//需要进位
                jin = 1;
            else//不需要进位
                jin = 0;
            l2 = l2.next;
        }
        if(jin!=0 && l2==null)//进位进出多一位的情况
        {
            tail.next = new ListNode(1);
            tail = tail.next;
        }
        if(jin==0 && l2!=null)//需要把后续连入的情况
        {
            tail.next = l2;
        }
        }

        return head;
    }
}

答案简洁版进位

重点就在于,先结束的链表用0补足

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //新建链表,给出头尾指针
        ListNode head = null, tail = null;
        //carry变量用来记录进位,需要进位则为1,否则为0
        int carry = 0;
        //两个链表只要还有一个非空,就继续操作
        while (l1 != null || l2 != null) {
            //这个方法里最重要的地方就在于,要用0补足先空了的链表
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            //计算新链表当前位的值,并建立链表
            int sum = n1 + n2 + carry;
            if (head == null) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            }
            carry = sum / 10;//计算进位
            //推动链表向下运算
            if (l1 != null) {
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        //如果都算完了还有进位,则意味着要多出一位,因为是加法,所以多出来的一位肯定是1
        if (carry > 0) {
            tail.next = new ListNode(carry);
        }
        return head;
    }
}

3.最长不重复子串·队列问题·滑动窗口

题目链接:

力扣-最长不重复子串

自我解题方法(错)(语法虽然没有毛病了,思路也没毛病,但运算结果就是不对。呵。)

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //建立一个队列
            Queue<String> queue = new LinkedList();
        //遍历字符串,送入队列并计数
            int len = 1;
            int max = len;
            int first = 1;
            for(int i=0;i<s.length();i++)
            {
                //如果是第一个元素,直接入队
                if(first == 1)
                {
                    first = 0;
                    queue.offer(s.substring(i,i+1));//入队
                }
                //非首元素
                else{
                    //如果和队首元素相同,得到当前不重复子串长度
                    if(s.substring(i,i+1)==queue.element())
                    //if(queue.element().equals(s.substring(i,i+1)))
                    {
                        //首先,进行不重复子串长度最大值比较
                        if(len > max)
                        {
                            max = len;//更新max值
                            len = 1; //len重新计数
                        }
                        //然后,弹出队首元素
                        String del = queue.poll();
                    }
                    else{//不相同,则将该元素入队并计数
                        queue.offer(s.substring(i,i+1));//入队
                        len++;//不重复子串长度计数
                    }
                }
            }
            if(len>max)//防止最大不重复子串在最后
                max = len;
            //返回max值
            return max;
    }
}

正解:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set<Character> occ = new HashSet<Character>();
        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;
    }
}

6. 最长回文子串

用到的主要结构:哈希表

【主要思想】

遍历字符串,每出现一个没见过的字符,就将其加入哈希表;哈希表中只保存每个元素第一次出现的位置。

如果在遍历字符串时,发现该元素已经出现过了,那么就查找哈希表,将(第一次出现的位置下标,当前下标)这个子字符串提取出来,判断这个子字符串是不是回文串。

如果是,则判断是否需要更新max值(最长回文串长度值);如果不是,则继续向下便利字符串。

(为了保证哈希表里只保存每个元素第一次出现的位置,只有当该元素不再哈希表中时才向内添加,否则不添加。)

【需要进行的特殊情况处理】

① 整个字符串无重复的情况——哈希表长度为零,返回第一个元素即可;

② 整个字符串全是重复的情况——哈希表为长度为1,直接返回整个字符串即可;

代码记录:

import java.util.HashMap;
class Solution {
    public String longestPalindrome(String s) {
        //建立哈希表,存储内容为字符元素及其下标
        HashMap<Character,Integer> map = new HashMap<>();
        int max = 0;
        int[] res = {0,0};//保存最长字符串的两个下标

        //遍历字符串,并存入哈希表
        int len = s.length();

        //特殊情况处理——如果该字符串中没有重复元素,那么就返回第一个字符值
        boolean nosame = true;

        //遍历字符串
        for(int i=0;i<len;i++)
        {         
            //判断哈希表中是否已经有该元素
            if(map.containsKey(s.charAt(i))){
                nosame=false;
                //如果有该元素,则找到该元素第一次出现的位置
                int x=map.get(s.charAt(i));
                //判断该子字符串是否为回文串,如果是,则计数
                    //首先截取该子串
                    String s1 = s.substring(x,i+1);//因为是前闭后开
                    //因为存在溢出问题,所以不截取该子串呢?

                    //判断是否是回文串
                    int i1=x;//头指针
                    int j1=i;//尾指针
                    boolean hui = true;
                    while(i1 < j1+1)
                    {
                        if(s.charAt(i1)!=s.charAt(j1))
                        {
                            hui = false;
                            break;
                        }
                        else{
                            i1++;
                            j1--;
                        }
                    }
                    //如果是回文串,更新max
                    if(hui == true)
                    {
                    int lens1 = s1.length();
                    if(lens1 > max)
                    {
                        max = lens1;
                        res[0]=x;
                        res[1]=i+1;
                    }
                    }
                    
                //如果当前元素为重复元素,那么不存入当前元素,即可保证哈希表中记录的是该元素第一次出现的位置
            }
            //如果该元素未重复,则将元素当前位置插入哈希表;
            map.put(s.charAt(i),i);
        }
        //无重复字符的特殊处理
        if(nosame == true)
        {
            return s.substring(0,1);
        }
        //全重复字符的特殊处理
        if(map.size()==1)
        {
            return s;
        }
        //返回最长回文子串
        return s.substring(res[0],res[1]);
    }
}

此题力扣的官方解法是动态规划,动态规划上学的时候我就没学明白,放在这里供大家参考吧。

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    public int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;
    }
}


11.盛最多水的容器

【原题地址】

力扣·盛最多水的容器·中等难度

【个人思路】

我的思路就是用双重循环进行暴力破解。每次遍历时,我只关心比我矮的垂线,因为这个比我矮的垂线会决定水位的高度,而我们之间下标差的绝对值就是水位的宽度。

故,暴力破解,高效完美。

但是力扣里面的输入例子里,非要给一个巨大的输入,你他喵的返回值就是个Int,整那么大谁能不溢出?!

哼。

在此记录一下我的暴力破解代码:

import java.lang.Math;

class Solution {
    public int maxArea(int[] height) {
        //暴力破解法

        //遍历数组,每次只关心值比当前值小的垂线,然后计算面积,不断更新迭代面积的最大值
        int max = 0;
        for(int i=0;i<height.length;i++)
        {
            for(int j=0;j<height.length;j++)
            {
                if(height[j]<height[i]+1)//如果j的垂线比i矮(或相等),那么它将决定水位的高度
                {
                    int size = height[j]*Math.abs(j-i); //计算面积
                    if(size>max)
                    {
                        max = size;//更新max值
                    }
                }
            }
        }

        //返回最大值
        return max;
    }
}

力扣官方的思路是这样的:

设置两个指针,一个头一个尾,每次就算这两个指针组成的长方体的面积;每次只移动比较矮的那个垂线的指针。

emmm我打了一遍代码,这次倒是通过了。不得不说官方这个时间复杂度确实小的多,只进行一次遍历。

import java.lang.Math;

class Solution {
    public int maxArea(int[] height) {
        int i=0;//设置头指针
        int j=height.length-1;//设置尾指针

        int max = 0;
        int size = 0;

        while(i<j) //两指针交汇即为遍历完整个链表
        {
            size = Math.min(height[i],height[j])*(j-i);//两个数里更小的那个决定水位高度
            if(size > max)
            {
                max = size;
            }

            //只移动较小值的指针
            if(Math.min(height[i],height[j])==height[i])
            {
                i++;
            }
            else{
                j--;
            }
        }

        return max;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值