文章目录
P2 两数相加
0、题目

1、long和int的区别
int只有32位,最大只有2137383647,超过了就会溢出
而long有64位,更适合计算超过10位数的整数
2、链表使用
链表计算时,用一个result来固定链表开头,之后使用一个cur来进行辅助,他可以代表当前指针在哪,可以往后移动
3、递归和迭代的区别
递归是整个函数的循环
迭代是函数中某段代码的循环,例如for
4、代码
(递归法)
class Solution {
int carry = 0;//记录进位
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//定义终止条件,当l1,l2指针都为null时且进位为0 ->null
if (l1 == null && l2 == null && carry == 0) return null;
//当有一条链表为null 且 进位为0时,next指针直接指向另外一条链表返回
if (l1 != null && l2 == null && carry == 0) return l1;
else if (l1 == null && l2 != null && carry == 0) return l2;
//sum = 两链表指针位置上的数字加上进位
int sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry;
//计算进位
carry = sum / 10;
//计算链表的value
int value = sum % 10;
ListNode node = new ListNode(value);
//递归算出这个node的next指向
node.next = addTwoNumbers((l1 == null ? null : l1.next), (l2 == null ? null : l2.next));
return node;
}
}
(迭代法)
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int total;
// 来储存十位数,加到next里
int next1 = 0;
// 结果
ListNode resoult = new ListNode();
// 代表指针
ListNode cur = resoult;
// 当两者都能继续时
while (l1 != null && l2 != null) {
total = l1.val + l2.val + next1;
cur.next = new ListNode(total % 10);
next1 = total / 10;
l1 = l1.next;
l2 = l2.next;
cur = cur.next;
}
// 只有l1可以继续
while (l1 != null) {
total = l1.val + next1;
cur.next = new ListNode(total % 10);
next1 = total / 10;
l1 = l1.next;
cur = cur.next;
}
// 只有l2可以继续
while (l2 != null) {
total = l2.val + next1;
cur.next = new ListNode(total % 10);
next1 = total / 10;
l2 = l2.next;
cur = cur.next;
}
// 判断后面是否还有数字
// 应用在类似99+9中,会多出一个百位
if (next1 != 0) {
cur.next = new ListNode(next1);
}
// 这里为什么返回的是next
// 因为如果不用next会多出一个0,resoult只是一个伪头
return resoult.next;
}
}
P3 无重复字符的最长子串
0、题目

1、滑动窗口算法
此算法是一种基于两种指针的算法,两个指针之间形成一个窗口
这个窗口可以是固定大小,也可以是动态变化的
什么情况可以用滑动窗口来解决实际问题呢?
- 一般给出的数据结构是数组或者字符串
- 求取某个子串或者子序列最长最短等最值问题或者求某个目标值时
- 该问题本身可以通过暴力求解
2、这道题目中此算法的使用
利用了set这个数据类型的特性
先使用right进行移动,查找元素时,
(1)如果发现set中没有此元素,把right对应的元素添加到set中,同时让长度len增加
(2)如果发现set中有此元素,说明重复了,移动left,对set中的元素进行逐个删除,直至set中查找不到此元素,同时让长度len减小
(具体查看这个视频)
https://www.bilibili.com/video/BV113411v7Ak/?spm_id_from=333.788&vd_source=d222e7454d13bc863f6b03624d77b447
3、代码
public static int lengthOfLongestSubstring(String s) {
int right = 0;
int left = 0;
int maxLen = 0;
int len = 0;
Set set = new HashSet<>();
while (right != s.length()) {
if (!set.contains(s.charAt(right))) {
set.add(s.charAt(right));
right++;
len = right - left;
if (len > maxLen) {
maxLen = len;
}
} else {
set.remove(s.charAt(left));
left++;
len--;
}
}
return maxLen;
}
P4 寻找两个正序数组的中位数
0、题目

1、为什么使用二分查询法
一般有类似时间复杂度为log的、有序数组,这类字眼,而且同时出现时,就是使用二分查询法
2、二分查询法讲解
使用low、mid、high

3、此题解法1:将所有元素分为两部分

Partiton的计算:
根据partition1 + partition2 == (len(nums1)+len(nums2)+1)/2
这里需要满足x2 <= y6,x3 >= y5,然后对partition1逐步增加,直到满足x2 <= y6,x3 >= y5

