LeetCode-热题100 Part.2

89 阅读7分钟

2024/7/22

递归

1.一个问题的解可以分解为几个子问题的解
2.这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
3.存在基线/终止条件

1.(LeetCode-70)爬楼梯

(问题) 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1: 输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶 1、1 阶 + 1 阶 2、2 阶 提示: · 1<= n <= 45

image.png

image.png

递归求解法

典型 自顶向下

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 进行标记:

  1. num = 4:

    • 计算 x = (4 - 1) % 8 = 3
    • nums[3] 加上 8,结果: nums = [4, 3, 2, 15, 8, 2, 3, 1]
  2. num = 3:

    • 计算 x = (3 - 1) % 8 = 2
    • nums[2] 加上 8,结果: nums = [4, 3, 10, 15, 8, 2, 3, 1]
  3. num = 2:

    • 计算 x = (2 - 1) % 8 = 1
    • nums[1] 加上 8,结果:
      nums = [4, 11, 10, 15, 8, 2, 3, 1]
  4. num = 15 (原来的 7 加了 8):

    • 计算 x = (15 - 1) % 8 = 6
    • nums[6] 加上 8,结果: nums = [4, 11, 10, 15, 8, 2, 11, 1]
  5. num = 8:

    • 计算 x = (8 - 1) % 8 = 7
    • nums[7] 加上 8,结果: nums = [4, 11, 10, 15, 8, 2, 11, 9]
  6. num = 2:

    • 计算 x = (2 - 1) % 8 = 1
    • nums[1] 加上 8,结果: nums = [4, 19, 10, 15, 8, 2, 11, 9]
  7. num = 11 (原来的 3 加了 8):

    • 计算 x = (11 - 1) % 8 = 2
    • nums[2] 加上 8,结果: nums = [4, 19, 18, 15, 8, 2, 11, 9]
  8. 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 的位置:

  1. nums[0] = 12(大于 8)
  2. nums[1] = 19(大于 8)
  3. nums[2] = 18(大于 8)
  4. nums[3] = 15(大于 8)
  5. nums[4] = 8(等于 8)
  6. nums[5] = 2(小于等于 8) → 位置 5 + 1 = 6 缺失
  7. nums[6] = 11(大于 8)
  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;  
    }  
}