力扣算法练习与解答笔记(11-20)

这篇博客详细介绍了力扣算法题目,包括回文数的判断、盛最多水的容器问题、整数转罗马数字与罗马数字转整数、最长公共前缀、三数之和、最接近的三数之和、电话号码的字母组合、删除链表倒数第N个节点以及有效的括号问题,涵盖了多种算法思路和解题技巧。
摘要由CSDN通过智能技术生成
1、回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 :

输入: 121
输出: true

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。

解答:
过滤特殊情况,拿到反转数比较大小

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

给定 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。
示例:

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

解答:
1、循环遍历

class Solution {
    public int maxArea(int[] height) {
        int maxArea = 0;
        for(int i = 0; i < height.length; i++) {
            for(int j = 0; j < height.length; j++) {
                maxArea = Math.max(maxArea, Math.abs(j - i) * Math.min(height[j], height[i]));
            }
        }
        return maxArea;
    }
}

2、一次遍历

class Solution {
    public int maxArea(int[] height) {
        int maxArea = 0;
        int leftIdx = 0;
        int rightIdx = height.length - 1;
        while(leftIdx < rightIdx) {
            // 1、左右距离越大面积越大  2、最小高度越高面积越大
            maxArea = Math.max(maxArea, (rightIdx - leftIdx) * Math.min(height[leftIdx], height[rightIdx]));
            // 可用高度取决于高度较小的一边,所以高度较小的一边移动到下一位置
            if(height[leftIdx] < height[rightIdx]) {
                leftIdx++;
            } else {
                rightIdx--;
            }
        }        
        return maxArea;
    }
}
3、 整数转罗马数字

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。

示例:

输入: 3
输出: "III"

输入: 4
输出: "IV"

输入: 9
输出: "IX"

输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

解答:

class Solution {
    public String intToRoman(int num) {
        if(num < 1 || num > 3999) {
            return "输入1-3999的数字,当前数字:" + num;
        }
        int temp = num;
        int m = 0;
        if(temp >= 1000) {// 获取千位数值
            m = temp / 1000;  
            temp -= m * 1000;
        }
        int c = 0;
        if(temp >= 100) {// 获取百位数值
            c = temp / 100;
            temp -= c * 100;
        }
        int x = 0;
        if(temp >= 10) {// 获取十位数值
            x = temp / 10;
            temp -= x * 10;
        }
        int i = temp;// 获取个位数值
        
        // 组装罗马数字
        StringBuilder sb = new StringBuilder();
        // 千位
        for(int idx = 0; idx < m; idx++) {
            sb.append("M");
        }
        // 百位
        if(c == 9) {
            sb.append("CM");
        } else if(c >= 5) {
            sb.append("D");
            c -= 5;
            for(int idx = 0; idx < c; idx++) {
                sb.append("C");
            }
        } else if(c == 4) {
            sb.append("CD");
        } else {
            for(int idx = 0; idx < c; idx++) {
                sb.append("C");
            }
        }
        // 十位
        if(x == 9) {
            sb.append("XC");
        } else if(x >= 5) {
            sb.append("L");
            x -= 5;
            for(int idx = 0; idx < x; idx++) {
                sb.append("X");
            }
        } else if(x == 4) {
            sb.append("XL");
        } else {
            for(int idx = 0; idx < x; idx++) {
                sb.append("X");
            }
        }
        // 个位
        if(i == 9) {
            sb.append("IX");
        } else if(i >= 5) {
            sb.append("V");
            i -= 5;
            for(int idx = 0; idx < i; idx++) {
                sb.append("I");
            }
        } else if(i == 4) {
            sb.append("IV");
        } else {
            for(int idx = 0; idx < i; idx++) {
                sb.append("I");
            }
        }
        return sb.toString();
    }
}
4、罗马数字转整数

罗马数字规则与 第3题 相同

