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]
]
解答:
- 数组排序
- 遍历数组,取出一个值作为锚点
- 遍历锚点之外的元素,找出两数之和减去锚点值 == 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();
}
}