目录
1. 两数之和
2. 两数相加
3. 无重复字符的最长字串
4. 寻找两个数组的中位数
5. Z字形变换
6. 整数反转
7. 字符串转换整数(atoi)
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1]
思路:将每个元素对应target的差值存入map中,若当前元素存在map中,将找到结果
class Solution {
public int[] twoSum(int[] nums, int target) {
Map map = new HashMap<>();
for(int i = 0; i < nums.length; ++i){
int num = target - nums[i];
if(map.containsKey(num)){
return new int[] {map.get(num), i};
}
map.put(nums[i], i);
}
return null;
}
}
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
思路:根据加法的步骤,记录进位标志,浪费一个节点来提高代码可读性也未尝不可
加完后还有最后的进位也需要处理(carryFlag != 0)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int carryFlag = 0;
ListNode result = new ListNode(0);
ListNode curr = result;
while (l1 != null || l2 != null || carryFlag != 0) {
int x = (l1 != null) ? l1.val : 0;
int y = (l2 != null) ? l2.val : 0;
int tmp = x + y + carryFlag;
curr.next = new ListNode(tmp % 10);
curr = curr.next;
carryFlag = tmp / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
return result.next;
}
}
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路:滑动窗口,i是窗口左边界,j是窗口右边界,将字符一个一个放入hashMap,如果当前字符在map中,则更新i为当前字符下标+1
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap map = new HashMap();
int res = 0;
for (int i = 0, j = 0; j < s.length(); ++j) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)) + 1, i);
}
res = Math.max(res, j - i + 1);
map.put(s.charAt(j), j);
}
return res;
}
}
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:nums1 = [1, 3],nums2 = [2],则中位数是 2.0
示例 2:nums1 = [1, 2],nums2 = [3, 4],则中位数是 (2 + 3)/2 = 2.5
思路:先排序然后取中位数,排序类似合并两个有序链表(自己的初始想法,以下是初始代码)
但时间复杂度是为O(m+n),空间复杂度为O(m+n)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//先排序然后取中位数,排序类似合并两个有序链表
int length1 = nums1.length;
int length2 = nums2.length;
int[] resArr = new int[length1 + length2];
int i = 0;
int j = 0;
int n = 0;
while (i < length1 || j < length2) {
if (i >= length1) {
resArr[n] = nums2[j++];
} else if (j >= length2) {
resArr[n] = nums1[i++];
} else if (nums1[i] > nums2[j]) {
resArr[n] = nums2[j++];
} else {
resArr[n] = nums1[i++];
}
++n;
}
int resLen = resArr.length;
return resLen % 2 == 0 ? (double) (resArr[resLen / 2] + resArr[resLen / 2 - 1]) / 2 : resArr[resLen / 2];
}
}
算法的时间复杂度为 O(log(min(m + n)))的解法
思路:首先,中位数:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
将A,B两个数组分别切割,切割点分别为i、j,分为左AB和右AB,保证切割点满足A[i-1] <= B[j] && B[j-1] <= A[i],从而保证左AB均小于等于右AB,并且,len(左AB)=len(总AB)-len(右AB),从而i+j = m-i + n-j,此时,
当A、B两个数组的总长度为偶数时,中位数=(max(A[i-1], B[j-1]) + min(A[i] + B[j]))/2,左AB的最大值和右AB的最小值的均值;
当A、B两个数组的总长度为奇数时,中位数=max(A[i-1], B[j-1]),左AB的最大值
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) {
// 保证 m <= n
return findMedianSortedArrays(B,A);
}
int iMin = 0;
int iMax = m;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
if (j != 0 && i != m && B[j - 1] > A[i]) {
iMin = i + 1;
} else if (i != 0 && j != n && A[i - 1] > B[j]) {
iMax = i - 1;
} else {
//满足以上两个切割点条件
int maxLeft = 0;
if (i == 0) {
maxLeft = B[j - 1];
} else if (j == 0) {
maxLeft = A[i - 1];
} else {
maxLeft = Math.max(B[j - 1], A[i - 1]);
}
//奇数直接返回
if ((m + n) % 2 == 1) {
return maxLeft;
}
int minRight = 0;
if (i == m) {
minRight = B[j];
} else if (j == n) {
minRight = A[i];
} else {
minRight = Math.min(B[j], A[i]);
}
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
Q1:为什么m <= n?
由于 0 <= i <= m 且 j = (m+n+1)/2−i,必须确保 j 不是负数。如果 n < m,那么 j 将可能是负数,而这会造成错误的答案。
Q2:j = (m+n+1)/2−i,为什么要+1?
因为当 A 数组和 B 数组的总长度是奇数时,要保证左半部分的长度比右半部分大1,这样中位数才是左AB的最大值
所以i + j = m - i + n - j + 1也就是 j = ( m + n + 1) / 2 - i
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。请你实现这个将字符串进行指定行数变换的函数:string convert(string s, int numRows);
示例 1:输入: s = "LEETCODEISHIRING", numRows = 3,输出: "LCIRETOESIIGEDHN"
示例 2:输入: s = "LEETCODEISHIRING", numRows = 4,输出: "LDREOEIIECIHNTSG"
L D R
E O E I I
E C I H N
T S G
思路:1.每一行都创建一个StringBuilder;2.遍历字符串,将每个字符放到对应行,遇到第一行和最后一行,行走的方向变化;3.将每一行的StringBuilder拼接
class Solution {
public String convert(String s, int numRows) {
if (s.length() < numRows || numRows == 1) {
return s;
}
//每一行都创建一个StringBuilder
List rowlist = new ArrayList<>(numRows);
for (int i = 0; i < numRows; ++i) {
rowlist.add(new StringBuilder());
}
//遍历字符串,将每个字符放到对应行
int curRow = 0;
boolean growRow = false;
for (char ch : s.toCharArray()) {
rowlist.get(curRow).append(ch);
//方向变化
if (curRow == 0 || curRow == numRows - 1) {
growRow = !growRow;
}
curRow += growRow ? 1 : -1;
}
//将每一行的StringBuilder拼接
StringBuilder res = new StringBuilder();
for (StringBuilder sb : rowlist) {
res.append(sb);
}
return res.toString();
}
}
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:输入: 123输出: 321
示例 2:输入: -123输出: -321
示例 3:输入: 120输出: 21
注意:假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
思路:通过取余(%)获取每位数,通过res = res * 10 + pop一位位递进,中间需判断是否有溢出情况
class Solution {
public int reverse(int x) {
int res = 0;
while (x != 0) {
//每位数
int pop = x % 10;
//Integer范围:-2147483648~2147483647
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && pop > 7)) {
return 0;
}
if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && pop < -8)) {
return 0;
}
res = res * 10 + pop;
x /= 10;
}
return res;
}
}
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:输入: "42"输出: 42
示例 2:输入: " -42"输出: -42,解释: 第一个非空白字符为 '-', 它是一个负号。我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:输入: "4193 with words"输出: 4193,解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:输入: "words and 987"输出: 0,解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:输入: "-91283472332"输出: -2147483648解释: 数字 "-91283472332" 超过 32 位有符号整数范围,因此返回 INT_MIN (−231) 。
思路:1. 去掉字符串前后空格;2. 处理第一个字符为正负号的情况;3. 一个一个取数判断通过res * 10 + tmp凑数;4. 判断是否溢出同7整数反转(初始想法)
class Solution {
public int myAtoi(String str) {
str = str.trim();
int res = 0;
int i = 0;
boolean negativeFlag = false;
if (str.startsWith("-")) {
i++;
negativeFlag = true;
}
if (str.startsWith("+")) {
i++;
}
char[] strArray = str.toCharArray();
for (; i < strArray.length; ++i) {
if (strArray[i] < '0' || strArray[i] > '9') {
return negativeFlag ? -res : res;
}
int tmp = strArray[i] - '0';
if (!negativeFlag && (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && tmp > 7))) {
return Integer.MAX_VALUE;
}
if (negativeFlag && (-res < Integer.MIN_VALUE / 10 || (-res == Integer.MIN_VALUE / 10 && tmp > 8))) {
return Integer.MIN_VALUE;
}
res = res * 10 + tmp;
}
return negativeFlag ? -res : res;
}
}