数组&字符串
9 回文数
public boolean isPalindrome(int x) {
if(x < 0) return false;
String str = Integer.toString(x);
char[] charArr = str.toCharArray();
for(int i = 0; i < charArr.length/2; i++){
if(charArr[i] != charArr[charArr.length-1-i]){
return false;
}
}
return true;
}
方法2:
public boolean isPalindrome(int x) {
if(x<0) return false;
if(x % 10 == 0 && x != 0) return false; //末位是0,不可能是回文数
int halfReserve = 0;
while((x - halfReserve) > 0){
halfReserve = halfReserve*10 + x%10;
x = x/10;
}
System.out.println(halfReserve+" "+ x);
if(x == halfReserve) return true;
if(x == halfReserve/10) return true;//121
return false;
}
反转字符串
编写一个函数,其作用是将输入的字符串反转过来。
注意不要想得太复杂了就好。
class Solution {
public String reverseString(String s) {
int len = s.length();
if(len<2)return s;
char[] chars = s.toCharArray();
int left = 0;
int right = len-1;
while(left<right){
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right–;
}
return new String(chars);
}
}
mid 481 神奇字符串【双指针 巧思】
神奇字符串 s 仅由 ‘1’ 和 ‘2’ 组成,并需要遵守下面的规则:
神奇字符串 s 的神奇之处在于,串联字符串中 ‘1’ 和 ‘2’ 的连续出现次数可以生成该字符串。
s 的前几个元素是 s = “1221121221221121122……” 。如果将 s 中连续的若干 1 和 2 进行分组,可以得到 “1 22 11 2 1 22 1 22 11 2 11 22 …” 。每组中 1 或者 2 的出现次数分别是 “1 2 2 1 1 2 1 2 2 1 2 2 …” 。上面的出现次数正是 s 自身。
给你一个整数 n ,返回在神奇字符串 s 的前 n 个数字中 1 的数目。
示例
输入:n = 6
输出:3
解释:神奇字符串 s 的前 6 个元素是 “122112”,它包含三个 1,因此返回 3 。
int magicalString(int n) {
string str = "12211";
int i = 3;
while (str.size() < n) {
char c = str.at(i); //下一个数字出现的次数
if (str[str.size()-1] == '1') {
str.append(c-48, '2');
} else if (str[str.size()-1] == '2') {
str.append(c-48, '1');
}
i++;
}
if (n < str.size()) {
str = str.substr(0,n);
}
int res = 0; //要初始为0,否则局部int类型的数组初始值是随机的垃圾数值
for (int i = 0; i < str.size(); i++) {
if (str[i] == '1') res++;
}
return res;
}
mid 56 合并区间
合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间
static bool cmp(const vector<int> arr1, const vector<int> arr2) {
if (arr1[0] == arr2[0]) return arr1[1] < arr2[1];
return arr1[0] < arr2[0];
}
vector<int> submerge(vector<int>& subres, vector<vector<int>>& intervals, int& index) {
int start = subres[0];
int end = subres[1];
int i = index;
for (; i < intervals.size(); i++) {
if (intervals[i][1] <= end) {
index = i;
continue;
}
while (i < intervals.size() && intervals[i][0] <= end) {
end = max(intervals[i][1], end);
subres = {start, end};
i++;
index++;
}
}
return subres;
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
vector<vector<int>> res;
for (int i = 0; i < intervals.size(); i++) {
vector<int> subres = {intervals[i][0], intervals[i][1]};
submerge(subres, intervals, i);
res.push_back(subres);
}
return res;
}
官方题解:先筛选不合并的添加到res中
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() == 0) return {};
sort(intervals.begin(), intervals.end());
vector<vector<int>> res;
for (int i = 0; i < intervals.size(); i++) {
int start = intervals[i][0];
int end = intervals[i][1];
if (res.empty() || res.back()[1] < start) {
res.push_back({start, end});
} else {
res.back()[1] = max(res.back()[1], end);
}
}
return res;
}
mid-反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。
不能使用long!!!
class Solution {
public int reverse(int x) {
int res = 0;
while (x != 0){
int temp = res;
res = res * 10 + x % 10;
if (res/10 != temp) return 0;//校验溢出
x /= 10;
}
return res;
}
}
387-字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回-1。
最简单:c++ find()和rfind()
int firstUniqChar(string s) {
for (int i = 0; i < s.size(); i++) {
if (s.find(s[i]) == s.rfind(s[i])) {
return i;
}
}
return -1;
}
方案二:使用一个256大小的数组来存放每个字符出现的次数,然后再遍历字符串中的每个字符,如果该字符出现的次数为1则返回。
class Solution {
public int firstUniqChar(String s) {
int len = s.length();
if(len==0)return -1;
int[] charTable = new int[256];
for(int i=0;i<len;i++){
int index = s.charAt(i)-‘0’;
charTable[index]++;
}
for(int i=0;i<len;i++){
int index = s.charAt(i)-‘0’;
if(charTable[index]==1)return i;
}
return -1;
}
}
有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
验证回文字符串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
注意小写字符比大写字符大32!
class Solution {
public boolean isPalindrome(String s) {
int len = s.length();
if(len<2)return true;
int left = 0;
int right = len-1;
while(left<right){
while(left<right&&letter(s.charAt(left))==null)left++;
while(left<right&&letter(s.charAt(right))==null)right–;
if(left<right&&letter(s.charAt(left))!=letter(s.charAt(right)))return false;
left++;
right–;
}
return true;
}
public Character letter(char c){
if(c>=‘a’&&c<=‘z’||c>=‘0’&&c<=‘9’)return c;
else if(c>=‘A’&&c<=‘Z’)return (char)(c+32);
else return null;
}
}
字符串转整数 (atoi)
实现 atoi,将字符串转为整数。
在找到第一个非空字符之前,需要移除掉字符串中的空格字符。如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值。如果第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
字符串可以在形成整数的字符后面包括多余的字符,这些字符可以被忽略,它们对于函数没有影响。
当字符串中的第一个非空字符序列不是个有效的整数;或字符串为空;或字符串仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。如果数值超过可表示的范围,则返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
public class Solution {
public int myAtoi(String str) {
str = str.trim();
if (str.isEmpty()) return 0;
int sign = 1, base = 0, i = 0, n = str.length();
if (str.charAt(i) == ‘+’ || str.charAt(i) == ‘-’) {
sign = (str.charAt(i++) == ‘+’) ? 1 : -1;
}
while (i < n && str.charAt(i) >= ‘0’ && str.charAt(i) <= ‘9’) {
if (base > Integer.MAX_VALUE / 10 || (base == Integer.MAX_VALUE / 10 && str.charAt(i) - ‘0’ > 7)) {
return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
base = 10 * base + (str.charAt(i++) - ‘0’);
}
return base * sign;
}
}
实现strStr()
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回-1。如果needle是空字符串,则返回0 。
数数并说
报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
- 1
- 11
- 21
- 1211
- 111221
1 被读作 “one 1” (“一个一”) , 即 11。
11 被读作 “two 1s” (“两个一”), 即 21。
21 被读作 “one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。
给定一个正整数 n ,输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串
class Solution {
public String countAndSay(int n) {
String countSequence=“1”;
if(n==1)return countSequence;
for(int i=2;i<=n;i++){
StringBuffer sb=new StringBuffer();
for(int j=0;j<countSequence.length()😉{
char current=countSequence.charAt(j);
int num=0;
while(j<countSequence.length()&&countSequence.charAt(j)==current){
num++;
j++;
}
sb.append(String.valueOf(num));
sb.append(current);
}
countSequence=sb.toString();
}
return countSequence;
}
}
mid-最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
使用startsWith()方法判断一个字符串是不是另一个字符串的前缀。
class Solution {
public String longestCommonPrefix(String[] strs) {
int count=strs.length;
String prefix=“”;
if(count!=0){
prefix=strs[0];
}
for(int i=0;i<count;i++){
while(!strs[i].startsWith(prefix)){
prefix=prefix.substring(0,prefix.length()-1);
}
}
return prefix;
}
}
1 两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数,返回它们的坐标。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
用HashMap!!!
class Solution {
public int[] twoSum(int[] nums, int target) {
int len = nums.length;
if(len<2)return null;
HashMap<Integer, Integer> map = new HashMap<>();
for(int i=0;i<len;i++){
if(map.containsKey(target-nums[i])){
return new int[]{i, map.get(target-nums[i])};
}
map.put(nums[i], i);
}
return null;
}
}
mid-15 三数之和 双指针
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
先对数组进行排序,然后遍历数组中小于等于0的部分,每次将当前元素设为三数中的其中一个,然后对数组剩余的部分运用两数之和的算法。
public List<List> threeSum(int[] nums) {
List<List> res = new ArrayList<List>();
if(nums == null || nums.length <= 2) return res;
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue; //去重
int target = -nums[i];
int left = i+1;
int right = nums.length-1;
while(left < right){
if(nums[left] + nums[right] == target){
List sublist = new ArrayList();
sublist = Arrays.asList(nums[i],nums[left],nums[right]);
res.add(sublist);
left++;
right–;
while (nums[left] == nums[left-1] && left < right) left++;
while (nums[right] == nums[right+1] && left < right) right–;
}else if(nums[left] + nums[right] < target){
left++;
}else{
right–;
}
}
}
return res;
}
mid-16 最接近的三数之和 双指针
三数之和最接近target,返回三数之和。假定存在一个解
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0]+nums[1]+nums[2];
for(int i = 0; i < nums.length-2; i++){
int left = i+1;
int right = nums.length-1;
while (left < right){
int sum = nums[i]+nums[left]+nums[right];
int dev = Math.abs(sum-target);
if(Math.abs(res-target) > dev){
res = sum;
}
if(sum < target){
left++;
}else{
right--;
}
}
}
return res;
}
mid-18 四数之和 双指针+双循环
/*
给定一个包含 n 个整数的数组 nums 和一个目标值 target,
判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?
找出所有满足条件且不重复的四元组。
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
使用双循环固定两个数,用双指针找另外两个数,通过比较与target 的大小(3数之和),移动指针。
??如何跳过重复值
方法一:使用set:不能存重复值
方法二:当有重复值,改变索引(例如sum3)
*/
public class Sum4 {
public List<List> fourSum(int[] nums, int target) {
List<List> resList = new ArrayList<List>();
Arrays.sort(nums);
Set<List> set = new HashSet<List>();
int l = nums.length;
for(int i = 0; i < l-3; i++) {
if(nums[i] > target && target > 0) break;
for(int j = i+1; j < l-2; j++) {
int start = j+1;
int end = l-1;
while(start < end) {
int sum = nums[i]+nums[j]+nums[start]+nums[end];
if(sum == target) {
set.add(Arrays.asList(nums[i],nums[j],nums[start],nums[end]));
start++;
end–;
}else if(sum < target) {
start++;
}else {
end–;
}
}
}
}
for(List list : set) {
resList.add(list);
}
return resList;
}
}
mid-11盛最多水的容器 双指针
public int maxArea(int[] height) {
if(height == null || height.length <= 1) return 0;
int left = 0;
int right = height.length-1;
int res = 0;
while(left < right){
res = Math.max(res, (right-left)*Math.min(height[right],height[left]));
if(height[left] < height[right]){
left++;
}else{
right--;
}
}
return res;
}
mid-31 下一个排列 双指针
必须 原地 修改,只允许使用额外常数空间。
思路:
从右往左遍历,找到num[i-1]<nums[i]的位置i-1,然后遍历第二次,找到比num[i-1]数字大的数,二者交换,然后对i-1之后的元素进行翻转
时间复杂度:O(N)O(N),其中 NN 为给定序列的长度。我们至多只需要扫描两次序列,以及进行一次反转操作。
空间复杂度:O(1)O(1),只需要常数的空间存放若干变量
public void nextPermutation(int[] nums) {
int left = -1;
int right = 0;
for(int i = nums.length - 1; i > 0; i–) {
if(nums[i-1] < nums[i]) {
left = i-1;
break;
}
}
if(left >= 0) { //对于321,其下一个排列是第一个123,所以第二次循环和交换不需要做
for(int i = nums.length - 1; i > left; i–) {
if(nums[i] > nums[left]){
right = i;
break;
}
}
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
reserve(left + 1, nums);
}
public void reserve(int begin, int[] nums){
int end = nums.length - 1;
while(begin < end){
int tmp = nums[begin];
nums[begin] = nums[end];
nums[end] = tmp;
begin ++;
end --;
}
}
88 合并两个有序数组 逆向 双指针
逆向
//非递减数组,合并后仍保持非递减
//输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
//输出:[1,2,2,3,5,6]
public void merge(int[] nums1, int m, int[] nums2, int n) {
int last = m+n; //逆向
while(n > 0){
if(m > 0 && nums1[m-1] > nums2[n-1]){
nums1[–last] = nums1[–m];
}else{
nums1[–last] = nums2[–n];
}
}
}
mid-四数相加
/*
-
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
-
遍历 A 和 B 所有元素和的组合情况,并记录在 ab_map 中,ab_map 的 key 为两数和,value 为该两数和出现的次数
遍历 C 和 D 所有元素和的组合情况,取和的负值判断其是否在 ab_map 中,若存在则取出 ab_map 对应的 value 值,count = count + value
*/
public class Sum4_2 {public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
int count = 0;
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
int len = A.length;
for(int i = 0; i < len; i++) {
for(int j = 0; j < len; j++) {
int val = 0;
if(map.containsKey(A[i]+B[j])) {
val = (A[i]+B[j])+1;
}else {
val = 1;
}
map.put(A[i]+B[j], val);
}
}for(int m = 0; m < len; m++) { for(int n = 0; n < len; n++) { int sum = -(C[m]+D[n]); int val = 0; if(map.containsKey(sum)) { val = sum; } count += val; } } return count;
}
}
mid-矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
你能想出一个常数空间的解决方案吗?
用第一行的元素表示每一列是否存在0,第一列的元素表示每一行是否存在0,再使用额外两个变量firstRowIsZero,firstColIsZero判断第一行以及第一列本身是否存在0。
class Solution {
public void setZeroes(int[][] matrix) {
int row = matrix.length;
if(row0)return;
int col = matrix[0].length;
if(col0)return;
boolean firstRowIsZero = false;
boolean firstColIsZero = false;
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
if(i!=0&&j!=0&&matrix[i][j]0){
matrix[i][0] = 0;
matrix[0][j] = 0;
}else if(matrix[i][j]0){
firstRowIsZero = i0 ? true:firstRowIsZero;
firstColIsZero = j0 ? true:firstColIsZero;
}
}
}
for(int i=1;i<row;i++){
if(matrix[i][0]==0){
for(int j=1;j<col;j++)matrix[i][j] = 0;
}
}
for(int j=1;j<col;j++){
if(matrix[0][j]==0){
for(int i=1;i<row;i++)matrix[i][j] = 0;
}
}
if(firstRowIsZero){
for(int j=0;j<col;j++)matrix[0][j] = 0;
}
if(firstColIsZero){
for(int i=0;i<row;i++)matrix[i][0] = 0;
}
}
}
mid-字谜分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串
说明:
● 所有输入均为小写字母。
● 不考虑答案输出的顺序
创建一个HashMap<String, List>,将每个词中的字母重新排序,把排好序之后的词作为key存入HashMap中,对每次词都进行同样的排序操作,然后将这个词放入对应的键值对中,最后返回HashMap中value的集合。
class Solution {
public List<List> groupAnagrams(String[] strs) {
int len = strs.length;
if(len==0)return new ArrayList<List>();
Map<String, List> map = new HashMap<>();
for(String str:strs){
char[] chars = str.toCharArray();
Arrays.sort(chars);
String keyStr = String.valueOf(chars);
if(!map.containsKey(keyStr))map.put(keyStr, new ArrayList());
map.get(keyStr).add(str);
}
return new ArrayList<List>(map.values());
}
}