1.两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解决方案
方法一:暴力法
private static int[] twoSum(int[] nums, int target) {
if(nums == null || nums.length == 1) {
return new int[]{0, 0};
}
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (target == nums[i] + nums[j]) {
return new int[] {i, j};
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(1)
方法二:哈希表
把数组中的元素作为 key 插入到 hashmap 中,值存元素下标
private static int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 1) {
return new int[]{0, 0};
}
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] {map.get(complement), i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)
7.反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0
方法:每次取一个数进行保存,保存之前判断是否有可能会溢出
溢出有四种情况:
a. 上次保存的数 > Integer.MAX_VALUE / 10,则肯定溢出
b. 上次保存的数 == Integer.MAX_VALUE / 10,且当前即将要保存的数大于 7,则会溢出
c. 上次保存的数 < Integer.MIN_VALUE / 10,则肯定溢出
d. 上次保存的数 == Integer.MIN_VALUE / 10,且当前即将要保存的数小于 -8,则会溢出
private static int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
if (rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
复杂度分析
时间复杂度:O(log(n))
空间复杂度:O(1)
9.回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数
进阶:
你能不将整数转为字符串来解决这个问题吗?
方法:反转后半部分,再和前半部分比较,相等则为回文数
怎么确定一半?
将原始数字除以 10,然后给反转后的数字乘上 10,所以,当原始数字小于反转后的数字时,就意味着我们已经处理了一半位数的数字
private static boolean isPalindrome(int x) {
if (x <0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
return x == revertedNumber || x == revertedNumber / 10;
}
复杂度分析
时间复杂度:参考给的是 O(log(n))
空间复杂度:O(1)
13.罗马数字转整数
罗马数字包含以下七种字符: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 的范围内
自己写的
private static int romanToInt(String s) {
// 输入的字符串为 null 或长度为 0
if (s == null || s.length() == 0) {
return 0;
}
String roman = "IVXLCDM";
char[] cs = s.toCharArray();
// 遍历输入的字符串
for (int i = 0; i < cs.length; i++) {
// 当前字符不在罗马数字对应的字符范围内
if (!roman.contains(cs[i] + "")) {
return 0;
}
}
// 若连续输入 4 个罗马字符,抛异常,提示罗马数字一般最多为连续三个字符
if (s.contains("IIII") || s.contains("VVVV") || s.contains("XXXX") || s.contains("LLLL") || s.contains("CCCC")
|| s.contains("DDDD") || s.contains("MMMM")) {
throw new IllegalArgumentException("连续字母不能超过三个");
}
Map<Character, 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);
int len = s.length();
// 初始化 sum 的值,赋值默认取最后一个罗马字符的值
int sum = map.get(s.charAt(len - 1));
for (int i = 0; i < len - 1; i++) {
if (map.get(s.charAt(i)) < map.get(s.charAt(i + 1))) {
sum -= map.get(s.charAt(i));
} else {
sum += map.get(s.charAt(i));
}
}
return sum;
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
14.最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀
如果不存在公共前缀,返回空字符串 “”
示例 1:
输入: [“flower”,”flow”,”flight”]
输出: “fl”
示例 2:
输入: [“dog”,”racecar”,”car”]
输出: “”
说明:
所有输入只包含小写字母 a-z
自己写的
private static String longestCommonPrefix(String[] strs) {
// 输入的数组为 null,或者数组所含元素个数为 0,即 strs = new String[]{}
if (strs == null) {
// 直接返回空字符串
return "";
} else if (strs.length == 1) {// 输入的数组所含元素个数为 1
// 直接返回该字符串
return strs[0];
}
// 获取输入的数组所含元素个数
int len = strs.length;
// 初始化数组元素最短长度为输入的数组中第一个元素的长度
int lessStrLen = strs[0].length();
// 数组第一个元素长度为 0
if (lessStrLen == 0) {
// 直接返回空字符串
return "";
}
// 从数组下标为 1 处遍历数组
for (int i = 1; i < len; i++) {
// 获取当前位置元素的长度
int currStrLen = strs[i].length();
// 元素长度为 0
if (currStrLen == 0) {
// 返回空字符串
return "";
}
// 如果当前元素长度小于记录的最小元素长度
if (currStrLen < lessStrLen) {
// 更新最小元素长度为当前元素长度
lessStrLen = currStrLen;
}
}
// 创建 map 对象
Map<Character, Integer> map = new HashMap<>(lessStrLen);
// 当前遍历的次数
int index = 0;
// 每轮遍历的个数
int count = 0;
// 当前遍历的字符
char c1 = 0;
// 轮数,即最短元素长度
for (int i = 0; i < lessStrLen; i++) {
// 当前遍历的次数
index = i + 1;
// 开始新的一轮,已遍历的个数清零
count = 0;
// 每一轮的次数,即数组长度
for (int j = 0; j < len; j++) {
// 每次获取数组第 j 个元素的第 i 个字符
c1 = strs[j].charAt(i);
// 从第二个元素开始,map 中若不存在 c1,表示数组下标为 j 的元素,第 i 个字符在 map 中不存在
if (j != 0 && map.get(c1) == null) {
// 返回第 i 个字符之前的字符串
return strs[0].substring(0, i);
}
// 当前第 i 轮遍历的个数加一
count += 1;
// 把 c1 放到数组中,值随意,判断的时候关键是判断 c1 这个 key 对应的 value 是否存在
map.put(c1, j + 1);
}
// 当前第 i 轮遍历结束,把 c1 置空,方便下一轮对字符进行存储、判断
map.put(c1, null);
}
// 最后一轮遍历的个数不等于数组元素的个数,即表示最后一轮没有遍历完就出现了不同的字符
if (count != len) {
// 最后一轮之前的轮数对应的字符串都是相同的,相同的轮数为当前遍历的次数 index - 1
index--;
}
// 返回 index 之前的字符串
return strs[0].substring(0, index);
}
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n)
20.有效的括号
给定一个只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
自己写的
private static boolean isValid(String s) {
if (s == null) {
return false;
}
if (s.length() == 0) {
return true;
}
boolean flag = false;
String[] strs = { "()", "{}", "[]" };
while (s.length() != 0) {
flag = false;
for (int i = 0; i < strs.length; i++) {
// 当前字符串包含有效括号
if (s.contains(strs[i])) {
flag = true;
// 剔除当前字符串中的有效括号
s = s.replace(strs[i], "");
// 跳出循环
break;
}
// 此时为遍历有效括号数组 strs 最后一个元素,即有效括号数组在遍历完之前还没有跳出循环
// 则证明当前字符串中含有非有效括号
if (i == strs.length - 1) {
// 返回 falg,此时 flag 为 false
return flag;
}
}
}
// 全部为有效括号,此时 flag 为 true
return flag;
}
复杂度分析
时间复杂度:
a. 最好情况,返回 true,即每次都找到有效括号,O(log2(n))
b. 最差情况,返回 false,数组遍历完也没找到有效括号,O(n)
空间复杂度:O(1)