Java解leetcode,助力面试之简单10道题(六)
第338题 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入 | 输出 |
---|---|
2 | [0,1,1] |
示例 2:
输入 | 输出 |
---|---|
5 | [0,1,1,2,1,2] |
解题思路
使用动态规划求解,每一个数字的二进制数包含1的个数等于它与它的前一个数字的二进制数进行&操作之和加1
代码
// 比特位计数:动态规划
class Solution {
public int[] countBits(int num) {
int[] bits = new int[num + 1];
for (int i = 1; i <= num; i++) {
bits[i] = bits[i & (i - 1)] + 1;//动态规划,每一个数字都与前一个数字的比特位相关
}
return bits;
}
}
时间复杂度为O(n),n为给定数组大小
空间复杂度为O(1)
第342题 4的幂
给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 4 的幂次方需满足:存在整数 x 使得 n == x个4相乘
示例 1:
输入 | 输出 |
---|---|
n = 16 | true |
示例 2:
输入 | 输出 |
---|---|
n = 5 | false |
示例 3:
输入 | 输出 |
---|---|
n = 1 | true |
解题思路
如果这个数是4的倍数则它和比它小1的数执行&操作会为0,且对3取余为1
代码
// 4的幂:位运算
class Solution {
public boolean isPowerOfFour(int num) {
return (num > 0) && ((num & (num - 1)) == 0) && (num % 3 == 1);
}
}
时间复杂度为O(1)
空间复杂度为O(1)
第344题 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入 | 输出 |
---|---|
[“h”,“e”,“l”,“l”,“o”] | [“o”,“l”,“l”,“e”,“h”] |
示例 2:
输入 | 输出 |
---|---|
[“H”,“a”,“n”,“n”,“a”,“h”] | [“h”,“a”,“n”,“n”,“a”,“H”] |
解题思路
使用双指针,left指针指向字符串首部,right指针指向字符串尾部,然后进行交换值,直到left指针等于或小于right指针,循环结束
代码
// 反转字符串:双指针
class Solution {
public void reverseString(char[] s) {
int n = s.length;
for (int left = 0, right = n - 1; left < right; ++left, --right) {//left指针右移,right指针左移
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}//交换值
}
}
时间复杂度为O(n),n为字符串中的字符长度
空间复杂度为O(1)
第345题 反转字符串中的元音字母
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1:
输入 | 输出 |
---|---|
“hello” | “holle” |
示例 2:
输入 | 输出 |
---|---|
“leetcode” | “leotcede” |
解题思路
本题与344题类似,只是增加了一个反转元音字母,所以多了一个判断是否为元音字母的函数,其他基本一样。
代码
// 反转字符串中的元音字母:双指针
class Solution {
public String reverseVowels(String s) {
int n = s.length();
int left = 0, right = n - 1;
char[] sChar = s.toCharArray();
while (left < right) {
char a = sChar[left];
char b = sChar[right];
if (!isVowel(a)) {
left++;
continue;
} else if (!isVowel(b)) {
right--;
continue;
} else {
char c = sChar[left];
sChar[left] = sChar[right];
sChar[right] = c;
}//交换值
left++;
right--;
}
return new String(sChar);
}
//判断是否为元音字母
private boolean isVowel(char c) {
return c == 'a' || c == 'o' || c == 'e' || c == 'i' || c == 'u' || c == 'A' ||
c == 'O' || c == 'E' || c == 'I' || c == 'U';
}
}
时间复杂度为O(n),n为字符串的长度
空间复杂度为O(1)
第367题 有效的完全平方数
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要 使用任何内置的库函数,如 sqrt 。
示例 1:
输入 | 输出 |
---|---|
num = 16 | true |
示例 2:
输入 | 输出 |
---|---|
num = 14 | false |
解题思路
本题使用了牛顿迭代法,循环判断一个数是否小于目标数,循环条件起初设为目标数的一半,然后是该书加上目标数除以该数,将两者的和除2,来更新该数,一旦目标数不大于该数的平方时,就结束循环,然后判断两者是否相等。牛顿迭代法求解就是:f(x)=x的平方-num=0的根
代码
// 有效的完全平方数:数学-牛顿迭代法
class Solution {
public boolean isPerfectSquare(int num) {
if (num < 2) return true;
long x = num / 2;
while (x * x > num) {
x = (x + num / x) / 2;
}
return (x * x == num);
}
}
时间复杂度为O(log n),n为数的大小
空间复杂度为O(1)
第389题 找不同
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例 1:
输入 | 输出 |
---|---|
s = “abcd”, t = “abcde” | “e” |
解释:‘e’ 是那个被添加的字母。
示例 2:
输入 | 输出 |
---|---|
s = “”, t = “y” | “y” |
示例 3:
输入 | 输出 |
---|---|
s = “a”, t = “aa” | “a” |
解题思路
本题可以使用位运算求解,有两种解法,其中一种是将字符串t中的字符转换为ASCII码的进行求和,然后同样将字符串s中的字符转换为ASCII码求和,最后用两者的差值得到不同的字符。第二种方法为下列代码,进行异或操作,不同的字符会被剩下来
代码
// 找不同:位运算
class Solution {
public char findTheDifference(String s, String t) {
int ret = 0;
for(int i=0;i<s.length();i++){
ret ^= s.charAt(i);//用0异或字符串s中的所有字符
}
for(int i=0;i<t.length();i++){
ret ^= t.charAt(i);//再用异或后得到的数对字符串t中的字符进行异或,最后剩下一个0异或两字符串中的不同字符,得到这个不同的字符的整数形式。
}
return (char)ret; //将这个数字转换为字符形式
}
}
时间复杂度为O(n),n为字符串长度
空间复杂度为O(1)
第392题 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入 | 输出 |
---|---|
s = “abc”, t = “ahbgdc” | true |
示例 2:
输入 | 输出 |
---|---|
s = “axc”, t = “ahbgdc” | false |
解题思路
本题使用双指针,分别指向字符串s和t,然后遍历,如果字符串s的字符与字符串t的字符匹配,则将指针都向右移,否则只移动指向t的指针,最后遍历完成后,如果字符串s遍历完成,则代表s是t的字串
代码
// 判断子序列:双指针
class Solution {
public boolean isSubsequence(String s, String t) {
int n = s.length(), m = t.length();
int i = 0, j = 0;
while (i < n && j < m) {
if (s.charAt(i) == t.charAt(j)) {//判断值是否相等
i++;//相等则指向字符串s的指针右移
}
j++;//不管值是否相等,都将字符串t的指针右移
}
return i == n;//如果s是t的子串,按照遍历的顺序一定能在字符串t中找到有这个顺序的字符,因此字符串s也一定遍历完成,所有i的长度为字符串s的长度
}
}
时间复杂度为O(n+m),n,m为两个字符串的长度
空间复杂度为O(1)
第404题 左叶子之和
计算给定二叉树的所有左叶子之和。
示例 1:
3
/ \
9 20
/ \
15 7
输入 | 输出 |
---|---|
[3,9,20,null,null,15,7] | 24 |
解释:在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
解题思路
本题使用dfs找寻是左叶子节点的值,然后将值累加
代码
// 左叶子之和:dfs
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
return root != null ? dfs(root) : 0;
}//判断根节点是否为空
public int dfs(TreeNode node) {//dfs深度搜索算法
int ans = 0;
if (node.left != null) {//如果左子节点不为空
ans += isLeafNode(node.left) ? node.left.val : dfs(node.left);//如果节点的左子节点为空,则加上该节点的值,否则对节点的左子节点进行dfs操作
}
if (node.right != null && !isLeafNode(node.right)) {//判断右子节点是否为叶子节点或为空节点
ans += dfs(node.right);//如果右子节点不为空节点且不是叶子节点,则加上对其进行dfs操作得到的值
}
return ans;
}
public boolean isLeafNode(TreeNode node) {//判断是不是左叶子节点
return node.left == null && node.right == null;//如果左子节点和右子节点都为空,则表明该节点为叶子节点
}
}
时间复杂度为O(n),n为树的节点树
空间复杂度为O(n)
第405题 数字转换为十六进制数
给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。
注意:
十六进制中所有字母(a-f)都必须是小写。
十六进制字符串中不能包含多余的前导零。如果要转化的数为0,那么以单个字符’0’来表示;对于其他情况,十六进制字符串中的第一个字符将不会是0字符。
给定的数确保在32位有符号整数范围内。
不能使用任何由库提供的将数字直接转换或格式化为十六进制的方法。
示例 1:
输入 | 输出 |
---|---|
26 | “1a” |
示例 2:
输入 | 输出 |
---|---|
-1 | “ffffffff” |
解题思路
本题使用基本的数学方法,在二进制中
代码
// 数字转换为十六进制数:数学
class Solution {
public String toHex(int num) {
if (num == 0) { return "0"; } // 判断是否为0
char[] hex = "0123456789abcdef".toCharArray(); // 将对应16位进制存入字符串中
StringBuilder ans = new StringBuilder();
while (num != 0) {//循环条件
int temp = num & 0xf; // 让数字与1111执行&操作,即取得数字的二进制数的后四位
ans.append(hex[temp]); // 将得到的最低4位的十进制转换位字符串中相对应的字符
num >>>= 4; // 逻辑右移4位,如果右移后的数字大于0,则求得的十六进制位不止1位
}
return ans.reverse().toString();//反转字符串,因为append函数是从原数组末尾加入数字,因此各个位数是倒过来的,所有需要反转字符串
}
}
时间复杂度为O(1)
空间复杂度为O(s),s位16进制存放的字符串长,也就是16
第409题 最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入 | 输出 |
---|---|
“abccccdd” | 7 |
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
解题思路
本题使用贪心算法来解决,对字符串遍历,然后将各个字符出现的字数存入一个数组中,然后再对这个数组进行遍历,如果有字符出现次数大于2,则加入小于等于它的偶数次数,然后考虑一种情况,就是回文字符串的中间字符可以单独出现,因此加了一个判断,如果遇到第一次数的出现次数为偶数,则将结果加1,把它作为中间字符,如果再遇到出现奇数次数的字符,则不再加1.
代码
// 最长回文串:贪心算法
class Solution {
public int longestPalindrome(String s) {
int[] count = new int[128];//定义一个字符集,长度为所有字符的大小即128
int length = s.length();
for (int i = 0; i < length; ++i) {
char c = s.charAt(i);
count[c]++;//统计各字符出现的次数
}
int ans = 0;
for (int v: count) {//遍历统计数组
ans += v / 2 * 2;//如果一个字符出现的次数大于2,则取小于等于它的偶数次数
if (v % 2 == 1 && ans % 2 == 0) {//如果遍历到的一个数为奇数,则将结果加1,后续再遍历到奇数,则不再加,因为这个奇数是作为回文数的中心存放的
ans++;
}
}
return ans;
}
}
时间复杂度为O(n),字符串s的长度
空间复杂度为O(s),字符集的大小,在java中使用了一个长为128的数组,存储每个字符出现的次数