题目1-5
1.两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) { //遍历
for (int j = i + 1; j < nums.length; j++) { //从当前的下一个继续双重遍历
if (nums[j] == target - nums[i]) { //如果刚好满足条件,那么输出
return new int[] { i, j };
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
2.两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
/**
* 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) {
if(l1==null) return l2;
if(l2 == null) return l1;
ListNode dummy = new ListNode(0); //dunmmy指向头结点
ListNode current = dummy; //current是当前指针
int carry = 0; //carry是进位
while(l1 != null && l2 != null){
int dig = l1.val+l2.val+carry; //数字一+数字二+进位
int val =dig %10; //取余得到本位结果
carry = dig /10; //除10得到下位进位
ListNode newnode = new ListNode(val); //new 新节点
current.next = newnode;
current = current.next; //current后移
l1 = l1.next; //l1后移
l2 = l2.next; //l2后移
}
while(l1 != null){
int dig = l1.val+carry; //12已经空了
int val =dig %10;
carry = dig /10;
ListNode newnode = new ListNode(val);
current.next = newnode;
current = current.next;
l1 = l1.next;
}
while(l2 != null){
int dig = l2.val+carry; //l1已经空了
int val =dig %10;
carry = dig /10;
ListNode newnode = new ListNode(val);
current.next = newnode;
current = current.next;
l2 = l2.next;
}
if(carry != 0) current.next = new ListNode(carry); //若进位不是0,就new 一个新的节点当做进位后的首位
return dummy.next; //dummy肯定是空的了,dummy的下一个才是有值的
}
}
3.无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLength = 0; //定义最大长度
char[] chars = s.toCharArray(); //字符串转为char数组
int leftIndex = 0; //定义左索引
for (int j = 0; j < chars.length; j++) { //遍历
for (int innerIndex = leftIndex; innerIndex < j; innerIndex++) {
//内索引(从左索引起)遍历至j
if (chars[innerIndex] == chars[j]) { //若出现相同字符
maxLength = Math.max(maxLength, j - leftIndex);
//重新定义最大长度:max{当前最大长度,j-左索引}
leftIndex = innerIndex + 1; //左索引更新为 当前(出现相同字符时)内索引+1
break;
}
}
}
return Math.max(chars.length - leftIndex, maxLength);
}
}
4.寻找两个有序数组的中位数
给定两个大小为 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
class Solution {
/*
用 len 表示合并后数组的长度,如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,
如果遍历的话需要遍历 int(len/2 ) + 1 次。
如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。
所以遍历的话,奇数和偶数都是 len/2+1 次。
返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。
所以我们用两个变量 left 和 right,right 保存当前循环的结果,
在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,
也就是上一次循环的结果,接下来 right 更新为最后一次的结果。
循环中该怎么写,什么时候 A 数组后移,什么时候 B 数组后移。
用 aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置。
如果 aStart 还没有到最后并且此时 A 位置的数字小于 B 位置的数组,那么就可以后移了。
也就是aStart<m&&A[aStart]< B[bStart]。
但如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],则会越界,所以判断下 bStart 是否大于数组长度了,这样 || 后边的就不会执行了,也就不会导致错误了,所以增加为 aStart<m&&(bStart) >= n||A[aStart]
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//不妨认为nums1为A nums2为B
int m = nums1.length; //A数组的长度
int n = nums2.length; //B数组的长度
int len = m + n; //合并后数组的长度
int left = -1, right = -1; //right 保存当前循环的结果
int aStart = 0, bStart = 0;
//aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置
for (int i = 0; i <= len / 2; i++) {
//遍历的话,奇数和偶数都是 len/2+1 【0---len/2】
left = right; //每次循环前将 right 的值赋给 left
//这样在最后一次循环的时候,left 将得到 right 的值
//也就是上一次循环的结果,接下来 right 更新为最后一次的结果
if (aStart < m && (bStart >= n || nums1[aStart] < nums2[bStart])) {
//如果 aStart 还没有到最后, B[ bStart ]不会越界
//并且此时 A 位置的数字小于 B 位置的数字
//那么就可以后移了
right = nums1[aStart++];
} else {
right = nums2[bStart++];
}
}
if ((len % 2) == 0)
return (left + right) / 2.0;
else
return right;
}
}
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len <= 1) {
return s;
}
int longestPalindrome = 1;
String longestPalindromeStr = s.substring(0, 1);
boolean[][] dp = new boolean[len][len];
// abcdedcba
// l r
// 如果 dp[l, r] = true 那么 dp[l + 1, r - 1] 也一定为 true
// 关键在这里:[l + 1, r - 1] 一定至少有 2 个元素才有判断的必要
// 因为如果 [l + 1, r - 1] 只有一个元素,不用判断,一定是回文串
// 如果 [l + 1, r - 1] 表示的区间为空,不用判断,也一定是回文串
// [l + 1, r - 1] 一定至少有 2 个元素 等价于 l + 1 < r - 1,即 r - l > 2
// 写代码的时候这样写:如果 [l + 1, r - 1] 的元素小于等于 1 个,即 r - l <= 2 ,就不用做判断了
// 因为只有 1 个字符的情况在最开始做了判断
// 左边界一定要比右边界小,因此右边界从 1 开始
for (int r = 1; r < len; r++) {
for (int l = 0; l < r; l++) {
// 区间应该慢慢放大
// 状态转移方程:如果头尾字符相等并且中间也是回文
// 在头尾字符相等的前提下,如果收缩以后不构成区间(最多只有 1 个元素),直接返回 True 即可
// 否则要继续看收缩以后的区间的回文性
// 重点理解 or 的短路性质在这里的作用
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > longestPalindrome) {
longestPalindrome = r - l + 1;
longestPalindromeStr = s.substring(l, r + 1);
}
}
}
}
return longestPalindromeStr;
}
}