注:边界条件: 若分割点取到了数组的首或尾时怎么办
根据数组的有序性在数组两侧添加虚拟的-∞和+∞.(不需要物理添加,只需要逻辑上添加)

代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 必须在较小的数组上尝试partition1的取值,因此对两个数组进行交换
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int len1 = nums1.length;
int len2 = nums2.length;
for (int partition1 = 0; partition1 <= len1; partition1++) {
// partition1在数组nums1上滑动, 根据partition1求出partition2
int partition2 = (len1 + len2 + 1) / 2 - partition1;
// 分别求出两个分割位置左右的值
int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];
int minRight1 = (partition1 == len1) ? Integer.MAX_VALUE : nums1[partition1];
int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];
int minRight2 = (partition2 == len2) ? Integer.MAX_VALUE : nums2[partition2];
// 若找到满足条件的分割方式,则求解中位数
if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
if ((len1 + len2) % 2 == 0) {
return ((double) Math.max(maxLeft1, maxLeft2)
+ Math.min(minRight1, minRight2)) / 2;
} else {
return (double) Math.max(maxLeft1, maxLeft2);
}
}
}
throw new IllegalArgumentException();
}
4、此题解法2:二分查找法
在上一个解法的基础上,改变partition1的求取方式,借助数组的有序性,减少求取partition1的次数

代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int len1 = nums1.length;
int len2 = nums2.length;
// 分别定义了partition1取值的上下限
int low = 0;
int high = nums1.length;
while (low <= high) {
int partition1 = (low + high) / 2;
int partition2 = (len1 + len2 + 1) / 2 - partition1;
int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];
int minRight1 = (partition1 == len1) ? Integer.MAX_VALUE : nums1[partition1];
int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];
int minRight2 = (partition2 == len2) ? Integer.MAX_VALUE : nums2[partition2];
if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {
// 若找到符合条件的分割,则直接返回
if ((len1 + len2) % 2 == 0) {
return ((double) Math.max(maxLeft1, maxLeft2)
+ Math.min(minRight1, minRight2)) / 2;
} else {
return (double) Math.max(maxLeft1, maxLeft2);
}
} else if (maxLeft1 > minRight2) {
// partiton1太靠右了,将区间向左倾斜
high = partition1 - 1;
} else {
// partition1太靠左了,将区间向右倾斜
low = partition1 + 1;
}
}
throw new IllegalArgumentException();
}
5、视频讲解
https://www.bilibili.com/video/BV1hk4y1B74M/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447
P5 最长回文子串
0、题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydMdkdHg-1680336290647)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230303114227932.png)]
1、中心扩散算法
根据某个char的左右对等关系来运转,分为l和r,如果l==r,l可以继续向左一位,而r则是向右一位,直到左右不对等,然后开始下一个char
2、代码
public static String longestPalindrome(String s) {
String result = "";
String re = "";
int l = 0;
int r = 0;
for (int i = 0; i < s.length(); i++) {
// 奇数时
r = i + 1;
l = i - 1;
while (r < s.length() && l >= 0 && s.charAt(l) == s.charAt(r)) {
r++;
l--;
}
// 这里出来的l和r,分别是我们所需要字符串的左边界-1和有边界+1
if (result.length() < r - l - 1) {
result = s.substring(l + 1, r);
}
// 偶数时
r = i + 1;
l = i;
while (r < s.length() && l >= 0 && s.charAt(l) == s.charAt(r)) {
r++;
l--;
}
if (result.length() < r - l - 1) {
result = s.substring(l + 1, r);
}
}
return result;
}
3、视频
https://www.bilibili.com/video/BV1UZ4y1U7tt/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447
P6 N字形变换
0、题目
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVnMVc9E-1680336290647)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230304144659845.png)]](/https://i-blog.csdnimg.cn/blog_migrate/206c89784b871abf76411c458108e86d.png)
1、思路
利用一个新的数组,长度为numRows,遍历字符串s,如果i到numRows-1就开始减小,到达0就开始增加

2、代码
public static String convert(String s, int numRows) {
int temp = 0; // 0向下,1向上
int num = 0; // 上次增长位置
String result = "";
if (numRows == 1) {
return s;
}
String[] strings = new String[numRows];
for (int i = 0; i < s.length(); i++) {
if (strings[num]==null){
strings[num] = "";
}
strings[num] += s.charAt(i);
if (num == numRows - 1) {
temp = 1;
} else if (num == 0) {
temp = 0;
}
if (temp == 0) {
num++;
} else if (temp == 1) {
num--;
}
}
for (String i : strings) {
if (i == null) {
return result;
}
System.out.println(i);
result += i;
}
return result;
}
}
P7 整数反转 、P8 字符串转换整数 (atoi)
0、题目
略
1、int类型溢出问题
查看是否溢出,包括正负数
P7溢出代码
while (x != 0) {
// if ((result > 214748364 || result < -214748364)) {
// return 0;
// }
if (((result > (Integer.MAX_VALUE - ge) / 10) && temp == true)
|| ((result < (Integer.MIN_VALUE - ge) / 10) && temp == false)) {
return 0;
}
ge = x % 10;
x = x / 10;
result = result * 10 + ge;
}
P8溢出代码
while (i < s.length() && ((s.charAt(i) - '0') >= 0 && (s.charAt(i) - '0') <= 9)) {
// 在加上下一个数之前,对reslut进行溢出验证
// 用最大值减去此次想要加上的值,再除10,和reslut进行对比,如果大于就溢出了
if (result > (Integer.MAX_VALUE - (s.charAt(i) - '0'))/10) {
return temp ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
result = (s.charAt(i) - '0') + result * 10;
i++;
}
P13 罗马数字转整数
0、题目

1、算法简述
使用map来存储字符以及他的对应值,只存储基本字符,然后判断当前字符和他之前一位的字符,他们所对应值的大小,之后进行操作
2、代码
/*
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
*/
public static int romanToInt(String s) {
int result = 0;
int last = 0; // 前一位的数字
Map<String, Integer> map = new HashMap<>();
map.put("I", 1);
map.put("V", 5);
map.put("X", 10);
map.put("L", 50);
map.put("C", 100);
map.put("D", 500);
map.put("M", 1000);
for (int i = 0; i < s.length(); i++) {
Integer integer = map.get(s.charAt(i) + "");
result += integer;
if (integer > last) { // 例如IV,4
result -= last * 2;
}
last = integer;
}
return result;
}
P15 三数之和、P16 最接近的三数之和
0、题目

1、三数之和的算法思路
三数之和需要先把数组排序(Arrays.sort(nums)),使用for从0开始遍历,之后创建两个指针,分别是left和
right,分别从开头0和结尾length-1开始算起,把nums[i/left/right]加起来,比较和目标大小,根据大了还是小了
来移动两个指针
2、代码
public static int threeSumClosest(int[] nums, int target) {
int result = nums[0] + nums[1] + nums[2];
int temp;
// 排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
temp = nums[i] + nums[left] + nums[right];
if (Math.abs(target - temp) < Math.abs(target - result)) {
result = temp;
} else if (temp - target > 0) {
right--;
} else if (temp - target < 0) {
left++;
} else {
return result;
}
}
}
return result;
}
P20 有效的括号
0、题目

