目录
初级算法
字符串
LC反转字符串
编写一个函数,其作用是将输入的字符串反转过来,输入字符串以字符串数组s的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组,使用o(1)的额外空间解决这一问题。
示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
示例 2:
输入:s = [“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]
提示:
1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符
题解:
第一种使用双指针
使用俩个指针,一个从第一个开始,一个从数组的最后一个开始。
代码如下:
class Solution {
public void reverseString(char[] s) {
int length = s.length;
//代表从后的指针
int j = length - 1;
//代表最前的指针
int i = 0;
//俩俩交换只需要循环一半即可
while(i < length / 2){
char temp = s[i];
s[i++] = s[j];
s[j--] = temp;
}
}
}
第二种递归方式解决
上面的代码我们可以改为递归方式实现:
class Solution {
public void reverseString(char[] s) {
if(s.length == 0)return;
reverseStringHelper(s,0,s.length - 1);
}
public void reverseStringHelper(char[] s, int left ,int right){
if(left >= right) return;
reverseStringHelper(s,left + 1,right - 1);
swap(s,left,right);
}
private void swap(char[] array,int i, int j){
char temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
LC整数反转
给你一个32位的有符合整数x,返回将x中的数字部分反转后的结果。
如果反转后整数超过32位的有符合整数的范围【-231,231 - 1】,就返回0。
假设环境不允许存储64位整数(有符合或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
-231 <= x <= 231 - 1
题解:
翻转每一位数字即可,原理比较简单,我们直接来看图分析:
class Solution {
public int reverse(int x) {
int res = 0;
while(x != 0){
int m = x % 10;
int newRes = res * 10 + m;
//防止溢出,如果溢出直接返回0
if((newRes - m) / 10 != res)return 0;
res = newRes;
x /= 10;
}
return res;
}
}
LC字符串中的第一个唯一字符
给定一个字符串s,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回-1.
示例 1:
输入: s = “leetcode”
输出: 0
示例 2:
输入: s = “loveleetcode”
输出: 2
示例 3:
输入: s = “aabb”
输出: -1
提示:
1 <= s.length <= 105
s 只包含小写字母
题解:
第一种俩次遍历
第一遍先统计每个字符出现的次数,第二遍再次从前往后遍历字符串s中的每个字符,如果某个字符出现一次直接返回,原来比较简单,代码如下:
class Solution {
public int firstUniqChar(String s) {
char [] w = s.toCharArray();
int count[] = new int[26];
for(int i = 0;i < w.length;i++){
count[w[i] - 'a']++;
}
for(int i = 0;i < w.length;i++){
if(count[w[i] - 'a'] == 1)
return i;
}
return -1;
}
}
第二种使用HashMao解决
和第一种一样,就是换个存储类型
代码如下:
class Solution {
public int firstUniqChar(String s) {
Map<Character,Integer> map = new HashMap<>();
char[] m =s.toCharArray();
for(char a: m){
map.put(a,map.getOrDefault(a,0) + 1);
}
for(int i = 0;i < m.length;i++){
if(map.get(m[i]) == 1)
return i;
}
return -1;
}
}
第三种使用javaAPi
当这个字符第一次出现的位置和它最后一次出现的位置一样那么这个字符就出现过一次
代码如下:
class Solution {
public int firstUniqChar(String s) {
for(int i = 0;i < s.length();i++){
if(s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i)))
return i;
}
return -1;
}
}
LC有效的字母异位词
给定俩个字符串s和t,编写一个函数来判断t是否是s的字母异位词。
注意:若s和t中每个字符出现的次数都相同,则称s和t互为字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s 和 t 仅包含小写字母
题解:
第一种计算俩个字符串中字符的差值
先统计字符串s中每个字符的数量
然后减去t中每个字符对应的数量
如果最后结果都是0,说明t是s的字母异位词。
代码如下:
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
int[] count = new int[26];
char[] sw = s.toCharArray();
char[] tc = t.toCharArray();
for(char w:sw){
count[w - 'a']++;
}
for(char c:tc){
count[c - 'a']--;
}
for(int z:count){
if(z != 0)return false;
}
return true;
}
}
第二种先排序,在比较
先把俩个字符串转化为字符数组,然后再对这俩个字符数组进行排序,因为相同的字符在排序之后肯定是挨着的,最后再比较这俩个排序后的数组的元素是否相同。
代码如下:
class Solution {
public boolean isAnagram(String s, String t) {
char[] sw = s.toCharArray();
char[] tc = t.toCharArray();
//对俩个字符串中的字符进行排序
Arrays.sort(sw);
Arrays.sort(tc);
return Arrays.equals(sw,tc);
}
}
第三种一次遍历
还可以使用另外一种方式,这种实现方式不太容易想到。
使用一个变量count记录新出现字符的个数,然后同时遍历俩个数组,如果出现了一个新的字符,count就加1,如果消失了一个字符,count就减一,最后判断count是否等于0即可。
代码如下:
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
char[] sw = s.toCharArray();
char[] tc = t.toCharArray();
int [] map = new int [26];
int count = 0;
for(int i = 0;i < sw.length;i++){
//出现了一个新的字符
if(++map[sw[i] - 'a'] == 1)
count++;
//消失了一个的字符
if(--map[tc[i] - 'a'] == 0)
count--;
}
return count == 0;
}
}
LC验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
解释:“amanaplanacanalpanama” 是回文串
示例 2:
输入: “race a car”
输出: false
解释:“raceacar” 不是回文串
提示:
1 <= s.length <= 2 * 105
字符串 s 由 ASCII 字符组成
题解:
双指针解决
这题没有什么难度,最简单的就是使用双指针,一个指向前,一个指向后,遇到空格以及特殊字符要跳过,然后判断,画个图来看一下:
代码如下:
class Solution {
public boolean isPalindrome(String s) {
int length = s.length();
int left = 0;
int right = length - 1;
while(left < right){
//left先过滤空格和其他字符
//这里一定要使用while因为他下一个有可能也是特殊字符
while(left < right && !Character.isLetterOrDigit(s.charAt(left)))
left++;
//过滤right空格和其他字符
while(right > left && !Character.isLetterOrDigit(s.charAt(right)))
right--;
//都转化为小写来做比较
if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right)))
return false;
left++;
right--;
}
return true;
}
}
双指针的另一种写法
public boolean isPalindrome(String s) {
if (s == null || s.length() == 0)
return true;
s = s.toLowerCase();
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
while (i < j && !Character.isLetterOrDigit(s.charAt(i)))
i++;
while (i < j && !Character.isLetterOrDigit(s.charAt(j)))
j--;
if (s.charAt(i) != s.charAt(j))
return false;
}
return true;
}
第二种使用正则匹配
这题还可以使用正则匹配,把特殊字符过滤掉,只留下字母和数字,然后转化为小写,再反转,最后再判断是否相等即可。
代码如下:
class Solution {
public boolean isPalindrome(String s) {
// replaceAll(String regex,String replacement)
//使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。
//toLowerCase()是返回小写字符串
String actual = s.replaceAll("[^A-Za-z0-9]","").toLowerCase();
//reverse()反转字符串
String rev = new StringBuffer(actual).reverse().toString();
return actual.equals(rev);
}
}
第三种递归方式实现
如果想玩出花样来,我们还可以把第一种的解题思路改为递归的方式
代码如下:
class Solution {
public boolean isPalindrome(String s) {
return isPalindromeString(s,0,s.length() - 1);
}
public boolean isPalindromeString(String s,int left,int right){
if(left > right)
return true;
while(left < right && !Character.isLetterOrDigit(s.charAt(left)))
left++;
while(right > left && !Character.isLetterOrDigit(s.charAt(right)))
right--;
return Character.toLowerCase(s.charAt(left)) == Character.toLowerCase(s.charAt(right)) && isPalindromeString(s,++left,--right);
}
}
LC字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:
本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符
。
示例 1:
输入:s = “42”
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:“42”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“42”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
^
第 3 步:“42”(读入 “42”)
^
解析得到整数 42 。
由于 “42” 在范围 [-231, 231 - 1] 内,最终结果为 42 。
示例 2:
输入:s = " -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 ‘-’ 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 “42”)
^
解析得到整数 -42 。
由于 “-42” 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
示例 3:
输入:s = “4193 with words”
输出:4193
解释:
第 1 步:“4193 with words”(当前没有读入字符,因为没有前导空格)
^
第 2 步:“4193 with words”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
^
第 3 步:“4193 with words”(读入 “4193”;由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193 。
由于 “4193” 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
提示:
0 <= s.length <= 200
s 由英文字母(大写和小写)、数字(0-9)、’ ‘、’+‘、’-’ 和 ‘.’ 组成
题解:
按照题中的要求做
总共分为3步:
- 先去掉字符串俩边的空格
- 然后判断符号
- 最后读取数字
代码如下:
class Solution {
public int myAtoi(String s) {
//根据题目需求先去空格,trim去除前后俩端的空格
s = s.trim();
//如果去除完空格后是个空串则会报下标越界
if(s.length() == 0 || s == "") return 0;
//表示指向第几个数
int index = 0;
//确定数是负数还是整数
int sign = 1;
//最后返回的结果
int res = 0;
//判断是正数还是负数
if(s.charAt(index) == '-' || s.charAt(index) == '+')
sign = s.charAt(index++) == '-'?-1:1;
//读取数字
for(;index < s.length();index++){
int w = s.charAt(index) - '0';
//判断是否是数字如果不是就退出循环
if(w < 0 || w > 9)
break;
//判断是否越界
if(res > Integer.MAX_VALUE / 10 ||(res == Integer.MAX_VALUE / 10 && w > Integer.MAX_VALUE % 10)) //如果越界通过sign来判断是最小值还是最大值
return sign == 1?Integer.MAX_VALUE:Integer.MIN_VALUE;
res = res * 10 + w;
}
return sign * res;
}
}
LC实现strStr()
实现strStr()函数
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
提示:
1 <= haystack.length, needle.length <= 104
haystack 和 needle 仅由小写英文字符组成
题解:
循环遍历
代码如下:
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0 || needle == "") return 0;
//最后的结果
int s = 0;
//记录第一个字符串的下标
int i = 0;
//记录第二个字符串的下标
int j = 0;
//记录从第一个字符串的第几个开始遍历的
int b = 0;
char[] z = haystack.toCharArray();
char[] c = needle.toCharArray();
while(i < haystack.length()){
j = 0;
b = i;
s = i;
//如果成立就循环
while (j < needle.length() && b < haystack.length() && z[b] == c[j] ){
b++;
j++;
}
//循环结束判断如果从b开始遍历完以后减去i的值肯定就是子串的长度就返回s
if(b - i == needle.length()) return s;
i++;
}
return -1;
}
}
一行代码搞定
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
如果要这样写就没意思了,算法题能直接写的还是要自己写的,尽量少用官方的api
逐个判断
一般字符串匹配的时候,最简单的一种方式,就是子串总头开始和主串匹配。
如果匹配失败,子串再次从头开始,而主串从上次匹配的下一个字符开始。
和第一种差不多,但是比第一种要写的好
代码如下:
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0)
return 0;
int i = 0;
int j = 0;
while(i < haystack.length() && j < needle.length()){
if(haystack.charAt(i) == needle.charAt(j)){
i++;
j++;
}else{
i = i - j + 1;
j = 0;
}
if(j == needle.length()) return i - j;
}
return -1;
}
}
LC外观数列
给定一个正整数n,输出外观数列的第n项。
[外观数列]是一个整数序列,从数字1开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:
-
1
-
11
-
21
-
1211
-
111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如,数字字符串 “3322251” 的描述如下图:
示例 1:
输入:n = 1
输出:“1”
解释:这是一个基本样例。
示例 2:
输入:n = 4
输出:“1211”
解释:
countAndSay(1) = “1”
countAndSay(2) = 读 “1” = 一 个 1 = “11”
countAndSay(3) = 读 “11” = 二 个 1 = “21”
countAndSay(4) = 读 “21” = 一 个 2 + 一 个 1 = “12” + “11” = “1211”
提示:
1 <= n <= 30
题解:
双循环
按照题意计算就好了,使用俩个for循环,第二个循环不断的统计字符串中字符的个数,然后存储到一个StringBuilder中,然后这个StringBuilder会作为下一个统计开始的字符串,一直重复上面得操作即可:
代码如下:
class Solution {
public String countAndSay(int n) {
//最后返回的字符串
StringBuilder res = new StringBuilder("1");
//循环体中的字符串
StringBuilder prev;
//记录重复次数
int count;
//当前比较的字符
char say;
for(int i = 1;i < n;i++){
//上一次的字符串
prev = res;
//重置字符串
res = new StringBuilder();
//重置重复次数
count = 1;
//从上个字符串第一个字符开始比较
say = prev.charAt(0);
//从第二个字符开始循环,循环上一个字符串的长度
for(int j = 1,len = prev.length();j < len;j++){
if(prev.charAt(j) != say){
//如果不一样就把他重复的次数加上这个字符
res.append(count).append(say);
//重置次数
count = 1;
//设置上一个字符串的下一个字符
say = prev.charAt(j);
}else{
//如果相等就增加次数
count++;
}
}
//添加最后一个字符
res.append(count).append(say);
}
//返回即可
return res.toString();
}
}
代码改变一下:
class Solution {
public String countAndSay(int n) {
//返回参数
String res = "1";
//从第二次开始循环
for(int i = 2;i <= n;i++){
//拼接字符串
StringBuilder w = new StringBuilder();
//上一个字符串的下标
int index = 0;
while(index < res.length()){
//累加
int count = 1;
//当下标小于长度-1才能继续循环,因为要比较index和index+1
while(index < res.length() - 1 && res.charAt(index) == res.charAt(index + 1)){
index++;
count++;
}
//拼接
w.append(count).append(res.charAt(index));
index++;
}
res = w.toString();
}
return res;
}
}
LC最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串“”。
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:“”
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
题解:
双循环
使用第一个字符串当公共串头,先取第一个字符串当做他们的公共前缀
然后找出他和第2个字符串的公共前缀,然后再用这个找出的公共前缀分别和第3个,第4个…判断
class Solution {
public String longestCommonPrefix(String[] strs) {
//边界条件判断
if(strs == null || strs.length == 0)
return "";
//默认第一个字符串是他们的公共前缀
String pre = strs[0];
int i = 1;
while(i < strs.length){
//不断的截取
while(strs[i].indexOf(pre) != 0){
//如果截取失败就把子串-1继续循环
pre = pre.substring(0,pre.length() - 1);
}
i++;
}
return pre;
}
}