时间过得真快,距离18年的1024已经一年了,一年前,刚开始在csdn上写博客,那时候对1024没有太上心,觉得自己不是一个程序员。经过了1年的学习,心态有了很大的变化,把写代码变得有仪式感,1024,不只是成长,更是挑战。
LeetCode的初级题或者说是简单题,跟智力和脑子没关系。锻炼的还是编程能力。就是说,这些题基本上看到就能有想法,它们锻炼的是把脑子中的想法快速转换为代码的能力。算是入门级别。
这个就是个憨批题:
public void reverseString(char[] s) {
char temp;
int length = s.length;
for (int i = 0; i < length / 2; i ++){
temp = s[i];
s[i] = s[length - 1 - i];
s[length - 1 - i] = temp;
}
}
由于这是字符串这一章节的问题,所以我很自然的就把它当成了字符串来做,开局换上字符数组直接开始怼,具体思路是:
即从前往后拿数据
1 2 3 4 5
21
3 4 5
public int reverse(int x) {
// 全转为正数
char[] numChar = Integer.valueOf(x > 0 ? x : -x).toString().toCharArray();
int reValue = 0;
int multi = 1;
int temp;
int billion = 1000000000;
for (char c : numChar) {
temp = (c - 48) * multi;
if (multi == billion) {
// 如果最后一位数比2大或者(等于2,但是后面的数比Max_Value大)
if (c > 50 || (reValue > Integer.MAX_VALUE % billion && c == 50)) {
return 0;
}
}
reValue += temp;
multi *= 10;
}
return x > 0 ? reValue : -reValue;
}
另外一种思路是从后往前拿数据,巧妙地运用了%
和 /
的作用。算是用了栈把
1 2 3 4 5
54
1 2 3
public int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > Integer.MAX_VALUE/10) return 0;
if (rev < Integer.MIN_VALUE/10) return 0;
rev = rev * 10 + pop;
}
return rev;
}
这个题不能像只出现一个的数字一样,即不能使用异或来解决,因为这个题中可能会出现多个唯一的数组,然后我们需要拿到第一个唯一的数组。
首先想到的就是使map
,value值随着key的重复而++
public int firstUniqChar(String str) {
Map<Character, Integer> map = new HashMap<>(15);
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
for (int i = 0; i < str.length(); i++) {
if (map.get(str.charAt(i)) == 1) {
return i;
}
}
return -1;
}
这个题和之前的两个数组的交集比较像
先排序,然后比较,非常方便,但是感觉有点慢
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
char[] cs = s.toCharArray();
char[] ct = t.toCharArray();
Arrays.sort(cs);
Arrays.sort(ct);
for(int i = 0; i < s.length(); i ++){
if(cs[i] != ct[i]) {
return false;
}
}
return true;
}
同时,还可以利用哈希表来观察这两个字符串中字符的出现次数
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
int[] counter = new int[26];
for(int i = 0; i < s.length(); i ++) {
// -97是因为前面的数组长度只有26
counter[s.charAt(i) - 97] ++;
}
for(int i = 0; i < t.length(); i ++) {
if(-- counter[t.charAt(i) - 97] < 0){
return false;
}
}
return true;
}
主要是字符串数组比较特别,确定字符串数组中的字符需要两重循环;str[i].charAt(j)
方法一是暴力
两重循环,第一个是数组的循环,第二个是字符串中字符的循环。用力扣管方的说法就是水平扫描
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 0) return "";
if (strs.length == 1) return strs[0];
int flag = 0;
for (int i = 1; i < strs.length; ) {
// 防止charAt(i) 越界
if (flag > strs[i].length() - 1 || flag > strs[i - 1].length() - 1) break;
if (strs[i].charAt(flag) == strs[i - 1].charAt(flag)) {
// 如果数组循环一遍,则回到第一个数组元素。同时把索引加一
if (i == strs.length - 1) {
flag ++;
i = 1;
continue;
}
} else break;
i ++;
}
return strs[0].substring(0, flag);
}
方法二:分治递归
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
return longestCommonPrefix(strs, 0 , strs.length - 1);
}
private String longestCommonPrefix(String[] strs, int l, int r) {
if (l == r) {
return strs[l];
}
else {
int mid = (l + r)/2;
String lcpLeft = longestCommonPrefix(strs, l , mid);
String lcpRight = longestCommonPrefix(strs, mid + 1,r);
return commonPrefix(lcpLeft, lcpRight);
}
}
private String commonPrefix(String left,String right) {
int min = Math.min(left.length(), right.length());
for (int i = 0; i < min; i++) {
if ( left.charAt(i) != right.charAt(i) )
return left.substring(0, i);
}
return left.substring(0, min);
}
方法三:二分法
主要是使用一些字符串相关API,同时还有字符和int类型的比较
这个题当理解了回文串的性质之后,无非就是比较前面和后面的值。没有什么难度,主要是Character
API 的熟悉
public static boolean isPalindrome(String s) {
boolean isNumOrLetterI;
boolean isNumOrLetterJ;
for (int i = 0, j = s.length() - 1; i <= j; i ++, j --){
isNumOrLetterI = Character.isLetterOrDigit(s.charAt(i));
isNumOrLetterJ = Character.isLetterOrDigit(s.charAt(j));
if (isNumOrLetterI && isNumOrLetterJ) {
if (Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(s.charAt(j))) {
continue;
}
return false;
} else {
if (!isNumOrLetterI && !isNumOrLetterJ) {
continue;
}
if (!isNumOrLetterI) {
j ++;
}
if (!isNumOrLetterJ) {
i --;
}
}
}
return true;
}
这个题字字符的处理上和第五题比较像,在int类型越界上和第二题比较像
第一步时处理字符,直至找到数字。有一下情况:
continue
-
or +
则需要记录它的正负return false
第二步是计算,并防止越界
if (!mark && (-num < Integer.MIN_VALUE/10 || (-num == Integer.MIN_VALUE/10 && str.charAt(index) > '8'))) return Integer.MIN_VALUE;
if (mark && (num > Integer.MAX_VALUE/10 || (num == Integer.MAX_VALUE/10 && str.charAt(index) > '7'))) return Integer.MAX_VALUE;
public static int myAtoi(String str) {
boolean mark = true;
int num = 0;
// 字符串索引
int index = 0;
char temp;
// 第一个循环,找到数字
while(index < str.length()) {
temp = str.charAt(index);
// 前面没有数字且是负号
if (temp == '-' || temp == '+') {
// -为最后一个字符或-后面不为数字
if (index == str.length() - 1 || !isNum(str.charAt(index + 1))) return 0;
else mark = temp != '-';
} else if (temp == ' ') {
index ++;
continue;
} else if (!isNum(temp)) return 0;
else break;
index ++;
}
// 找到数字以后
for(; index < str.length() && isNum(str.charAt(index)); index ++) {
if (!mark && (-num < Integer.MIN_VALUE/10 || (-num == Integer.MIN_VALUE/10 && str.charAt(index) > '8'))) return Integer.MIN_VALUE;
if (mark && (num > Integer.MAX_VALUE/10 || (num == Integer.MAX_VALUE/10 && str.charAt(index) > '7'))) return Integer.MAX_VALUE;
num = (str.charAt(index) - '0') + num * 10;
}
return mark ? num : -num;
}
private static boolean isNum(char c) {
return c >= '0' && c <= '9';
}
这个类似于Java的indexOf
的函数
思路一,暴力双指针
这个题也是用了双指针,数组模块经常用到双指针,如第一题和第六题都是这样。
其实全一点的话是4个指针:
public static int strStr(String haystack, String needle) {
if ("".equals(needle)) return 0;
int beginI = 0;
// j != 0 说明已经开始比较了
for (int i = 0, j = 0; i < haystack.length() && j < needle.length(); i ++) {
if (haystack.charAt(i) == needle.charAt(j)) {
beginI = j == 0 ? i : beginI;
if (j ++ == needle.length() - 1) {
return beginI;
}
} else if (j != 0) {
j = 0;
i = beginI;
}
}
return -1;
}
思路二,KMP算法:
KMP算法在面试中常问到,基本是字符串比较中最优的算法了。这个算法是用空间换时间,时间复杂度是o(1)
public int strStr(String haystack, String needle) {
int strLen = haystack.length(), subLen = needle.length();
if (subLen == 0) return 0;
if (strLen == 0) return -1;
// 构建状态机
int[][] FSM = new int[subLen][256];
int X = 0, match = 0;
for (int i = 0; i < subLen; i++) {
match = (int) needle.charAt(i);
for (int j = 0; j < 256; j++) {
// 当前状态 + 匹配失败字符 = 孪生词缀状态 + 匹配字符
FSM[i][j] = FSM[X][j];
}
FSM[i][match] = i + 1;
if (i > 0) {
// 下一孪生前缀状态 = X + match
X = FSM[X][match];
}
}
// 匹配子串
int state = 0;
for (int i = 0; i < strLen; i++) {
state = FSM[state][haystack.charAt(i)];
if (state == subLen) {
return i - subLen + 1;
}
}
return -1;
}
思路三,Sunday算法:
这个还没看,等到面试之前再刷一哈
这题意真是SB,是我脑子不够用了吗?
看了评论,明白这题说的是什么意思了,这题也不算难,非常简单,基本看一下就能出来结果。只要把脑子里的想法写出来就行。
counter
str.charAt(i) == str.charAt(i + 1)
,如果为真,counter++
;如果为假,则把i
的值和counter
增加到stringBuffer
中,之后把counter
变为1,。public String countAndSay(int n) {
String str = "1";
for (int i = 1; i < n; i ++) {
str = nextStr(str);
}
return str;
}
private String nextStr(String str) {
StringBuilder sb = new StringBuilder("");
int counter = 1;
for (int i = 0; i < str.length(); i ++) {
if (i < str.length() - 1 && str.charAt(i) == str.charAt(i + 1)) {
counter ++;
} else {
// 如果是最后一个且前面没有重复
if (i == str.length() - 1 && str.length() > 1 && str.charAt(i) != str.charAt(i - 1)) {
counter = 1;
}
sb.append(counter).append(str.charAt(i));
counter = 1;
}
}
return sb.toString();
}