1、思路
使用map来存储两个括号之间的对应关系,然后使用数组来存储,有对应的就消除前括号,没有就继续存储

2、代码
public static boolean isValid(String s) {
HashMap<Character, Character> map = new HashMap<>();
map.put('(', ')');
map.put('[', ']');
map.put('{', '}');
if (s.length() % 2 == 1) {
return false;
}
char[] chars = new char[s.length()];
int temp = 0;
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
chars[temp] = s.charAt(i);
temp++;
continue;
}
if (temp == 0){
return false;
}
if (map.get(chars[temp - 1]) == s.charAt(i)) {
chars[temp-1] = '0';
temp--;
} else {
return false;
}
}
return (chars[1] == '0');
}
P22 括号生成
0、题目

1、思路
使用递归和剪枝进行

当左括号 < 右括号时,return
当左括号数目 > n时,return
左括号必须比右括号先加入
当左括号 == 右括号 == n时,把当前结合出来的字符串加到list里,return
2、代码
public List<String> list = new ArrayList<>();
int n;
public List<String> generateParenthesis(int n) {
this.n = n;
String str = "";
curr(str, 0, 0);
return list;
}
public void curr(String str, int left, int right) {
if (right == n && left == n) {
list.add(str);
return;
}
if (left < right || left > n) {
return;
}
curr(str + "(", left + 1, right);
curr(str + ")", left, right + 1);
return;
}
当时的疑问:这样左括号不会一直先加入吗,直到左括号加完?
解答:这里的两个curr是两个不相关的递归,这里的str不是相同的,先加入的“(”执行到最后也会return,只是把符合要求的加到list里
P28 找出字符串中第一个匹配项的下标
0、题目

1、KMP算法(重点)

视频:
https://www.bilibili.com/video/BV1AY4y157yL/?spm_id_from=333.880.my_history.page.click&vd_source=d222e7454d13bc863f6b03624d77b447
图解
https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solution/duo-tu-yu-jing-xiang-jie-kmp-suan-fa-by-w3c9c/
使用next数组来辅助计算,两个指针,i指针会一直往右走,而j指针会向左和向右
当i发现两个字符串对应不上时,用当前j指针向左走,走当前指针所对应的next数组值
next数组计算:用下边字符串的前后缀进行计算,如果找到一样的时候,next数组的值加1,
如果找不到时,会一直减1直至0,然后从0开始重新计算
2、代码
next数组代码
public static int[] getNext(String needle) {
int[] next = new int[needle.length()];
for (int right = 1, left = 0; right < needle.length(); right++) {
// 定义好两个指针right与left
// 在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面
while (left > 0 && needle.charAt(left) != needle.charAt(right)) {
// 如果不相等就让left指针回退,到0时就停止回退
left = next[left - 1];//进行回退操作;
}
if (needle.charAt(left) == needle.charAt(right)) {
left++;
}
next[right] = left;
// 这是从 1 开始的
}
return next;
// 循环结束的时候,next数组就已经计算完毕了
}
对比代码
public static int strStr(String haystack, String needle) {
int[] next = getNext(needle);
for (int i = 0, j = 0; i < haystack.length(); i++) {
while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
j = next[j - 1];
}
if (haystack.charAt(i) == needle.charAt(j)) {
j++;
}
if (j == needle.length()) return i - needle.length() + 1;
}
return -1;
}
P29 两数相除
0、题目