class Solution {
    public int romanToInt(String s) {
        Map<String, Integer> map = new HashMap<>();
        map.put("I", 1);
        map.put("V", 5);
        map.put("X", 10);
        map.put("L", 50);
        map.put("C", 100);
        map.put("D", 500);
        map.put("M", 1000);
        map.put("IV", 4);
        map.put("IX", 9);
        map.put("XL", 40);
        map.put("XC", 90);
        map.put("CD", 400);
        map.put("CM", 900);
        
        int result = 0;
        char[] ch = s.toCharArray();
        int len = ch.length;
        for(int i = 0; i < len; i++) {
            String tmp = ch[i] + "";
            if(i < len - 1) {
                String temp = tmp + ch[i+1];
                if(map.containsKey(temp)) {
                    result += map.get(temp);
                    i++;
                } else if(map.containsKey(tmp)) {
                    result += map.get(tmp);
                }
            } else if(map.containsKey(tmp)) {
                result += map.get(tmp);
            }
        }
        return result;
    }
}
5、最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 :

输入: ["flower","flow","flight"]
输出: "fl"

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

说明: 所有输入只包含小写字母 a-z 。

解答:

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length == 0) {
            return "";
        }
        String first = strs[0];
        
        char[] ch = first.toCharArray();
        int len = ch.length;
        
        StringBuilder sb = new StringBuilder();
        
        out:for(int i = 0; i < len; i++) {
            char temp = ch[i];
            for(int j = 1; j < strs.length; j++) {
                String str = strs[j];
                int strLen = str.length();
                if(strLen < i + 1) {
                    break out;
                }
                if(str.charAt(i) != temp) {
                    break out;
                }
            }
            sb.append(temp + "");
        }        
        return sb.toString();
    }
}
6、三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:

[
  [-1, 0, 1],
  [-1, -1, 2]
]

解答:

  1. 数组排序
  2. 遍历数组,取出一个值作为锚点
  3. 遍历锚点之外的元素,找出两数之和减去锚点值 == 0 的数
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        // 1、数组从小到大排序
        Arrays.sort(nums);
        // 2、遍历数组,从数组中找出 两数之和 等于 负的锚点值 的两个数,两数从锚点元素的下一个元素和数组末尾开始,往数组中间偏移
        int len = nums.length;
        int i = 0;//锚点元素下标
        for(; i < len; i++) {
            int num = nums[i];//两数之和的目标值
            int leftIdx = i + 1;
            int rightIdx = len - 1;
            while(leftIdx < rightIdx) {
                int left = nums[leftIdx];
                int right = nums[rightIdx];
                if(left + right == -num) {
                    List<Integer> item = new ArrayList<>();
                    item.add(num);
                    item.add(left);
                    item.add(right);
                    result.add(item);
                    while(leftIdx < rightIdx && nums[leftIdx] == nums[leftIdx + 1]) {//过滤左边指针遇到的重复值
                        leftIdx++;
                    }
                    while(leftIdx < rightIdx && nums[rightIdx] == nums[rightIdx - 1]) {//过滤右边指针遇到的重复值
                        rightIdx--;
                    }
                    leftIdx++;
                    rightIdx--;
                } else if (left + right < -num) {//小于目标值,left向右偏移才能增加 left+right 的值
                    while(leftIdx < rightIdx && nums[leftIdx] == nums[leftIdx + 1]) {//过滤左边指针遇到的重复值
                        leftIdx++;
                    }
                    leftIdx++;
                } else {//大于目标值,right向右偏移才能减少 left+right 的值
                    while(leftIdx < rightIdx && nums[rightIdx] == nums[rightIdx - 1]) {//过滤右边指针遇到的重复值
                        rightIdx--;
                    }
                    rightIdx--;
                }
            }
            while(i < len - 1 && num == nums[i+1]) {//过滤值相同的锚点
                i++;
            }
        }
        return result;
    }
}
7、最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如:

