重刷leedcode
【ps】下面内容大部分是参考leedcode官方解题和b站上的油管leedcode视频。还有自己做的一些笔记
油管leedcode视频
001 两数之和
001-ps:
使用时有一个前提:返回的结果都在数组里面。因为返回的是两个数,所以list.add()要添加两次。
array和map联合使用,取长补短.map中存入Array[i]作为键,将对应的下标作为值
Array[i] | i |
---|---|
K | V |
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> result=new ArrayList<>();
Map<Integer,Integer>map=new HashMap<>();
public int[] twoSum(int[] nums, int target) {
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
result.add(i);
result.add(map.get(target-nums[i]));
}else{
map.put(nums[i],i);
}
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
}
002 add two numbers 两数相加
002-ps:
除10得到当前位的十进制数,模%10得到两数相加后的进位数
/**
* 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) {
//需要记录头节点
ListNode result=new ListNode(0);
ListNode t=result;
int sum=0;
//需要注意,这里是 || 或操作,而非 &&与操作-----》或是最长的遍历完循环才结束,与是最短的遍历完,循环就结束了
while(l1!=null || l2!=null){
//第一次 0/10=0,第二次:7/10=0 第三次:10/10=1
//除10得到的是上一轮进位的数。如果上一轮没有进位,则结果为0
sum=sum/10;
//一定是不为空,才能操作
if(l1!=null){
sum+=l1.val;
l1=l1.next;
}
//一定是不为空,才能操作
if(l2!=null)
{
sum+=l2.val;
l2=l2.next;
}
//t.next~第一次:7%10=7,第二次:10%10=0.第三次:3+4+1=8
//每一位相加后抛开进位后的结果
t.next=new ListNode(sum%10);
t=t.next;
}
//最后循环完如果还有进位,就需要把进位的1补上去----相加要么不进位,要么进位为1
// ps: 2->4->3 + 9->6->9 = 1->1->3->1(就是循环完还有进位,就new ListNode(1)得到的)
if(sum/10 !=0) t.next=new ListNode(1);
return result.next;
}
}
003 . 无重复字符的最长子串
003-ps:
class Solution {
public int lengthOfLongestSubstring(String s) {
//if(s==null||s.length()==0) return 0;
int max=0;
//char[] datas = s.toCharArray();
boolean[] used=new boolean[128];
int left=0;
int right=0;
while(right<s.length()){
//while(left<=right&& right<s.length()){
if(used[s.charAt(right)]==false){
used[s.charAt(right)] = true;
right++;
}else{
max=Math.max(max,(right-left));
//比如这几种情况: "abcdabc" , "abaaacdd"
while(left<right&&s.charAt(right)!=s.charAt(left)){
//while(left<right&&used[s.charAt(right)]!=used[s.charAt(left)]){
//踢掉,就是恢复到原始状态
used[s.charAt(left)]=false;
left++;
}
//位置别放错了
right++;
left++;
}
}
max=Math.max(max,(right-left));
return max;
}
}
004 寻找两个有序数组的中位数
ps
- int型的最大数和最小数 Integer.MIN_VALUE,Integer.MAX_VALUE
- 无穷大数和无穷小的数 正无穷大 POSITIVE_INFINITY 负无穷大(即无穷小)NEGATIVE_INFINITY
- NaN不与任何数值相等。表示非数。即不合法的数—比如对一个负数开方或者 3/0结果就是NaN(not a number)
首先需要理解中位数的概念:它将一个集合分成两个集合,左集合中的数全都小于右集合中的数。所以我们需要分别对数组1和数组2进行切割。最终理想的样子是
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
如果左集合的元素=右集合的元素,那么我们就可用公式Math.max(A[i-1],B[j-1])+Math.min(A[i],B[j]))/2.0求出中位数
如何能使左右集合的元素都相等呢。
为此我们可以把数组1和数组2的长度都扩大两倍,找切割的边界时在除以2给它除回来就可以了。这样就可以不用再讨论nums1.length+nums2.length等于奇数和等于偶数的情况了。也简化了几种需要特殊讨论的情况。
1. 将数组1长度扩大一倍
2. 选数组1的右中位数作为切割点,组2的右边界点计算时不除以2
3. 计算r1,r2,r3,r4时要记得除以2
4. 左右指针的移动是边界点的下标值加1,减1
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
} else {
int m = nums1.length;
int n = nums2.length;
int r1 = 0, r2 = 0, r3 = 0, r4 = 0;
int left = 0;
int right = m * 2;
while (left <= right) {
int i = (left + right) >>> 1;
int j = m + n - i;
r1 = (i == 0) ? Integer.MIN_VALUE : nums1[(i - 1) / 2];
r2 = (i == 2 * m) ? Integer.MAX_VALUE : nums1[i / 2];
r3 = (j == 0) ? Integer.MIN_VALUE : nums2[(j - 1) / 2];
r4 = (j == 2 * n) ? Integer.MAX_VALUE : nums2[(j) / 2];
if (r1 > r4) {
right = i - 1;
} else if (r2 < r3) {
left = i + 1;
} else break;
}
return (Math.max(r1, r3) + Math.min(r2, r4)) / 2.0;
}
}
}
这是官方题解的:
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && B[j-1] > A[i]){
iMin = i + 1; // i is too small
}
else if (i > iMin && A[i-1] > B[j]) {
iMax = i - 1; // i is too big
}
else { // i is perfect
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-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;
}
}
005 最长回文子串
分析:
class Solution {
public String longestPalindrome(String s) {
//判断输入是否有效 //也可用Assert()断言来做
if (s == null || s.length() == 0) return s;
int len = s.length();
char[] sChar = s.toCharArray();
int start = 0, length = 1; //记录最后的结果,从哪点开始,长度是多少。发现一个回文子串,就会更新这两个值
/**
* 记住这个动态规划的框架
*
* 以下两个for循环是初始化
*/
boolean[][] isPalindrome = new boolean[len][len];
for (int i = 0; i < len; i++) {
/**
* 自己和自己组合---单字符的初始化
* 找长度为1的回文串
*/
isPalindrome[i][i] = true;
}
/**
* 自己和相邻的组合---相邻两字符的初始化
* 找长度为2的回文串
*/
for (int i = 0; i < len - 1; i++) {
//相邻两个字母中间没有字母了,所以不需要对中间字符串进行回文串的判断
if (sChar[i] == sChar[i + 1]) {
//如果相邻两个字符一样,就是true即回文串
isPalindrome[i][i + 1] = true;
start = i; //从当前开始
length = 2;
/**
* 如果需要遍历所有的回文
* 就将 start = j; length = i;放入list里面即可
*/
}
}
/**
* 状态转移
* 找长度为3...4..5...的回文串
*/
for (int i = 3; i <= len; i++) {
/**
* j是在横坐标上递增进行字符串的遍历
* 由于使用了 j + i - 1 < len判断,所以j最大只会移动到len-i+1的位置,而非字符串的终点。这只由于我们要查找的是回文字符串所决定的
* j是横坐标(头字符---横坐标指)针--从前往后,j+i-1是纵坐标(尾字符---总坐标指针,从后往前)
* isPalindrome[j + 1][j + i - 2]它是中间子字符的是否是回文数的状态
*/
for (int j = 0; j + i - 1 < len; j++) {
/**
* 状态转移方程,如果首位字符相同,中间的也是回文数字
* sChar[j]和sChar[j+2]中间的字符刚好是三个,2=i-1=3-1 随着i++,2从2变到3,4,5.....
*
* sChar[j] == sChar[j + i - 1]先判断字符是否相等,再判断中间的子字符是否是回文字符串
*
* isPalindrome[j + 1][j + i - 2]记录了上一阶段的状态。所以这里发生了状态的转移
*/
if (sChar[j] == sChar[j + i - 1] && isPalindrome[j + 1][j + i - 2] == true) {
isPalindrome[j][j + i - 1] = true;
//满足条件,j就会右移
start = j;
//所以这里不需要判断i是否是最长的
length = i;
/**
* 如果需要遍历所有的回文
* 就将 start = j; length = i;放入list里面即可
*/
}
}
}
return s.substring(start, start + length);
}
}
6. Z 字形变换
分析
题目说的是z字形,可给出的实例是N字形的变换吗。题目改为N字形变换会更好。
ps :代码来自官方题解
class Solution {
public String convert(String s, int numRows)
{
if(numRows==1) return s;
String result="";
int len =s.length();
numRows=Math.min(numRows,len)-1;
int curNumRow=0;
boolean goingDown=false;
List<StringBuilder> dataContainer=new ArrayList<>();
for(int i=0;i<=numRows;i++){
dataContainer.add(new StringBuilder());
}
char[] sChars=s.toCharArray();
for(int i=0;i<sChars.length;i++){
dataContainer.get(curNumRow).append(sChars[i]);
if (curNumRow==0||curNumRow==numRows){goingDown=!goingDown;}
curNumRow=goingDown?curNumRow+1:curNumRow-1;
}
for (StringBuilder stringBuilder:dataContainer){
result+= stringBuilder.toString();
}
return result;
}
}
7.整数反转
这是我自己想的,官网运行时击败了33%的用户
class Solution {
public int reverse(int x) {
int result = 0;
int temp = 1;
int j = 0;
Stack<Integer> stack = new Stack<>();
stack.add(x % 10);
while ((x /= 10) != 0) {
stack.add(x % 10);
}
// stack.add(x); 这一步是我想当然的一步,所以错了--多添加了0到栈中。
result = stack.pop();
while (!stack.isEmpty()) {
temp *= 10;
j++;
if (j == 9 && (stack.peek() >= 2 || stack.peek() <= -2)) {
if (result > 147483647 || result < -147483648) {
return 0;
}
}
result += stack.pop() * temp;
}
return result;
}
}
这是教学视频上的:击败了100%的提交者
class Solution {
public int reverse(int x) {
int result=0;
while (x!= 0) {
int temp = result * 10 + x % 10;
/**
*如果溢出,则(temp - x % 10) / 10 != result就为true
*/
if ((temp - x % 10) / 10 != result) return 0;
result = temp;
x/=10;
}
return result;
}
}
8. 字符串转换整数 (atoi)
分析
这里我们使用正则表达式从字符串中取出整数,作为结果字符串,再使用Integer.parseInt(" ")去转换成整数
使用正则表达式:(java里面用到\的都需要用\来表示\,第一个\表示转义)
(java里面是两个斜杠,第一个斜杠用来转义)[\+\-]
^:匹配字符串开头
[\+\-]:代表一个+字符或-字符
?:前面一个字符可有可无
\d:一个数字
+:前面一个字符的一个或多个
\D:一个非数字字符
*:前面一个字符的0个或多个
import java.util.regex.*;
class Solution {
public int myAtoi(String str) {
str=str.trim();
Pattern pattern=Pattern.compile("^[\\+\\-]?\\d+");
Matcher matcher=pattern.matcher(str);
int value=0;
if(matcher.find()){
String result=str.substring(matcher.start(),matcher.end());
try{
return Integer.parseInt(result);
}catch(Exception e){
//这里不能写成 "-" (这表示字符串String类型的) '-'表示字符(char类型的)
value= result.charAt(0)=='-'?Integer.MIN_VALUE:Integer.MAX_VALUE;
return value;
}
}
return value;
}
}
9. 回文数
分析
class Solution {
public boolean isPalindrome(int x) {
//对整数的操作,一般都会用到循环/10(得到最高位)和循环%10(得到最低位)对10取余的操作
//模板一般 int temp=1; while((x/=10)!=0) {temp*=10;} ---->x/temp>=10{}
if(x<0) return false;
int temp=1;
while(x/temp>=10){
temp*=10;
}
while(x!=0){
if(x/temp!=x%10){return false;}
//这一步是获得去掉首和尾的数后的数
//x是一位数,temp是1
//x是两位数,temp是10,x是三位数,temp是100
//.........
x=(x-(x/temp*temp))/10;
//这里不能/10,否则位数对不上
//上一步划掉了两个数,所以要除以100
temp/=100;
}
return true;
}
}
10. 正则表达式匹配
分析
class Solution {
public boolean isMatch(String s, String p) {
boolean[][] match=new boolean[s.length()+1][p.length()+1];
if(s==null||p==null) return false;
//if(s==""||p=="") return true;
match[0][0]=true;
for(int i=1;i<=p.length();i++){
//重点温习
if(p.charAt(i-1)=='*'){
match[0][i]=match[0][i-2];
}
}
for(int i=1;i<=s.length();i++){
for(int j=1;j<=p.length();j++){
//p.charAt(j)
if(p.charAt(j-1)=='.'||p.charAt(j-1)==s.charAt(i-1)){
match[i][j]=match[i-1][j-1];
//else if(p.charAt(j)=='*')
}else if(p.charAt(j-1)=='*'){
if(p.charAt(j-2)==s.charAt(i-1)||p.charAt(j-2)=='.'){
match[i][j]=match[i][j-2] || match[i-1][j];
}else{
match[i][j]=match[i][j-2];
}
}
}
}
return match[s.length()][p.length()];
}
}