1、算法思路
如果是一个一个加上去的话,算法会很慢,因为量太多了
我们利用2的n次方来计算,比如divisor = 3:3—》6—》12—》24—》48这样来跳着计算
而且n是从最大值32开始逐步向下减少,一旦发现dividend>>i 大于 divisor就开始改变dividend
2、代码
public static int divide(int dividend, int divisor) {
if (dividend == 0) return 0;
if (dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;
int flag = dividend ^ divisor; // 如果两个符号不相同,flag是负数,相同则为正数或是0(两者相同的情况下)
System.out.println(flag);
long new_dividend = Math.abs((long) dividend);
long new_divisor = Math.abs((long) divisor);
int result = 0;
// >>i相当于除以2的i次方
for (int i = 32; i >= 0; i--) {
if (new_dividend >> i >= new_divisor) {
new_dividend -= new_divisor << i;
result += 1 << i;
}
}
return flag < 0 ? -result : result;
}
3、视频
https://www.bilibili.com/video/BV1Su411Z71w/?spm_id_from=333.337.search-card.all.click&vd_source=d222e7454d13bc863f6b03624d77b447
P34 在排序数组中查找元素的第一个和最后一个位置
0、题目

1、思路
这里看到时间复杂度为logN级就应该想到要使用二分查找法,但我们普通的二分查找法需要有序且不重复的序列,这里要对所使用的二分查找法进行修改
先是左边界,再找出右边界
因为普通的/法,比如3/2=1,那么可以应用到查找左边界,同理(3+1)/2=2就可以应用到右边界
左边界需要先定义r,右边界则是l
2、代码
public static int[] searchRange(int[] nums, int target) {
if (nums.length == 0) {
return new int[]{-1, -1};
}
int l = 0, r = nums.length - 1;
int mid = 0;
// 求出左边界
while (l < r) {
mid = (l + r) >> 1;
if (target <= nums[r]) r = mid;
else l = mid + 1;
}
int L = l;
if (nums[r] != target) return new int[]{-1, -1};
l = 0;
r = nums.length - 1;
// 求出右边界
while (l < r) {
mid = (l + r + 1) >> 1;
if (nums[l] <= target) l = mid;
else r = mid - 1;
}
return new int[]{L, r};
}
本文详细介绍了几种编程算法,包括链表表示的两数相加的递归与迭代解法,滑动窗口在找无重复字符最长子串问题中的应用,二分查询法用于寻找两个正序数组中位数的两种策略,以及最长回文子串的中心扩散算法。此外,还涵盖了整数反转和字符串转换整数过程中的溢出处理问题。

1607

被折叠的 条评论
为什么被折叠?