给定数组 nums = [-1,2,1,-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

解答:

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int result = nums[0] + nums[1] + nums[2];//至少有三个元素
        int len = nums.length;
        for(int i = 0; i < len; i++) {
            int anchor = nums[i];
            int leftIdx = i + 1;
            int rightIdx = len - 1;
            while(leftIdx < rightIdx) {
                int left = nums[leftIdx];
                int right = nums[rightIdx];
                int currResult = anchor + left + right;
                if (Math.abs(currResult - target) < Math.abs(result - target)) {//找到更接近的值并赋值
                    result = currResult;
                }                
                //偏移逻辑
                if(currResult == target) {
                    return target;
                } else if(currResult < target) {//三数之和小于目标值,要更接近,左侧指针右移
                    leftIdx++;
                } else {//三数之和大于目标值,要更接近,右侧指针左移
                    rightIdx--;
                }
            }
        }
        return result;
    }
}
8、电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

解答:

class Solution {
    String[] keyArray = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    public List<String> letterCombinations(String digits) {
        List<String> result = new ArrayList<>();
        if(digits == null || digits.length() == 0) {
            return result;
        }
        appendChar(result, digits, "");
        return result;
    }

    private void appendChar(List<String> list, String digits, String s) {
        // 目标字符串,要从每个键取一个字母,所以目标字符串长度与数字键长度相同时就拼接完成了
        if (s.length() == digits.length()) {
            list.add(s);
            return;
        }
        // s.length()是已拼接的字符串长度,这里是取下一个数字用来继续拼接字符串
        char numChar = digits.charAt(s.length());
        //字符转数字
        int num = Character.getNumericValue(numChar);
        //取出数字映射的字符串
        String numMapString = keyArray[num];
        char[] strCharArray = numMapString.toCharArray();
        //遍历字符串的每个字符,拼接字符
        for(int i = 0; i < strCharArray.length; i++) {
            appendChar(list, digits, s + strCharArray[i]);// 递归
        }
    }
}
9、删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:给定的 n 保证是有效的。
解答:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null) {
    		return head;
    	}
        //双指针,先行指针先行n步,后行指针再开始走,等先行指针走完时,后行指针正好走到倒数第n条数据的位置
    	ListNode leader = head;
    	ListNode follower = head;
    	//先行指针先行n步
    	for (int i = 0; i < n ; i++) {
			leader = leader.next;	
		}
        //遍历n次,leader走到了 n + 1的位置,此时leader == null,说明总共只有n条数据,倒数第n条数据就是head,删除head那么新的head就是head.next
        if(leader == null) {
            return head.next;
        }
        //链表长度大于n
    	//同步前进直到领导到末尾
        // leader 与 follower 的距离正好是 n,所以leader遍历结束时,follower正好位于倒数第n的位置
    	while (leader != null) {
			leader = leader.next;
            if(leader != null) {//如果leader为null了,说明已经遍历完了,follower不再往下走,follower处于倒数n+1的位置,方便删除倒数第n的元素
			    follower = follower.next;
            }
		}
    	//follower.next位于倒数第n的位置,删除follower.next
    	follower.next = follower.next.next;
    	return head;
    }
}
10、有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 :

输入: "()"
输出: true

输入: "()[]{}"
输出: true

输入: "(]"
输出: false

输入: "([)]"
输出: false

输入: "{[]}"
输出: true

解答:

class Solution {
 
    private HashMap<Character, Character> mappings;

    public Solution() {
        this.mappings = new HashMap<Character, Character>();
        this.mappings.put('(', ')');
        this.mappings.put('{', '}');
        this.mappings.put('[', ']');
    }

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (this.mappings.containsKey(c)) {//正括号,塞到栈里
                stack.push(c);
            } else {//反括号
                //扫描到反括号时,栈是空的,说明是反括号开头的字符串,无效
                if(stack.empty()) {
                    return false;
                }
                //弹出栈顶元素(栈里的都是正括号,每次扫描到反括号时,从栈顶弹出正括号匹配抵消)
                char topElement = stack.pop();
                //栈顶元素与该反括号不匹配,则无效
                if (mappings.get(topElement) != c) {
                    return false;
                }
            }
        }
        //如果遍历完之后栈中还有元素,则字符串存在无效括号
        return stack.isEmpty();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值