2024/7/22
递归
1.一个问题的解可以分解为几个子问题的解
2.这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
3.存在基线/终止条件
1.(LeetCode-70)爬楼梯
(问题) 假设你正在爬楼梯。需要
n阶你才能到达楼顶。 每次你可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1: 输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶 1、1 阶 + 1 阶 2、2 阶 提示: · 1<= n <= 45
递归求解法
典型 自顶向下
class Solution {
private Map<Integer , Integer > storeMap = new HashMap<>();
public int climbing(int n ){
if(n == 1) return 1;
if(n == 2) return 2;
if(null != storeMap.get(n))
return storeMap.get(n);
else {
int result = climbing(n-1) + climbing(n-2)
storeMap.put(n,result);
return result;
}
}
}
非递归求解法
循环的解法 -- 自底向上累加
class Slution{
if( n == 1 ) return 1;
if( n == 2 ) return 2;
int result = 0;
int pre = 2;
int prePre = 1;
for( int i = 3;i <= n; i++){
result = pre + prePre;
prePre = pre;
pre = result;
}
return result;
}
2.(LeetCode-509)斐波那契数列--同样的题解
斐波那契数 (通常用
F(n)表示)形成的序列称为 斐波那契数列 。该数列由0和1开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给定n,请计算F(n)。
同样的---解法 1.自顶向下-递归
class Solution {
private Map<Integer,Integer> storeMap = new HashMap<>();
public int fib(int n){
if (n == 0) return 0;
if (n == 1) return 1;
if(storeMap.get(n) != null) return storeMap.get(n);
else {
int result = fib(n-1) + fib(n-2);
storeMap.put(n,result);
return result;
}
}
}
解法 2.自底向上循环
class Solution {
public int fib(int n){
if (n == 0) return 0;
if (n == 1) return 1;
int result = 0;
int pre = 1;
int prePre = 0;
for ( int i = 2;i <= n;i++ ){
result = pre + prePre;
prePre = pre;
pre = result;
}
return result;
}
}
3.(LeetCode-1)两数之和
给定一个整数数组
nums和一个整数目标值target,请你在该数组中找出 和为目标值target的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。
方法一.递归
class Solution {
private Map<Integer,Integer> storeMap = new HashMap<>();
public int[] sum(int[] a ,int target){
int[] result = int[2];
for (int i = 0;i<nums.length; i++){
int another = target - nums[i];
int anotherIndex = storeMap.get(another);
if (anotherIndex != null){
result[0] = anotherIndex;
result[1] = i;
}
}
return result;
}
}
方法二.暴利枚举
class Solution {
public int[] sum(int[] nums,int target){
int[] result = int[2];
for(int i = 0 ; i < nums.length ; i++){
for (int j = 0 ; j < nums.length; j ++){
if(nums[i] + nums[j] == target){
result[0] = i;
result[1] = j;
}
}
}
return result;
}
}
4.(LeetCode-88)合并两个有序数组
给你两个按 非递减顺序 排列的整数数组
nums1和nums2,另有两个整数m和n,分别表示nums1和nums2中的元素数目。请你 合并
nums2到nums1中,使合并后的数组同样按 非递减顺序 排列。**注意:**最终,合并后数组不应由函数返回,而是存储在数组
nums1中。为了应对这种情况,nums1的初始长度为m + n,其中前m个元素表示应合并的元素,后n个元素为0,应忽略。nums2的长度为n。
方法一.直接合并排序法
class Solution{
public void merge(int[] nums1,int m , int[] nums2,int n ){
for (int i = 0;i < n ; i ++){
nums[m + i] = nums2[i]
}
Arrays.sort(nums1);
}
}
方法二.双指针
class Solution{
public void merge(int[] nums1,int m,int[] nums1,int n){
int k = m+n;
int[] temp = int[k];
for (int index = 0 , nums1Index = 0 , nums2Index = 0 ; index < k ;index ++){
if(nums1Index >= m){
temp[index] = nums2[nums2Index++];
} else if (nums2Index >= n){
temp[index] = nums1[nums1Index++];
} else if (nums1[nums1Index] < nums2[nums2Index]){
temp[index] = nums1[nums1Index++];
} else {
temp[index] = nums2[nums2Index++]
}
}
for (int i =0 ; i < k ; i ++){
nums1[i] = temp[i];
}
}
}
5.(LeetCode-283)自动零
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序
实例: 输入:[0,1,0,3,12] 输出:[1,3,12,0,0]
说明: 1.必须在原数组上操作,不能拷贝额外的数组 2.尽量减少操作次数
双指针解法
class Solution{
public void moveZeroes(int[] nums){
if(nums == null){
return;
}
int j = 0;
for (int i = 0; i < nums.length ; i++){
if(nums[i] != 0){
nums[j++] = nums[i]
}
}
for (int i = j;i<nums.length ; i++){
nums[i] = 0
}
}
}
6.(LeetCode-448)找到所有数组中消失的数字
给你一个含
n个整数的数组nums,其中nums[i]在区间[1, n]内。请你找出所有在[1, n]范围内但没有出现在nums中的数字,并以数组的形式返回结果。
实例: 输入:nums = [4,3,2,7,8,2,3,1] 输出:[5,6]
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
int n = nums.length;
for (int num:nums){
int x = (num -1 ) % n;
nums[x] += n;
}
List<Integer> result = new ArrayList<Integer>();
for (int i = 0 ; i < n ; i ++){
if(nums[i] <= n){
result.add(i+1);
}
}
return result;
}
}
1.标记出现的数字 遍历数组
nums,对每个数字num进行标记:
-
num = 4:- 计算
x = (4 - 1) % 8 = 3 - 将
nums[3]加上 8,结果:nums = [4, 3, 2, 15, 8, 2, 3, 1]
- 计算
-
num = 3:- 计算
x = (3 - 1) % 8 = 2 - 将
nums[2]加上 8,结果:nums = [4, 3, 10, 15, 8, 2, 3, 1]
- 计算
-
num = 2:- 计算
x = (2 - 1) % 8 = 1 - 将
nums[1]加上 8,结果:
nums = [4, 11, 10, 15, 8, 2, 3, 1]
- 计算
-
num = 15(原来的7加了8):- 计算
x = (15 - 1) % 8 = 6 - 将
nums[6]加上 8,结果:nums = [4, 11, 10, 15, 8, 2, 11, 1]
- 计算
-
num = 8:- 计算
x = (8 - 1) % 8 = 7 - 将
nums[7]加上 8,结果:nums = [4, 11, 10, 15, 8, 2, 11, 9]
- 计算
-
num = 2:- 计算
x = (2 - 1) % 8 = 1 - 将
nums[1]加上 8,结果:nums = [4, 19, 10, 15, 8, 2, 11, 9]
- 计算
-
num = 11(原来的3加了8):- 计算
x = (11 - 1) % 8 = 2 - 将
nums[2]加上 8,结果:nums = [4, 19, 18, 15, 8, 2, 11, 9]
- 计算
-
num = 9(原来的1加了8):- 计算
x = (9 - 1) % 8 = 0 - 将
nums[0]加上 8,结果:nums = [12, 19, 18, 15, 8, 2, 11, 9]
- 计算
标记后的数组:
nums = [12, 19, 18, 15, 8, 2, 11, 9]
遍历数组 nums,找出值小于等于 8 的位置:
nums[0] = 12(大于 8)nums[1] = 19(大于 8)nums[2] = 18(大于 8)nums[3] = 15(大于 8)nums[4] = 8(等于 8)nums[5] = 2(小于等于 8) → 位置5 + 1 = 6缺失nums[6] = 11(大于 8)nums[7] = 9(大于 8) 最终,缺失的数字是[5, 6]。 所以,输出结果是[5, 6]。
7.(LeetCode-21)合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
实例: 输入:l1 = [], l2 = [0] 输出:[0]
方法一.递归
class Solution {
public ListNode mergeTwoLists(ListNode l1,ListNode l2){
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
方法二.循环双指针
class Solution {
public ListNode mergeTwoLists(ListNode list1,ListNode list2){
if(list2 == null) return list1;
if(list1 == null) return list2;
ListNode resultNode = new ListNode(0);
ListNode p = resultNode;
while (list1 != null && list2 != null){
if(list1.val < list2.val){
p.next = list1;
list1 = list1.next;
}else {
p.next = list2;
list2 = list2.next;
}
p = p.next;
}
if(list1 != null) p.next = list1;
if(list2 != null) p.next = list2;
return resultNode.next;
}
}
8.(LeetCode-83)删除排序链表中的重复元素
给定一个已排序的链表的头
head, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
实例: 输入:head = [1,1,2] 输出:[1,2]
方法一.非递归
class Solution{
public ListNode deleteDuplicates(ListNode head){
if(head == null){
return head;
}
ListNode currentNode = head;
while (currentNode != null){
if(currentNode.val == currentNode.next.val){
currentNode.next = currentNode.next.next;
} else {
currentNode = currentNode.next;
}
}
return head;
}
}
方法二.递归
递归处理,在本质上其实就是将链表压栈后倒序处理了
class Solution{
public ListNode deleteDuplicates(ListNode head){
if(head == null || head.next == null) return head;
head.next = deleteDuplicates(head.next);
return head.val = head.next.val ? head.next : head;
}
}
9.(LeetCode-141)环形链表
给你一个链表的头节点
head,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪
next指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回
true。 否则,返回false。
class Solution {
public boolean hasCycle(ListNode head){
if(head == null) return false;
ListNode slowPtr = head,fastPtr = head;
while(fastPtr.next != null && fastPtr.next.next != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
if(slowPtr == fastPtr)
return true;
}
return false;
}
}