leetCode热题40-45 解题代码,调试代码和思路

Posted 黑白极客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetCode热题40-45 解题代码,调试代码和思路相关的知识,希望对你有一定的参考价值。

前言

本文属于特定的六道题目题解和调试代码。

1 ✔ [70]爬楼梯 Easy 2023-03-24 107
2 ✔ [148]排序链表 Medium 2022-12-13 106
3 ✔ [82]删除排序链表中的重复元素 II Medium 2023-03-18 105
4 ✔ [31]下一个排列 Medium 2023-02-15 105
5 ✔ [1143]最长公共子序列 Medium 2023-03-23 104
6 ✔ [2]两数相加 Medium 2023-03-22 102

正所谓磨刀不误砍柴功。下面我做这几篇文档对于涉及的题型或者数据结构的分析都很有帮助,贴出来仅供参考。

如何调试递归程序,有何技巧?

按照树形结构直观地打印出一棵二叉树、快速创建leetcode中树的结构(Java)


leetCode热题40-45 解题代码,调试代码和思路

1 ✔ [70]爬楼梯 Easy 2023-03-24 107

//假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
//
// 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
//
//
//
// 示例 1:
//
//
//输入:n = 2
//输出:2
//解释:有两种方法可以爬到楼顶。
//1. 1 阶 + 1 阶
//2. 2 阶
//
// 示例 2:
//
//
//输入:n = 3
//输出:3
//解释:有三种方法可以爬到楼顶。
//1. 1 阶 + 1 阶 + 1 阶
//2. 1 阶 + 2 阶
//3. 2 阶 + 1 阶
//
//
//
//
// 提示:
//
//
// 1 <= n <= 45
//
//
// Related Topics 记忆化搜索 数学 动态规划 👍 2945 👎 0


自测代码

public class P70_ClimbingStairs
	 public static void main(String[] args) 
	 	 //测试代码
	 	 Solution solution = new P70_ClimbingStairs().new Solution();
		 System.out.println(solution.climbStairs(4));
	 
	 
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
class Solution 
    public int climbStairs(int n) 

		if (0 >= n) 
			return 0;
		

		if (1 == n) 
			return 1;
		

		if (2 == n) 
			return 2;
		

		int l = 1;
		int r = 2;
		int temp = l+r;
		for (int i = 3; i < n; i++) 
			temp = l+r;
			l = r;
			r = temp;
		

		return l+r;
    

//leetcode submit region end(Prohibit modification and deletion)




提交代码

class Solution 
    public int climbStairs(int n) 

		if (0 >= n) 
			return 0;
		

		if (1 == n) 
			return 1;
		

		if (2 == n) 
			return 2;
		

		int l = 1;
		int r = 2;
		int temp = l+r;
		for (int i = 3; i < n; i++) 
			temp = l+r;
			l = r;
			r = temp;
		

		return l+r;
    

2 ✔ [148]排序链表 Medium 2022-12-13 106

//给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
//
//
//
//
//
//
// 示例 1:
//
//
//输入:head = [4,2,1,3]
//输出:[1,2,3,4]
//
//
// 示例 2:
//
//
//输入:head = [-1,5,3,4,0]
//输出:[-1,0,3,4,5]
//
//
// 示例 3:
//
//
//输入:head = []
//输出:[]
//
//
//
//
// 提示:
//
//
// 链表中节点的数目在范围 [0, 5 * 10⁴] 内
// -10⁵ <= Node.val <= 10⁵
//
//
//
//
// 进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
//
// Related Topics 链表 双指针 分治 排序 归并排序 👍 1952 👎 0


有去写了一种方式:就是从头开始向后一次一个添加进来排序,但是显示超时了。

疑惑点:如果使用归并排序,分治,应该需要知道中位数,但是遍历的时间不就是n了?之后再回溯 合并两个链表时,时间应该也是n

解决方案:这道题目采用了一种非常新颖的方式,首先是通过位运算 将需要排序的子链表长度控制在 1 2 4 8… ,第一遍是将所有相邻的两个排序,然后连接,然后将相邻的四个排序连接。最后得出所有的排序连接。
需要注意的就是整个过程中关键节点的赋值。
链表的解题思路,就是找关键节点,可以先声明出来, 再就是双指针。
先新建一个dummyHead 做链表开头。
然后新建一个 pre 做需要排序的前半部分的最后一个节点
再新建一个cuur 做遍历节点。
head1,head2做要排序的两个链表的的头节点

自测代码

public class P148_SortList
	 public static void main(String[] args) 
	 	 //测试代码
	 	 Solution solution = new P148_SortList().new Solution();
		 /*想到的解决方法:遍历两个指针,l指针及之前的都是排序完成的,r为要插入的指针*/
		 /*如果使用归并排序,分治,应该需要知道中位数,但是遍历的时间不就是n了?之后再回溯合并两个链表时,时间应该也是n*/
		 /*解决方案:就是上面疑惑的解决方法*/
		 ListNode listNode1 = new ListNode("[-1,5,3,4,0]");
		 ListNode listNode = solution.sortList(listNode1);
		 System.out.println(listNode.toString());
	 
	 
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for singly-linked list.
 * public class ListNode 
 *     int val;
 *     ListNode next;
 *     ListNode() 
 *     ListNode(int val)  this.val = val; 
 *     ListNode(int val, ListNode next)  this.val = val; this.next = next; 
 * 
 */
class Solution 
	public ListNode sortList(ListNode head) 
		if (head == null) 
			return null;
		

		int len = 0 ;
		ListNode sum = head;
		while (sum != null) 
			len++;
			sum = sum.next;
		

		ListNode dummyHead = new ListNode(0, head);

		for (int i = 1; i < len ; i <<= 1) 
			ListNode pre = dummyHead,curr = dummyHead.next;

			while (curr != null)

				ListNode head1 = curr;

				for (int i1 = 1; i1 < i && curr.next != null; i1++) 
					curr = curr.next;
				

				ListNode head2 = curr.next;
				curr.next = null;

				curr = head2;

				for (int i1 = 1; i1 < i && curr != null && curr.next != null; i1++) 
					curr  = curr.next;
				

				ListNode next = null;
				if (curr != null) 
					next = curr.next;
					curr.next = null;
				
				ListNode merge = merge(head1, head2);

				pre.next = merge;
				while (pre.next != null) 
					pre = pre.next;
				
				curr = next;
			

		

		return dummyHead.next;
	

	public ListNode merge(ListNode head1,ListNode head2)
		ListNode dummyHead = new ListNode(0);
		ListNode listNode = dummyHead;
		while (head1 != null && head2 != null) 

			if (head1.val > head2.val) 
				listNode.next = head2;
				head2 = head2.next;
				listNode = listNode.next;
			else 
				listNode.next = head1;
				head1 = head1.next;
				listNode = listNode.next;
			
		

		if (head1 == null) 
			listNode.next = head2;
		else 
			listNode.next = head1;
		

		return dummyHead.next;
	




//leetcode submit region end(Prohibit modification and deletion)




提交代码

class Solution 
	public ListNode sortList(ListNode head) 
		if (head == null) 
			return null;
		

		int len = 0 ;
		ListNode sum = head;
		while (sum != null) 
			len++;
			sum = sum.next;
		

		ListNode dummyHead = new ListNode(0, head);

		for (int i = 1; i < len ; i <<= 1) 
			ListNode pre = dummyHead,curr = dummyHead.next;

			while (curr != null)

				ListNode head1 = curr;

				for (int i1 = 1; i1 < i && curr.next != null; i1++) 
					curr = curr.next;
				

				ListNode head2 = curr.next;
				curr.next = null;

				curr = head2;

				for (int i1 = 1; i1 < i && curr != null && curr.next != null; i1++) 
					curr  = curr.next;
				

				ListNode next = null;
				if (curr != null) 
					next = curr.next;
					curr.next = null;
				
				ListNode merge = merge(head1, head2);

				pre.next = merge;
				while (pre.next != null) 
					pre = pre.next;
				
				curr = next;
			

		

		return dummyHead.next;
	

	public ListNode merge(ListNode head1,ListNode head2)
		ListNode dummyHead = new ListNode(0);
		ListNode listNode = dummyHead;
		while (head1 != null && head2 != null) 

			if (head1.val > head2.val) 
				listNode.next = head2;
				head2 = head2.next;
				listNode = listNode.next;
			else 
				listNode.next = head1;
				head1 = head1.next;
				listNode = listNode.next;
			
		

		if (head1 == null) 
			listNode.next = head2;
		else 
			listNode.next = head1;
		

		return dummyHead.next;
	



3 ✔ [82]删除排序链表中的重复元素 II Medium 2023-03-18 105

//给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
//
//
//
// 示例 1:
//
//
//输入:head = [1,2,3,3,4,4,5]
//输出:[1,2,5]
//
//
// 示例 2:
//
//
//输入:head = [1,1,1,2,3]
//输出:[2,3]
//
//
//
//
// 提示:
//
//
// 链表中节点数目在范围 [0, 300] 内
// -100 <= Node.val <= 100
// 题目数据保证链表已经按升序 排列
//
//
// Related Topics 链表 双指针 👍 1090 👎 0


这道题目相对于上道题目我觉得难度不大,把上道题目中的找关键节点搞懂,我觉得这道题目就很好解决。

自测代码

public class P82_RemoveDuplicatesFromSortedListIi
	 public static void main(String[] args) 
	 	 //测试代码
	 	 Solution solution = new P82_RemoveDuplicatesFromSortedListIi().new Solution();
		 System.out.println(solution.deleteDuplicates(new ListNode("[1,1]")).toString());
	 
	 
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for singly-linked list.
 * public class ListNode 
 *     int val;
 *     ListNode next;
 *     ListNode() 
 *     ListNode(int val)  this.val = val; 
 *     ListNode(int val, ListNode next)  this.val = val; this.next = next; 
 * 
 */
class Solution 
    public ListNode deleteDuplicates(ListNode head) 

		ListNode dummyHead = new ListNode(0, head);
		ListNode pre = dummyHead;
		ListNode cuur = dummyHead.next;
		while (cuur != null &&  cuur.next != null ) 

			if (cuur.val == cuur.next.val) 

				while (cuur != null &&  cuur.next != null && cuur.val == cuur.next.val) 
					cuur.next = cuur.next.next;
				

				cuur = cuur.next;
				pre.next = cuur;
			else 
				pre = pre.next;
				cuur = cuur.next;
			

		

	return dummyHead.next;
	

//leetcode submit region end(Prohibit modification and deletion)




提交代码

class Solution 
    public ListNode deleteDuplicates(ListNode head) 

		ListNode dummyHead = new ListNode(0, head);
		ListNode pre = dummyHead;
		ListNode cuur = dummyHead.next;
		while (cuur != null &&  cuur.next != null ) 

			if (cuur.val == cuur.next.val) 

				while (cuur != null &&  cuur.next != null && cuur.val == cuur.next.val) 
					cuur.next = cuur.next.next;
				

				cuur = cuur.next;
				pre.next = cuur;
			else 
				pre = pre.next;
				cuur = cuur.next;
			

		

	return dummyHead.next;
	

4 ✔ [31]下一个排列 Medium 2023-02-15 105

//整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
//
//
// 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
//
//
// 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就
//是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
//
//
// 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
// 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
// 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
//
//
// 给你一个整数数组 nums ,找出 nums 的下一个排列。
//
// 必须 原地 修改,只允许使用额外常数空间。
//
//
//
// 示例 1:
//
//
//输入:nums = [1,2,3]
//输出:[1,3,2]
//
//
// 示例 2:
//
//
//输入:nums = [3,2,1]
//输出:[1,2,3]
//
//
// 示例 3:
//
//
//输入:nums = [1,1,5]
//输出:[1,5,1]
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 100
// 0 <= nums[i] <= 100
//
//
// Related Topics 数组 双指针 👍 2124 👎 0


疑惑点:题没有读懂,字典排序如果都是数字,类似于看组合的数字大小。
分析题目发现,就是找到最后一个数字 和从后往前第一个比它小的数字交换,然后对中间的数字进行排序。

自测代码

public class P31_NextPermutation
	 public static void main(String[] args) 
	 	 //测试代码
	 	 Solution solution = new P31_NextPermutation().new Solution();
		  /*疑惑点:题没有读懂,字典排序如果都是数字,类似于看组合的数字大小。
		  分析题目发现,就是找到最后一个数字 和从后往前第一个比它小的数字交换,然后对中间的数字进行排序。*/

	 
	 
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
class Solution 
    public void nextPermutation(int[] nums) 
		int len = nums.length;
		int r = len -1;

		for (int i = len-1; i >= 0; i--) 
			for (int r1 = r; r1 >= 0 && r1 > i; r1--) 

				if (nums[i] <nums[r1]) 
					int temp = nums[i];
					nums[i] = nums[r1];
					nums[r1] = temp;

					Arrays.sort(nums,i+1,len );
return;

				

			

		
		Arrays.sort(nums);

	

//leetcode submit region end(Prohibit modification and deletion)




提交代码

class Solution 
    public void nextPermutation(int[] nums) 
		int len = nums.length;
		int r = len -1;

		for (int i = len-1; i >= 0; i--) 
			for (int r1 = r; r1 >= 0 && r1 > i; r1--) 

				if (nums[i] <nums[r1]) 
					int temp = nums[i];
					nums[i] = nums[r1];
					nums[r1] = temp;

					Arrays.sort(nums,i+1,len );
return;

				

			

		
		Arrays.sort(nums);

	

5 ✔ [1143]最长公共子序列 Medium 2023-03-23 104

//给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
//
// 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
//
//
// 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
//
//
// 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
//
//
//
// 示例 1:
//
//
//输入:text1 = “abcde”, text2 = “ace”
//输出:3
//解释:最长公共子序列是 “ace” ,它的长度为 3 。
//
//
// 示例 2:
//
//
//输入:text1 = “abc”, text2 = “abc”
//输出:3
//解释:最长公共子序列是

leetcode热题Hot100——LRU缓存

  • 💂 个人主页:努力学习的少年
  • 🤟 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦

目录

一. 题目

1.题目描述

2.基础框架

3.原题链接

二.解题报告

代码详解


一. 题目

1.题目描述

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行

样例输入

["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

样例输出

[null, null, null, 1, null, -1, null, -1, 3, 4]

2.基础框架

​

class LRUCache 
public:
    LRUCache(int capacity) 

    
    
    int get(int key) 

    
    
    void put(int key, int value) 

    
;


​

3.原题链接

LRU缓存

二.解题报告

思路:

该结构我们将设置为 双向链表+哈希表 的结构。

哈希表存储的 key-节点的地址,方便找到对应的节点。

双向链表中的节点存储的是 key-value 的值,该双向链表包含了 head节点 tail节点,head节点指向的是最近被使用过的节点(新插入,修改,返回值),tail节点指向的是最久未使用的节点。

void put(int key,int value)实现;

  • 如果key值在哈希表中找不到,则说明该key值的节点不存在,因此 我们需要将key值节点插入到head的后面

如下:新插入4-4的节点,通过哈希表发现,该key值不存在,说明key=4的节点不存在。

  • 如果key值在哈希表总找到了,则说明该key值的节点存在,因此我们去修改节点的value,并将修改的节点链接到后面,代表该节点最近被使用过。

如下:put(3,4);因为key=3节点已经存在,所以我们需要该节点的value进行修改。

首先通过哈希表找到该节点,修改后再将该节点放在head的后面,表示该节点最近被使用过。

 

  • 如果插入的节点的数量大于capacity,我们需要删除链表最后一个节点,也就是tail节点前面的节点,在将新节点插入到head节点的后面。

假设LRU的capacity为3,那么如果我们在插入一个key-value值为4,4,我们需要删除tail最后一个节点,因为它是最久没有被使用过的节点,然后将删除的节点在哈希表的映射关系。再将key=4的节点插入到head后面,如下

int get(value)函数

  • 如果key值在哈希表中找不到,直接返回-1。
  • 如果找到了,那么找到相对应的节点,并将该节点连接到head的后面,代表该节点最近是被使用过,然后再返回它的value值。

如下:查找key的值为2;

 我们将1节点和3节点互相连接,然后再将head与2节点互相连接,连接的时间复杂度为O(1).

总结:由于key-节点的地址存储在哈希表中,因此查找节点的时间复杂度是O(1),节点连接的过程也是O(1),删除节点是直接将tail前面的节点删除,因此时间复杂度也为O(1),因此,整个get()和put()的时间复杂度都是O(1).

代码详解

//设置Node节点类
class Node
 public:
    int _key;
    int _value;
    Node* prev=nullptr;
    Node* next=nullptr;
public:
    Node(int key=0,int value=0):_key(key),_value(value)
;
class LRUCache 
public:
    LRUCache(int capacity) 
        _capacity=capacity;
        head=new Node();
        tail=new Node();
        head->next=tail;
        tail->prev=head;
    
    
    int get(int key) 
        //该key值没有在LRU缓存中
        if(mp.count(key)==0)
            return -1;
        
        //该key值在LRU缓存中
        //找到该节点,连接前后节点
            Node* cur=mp[key]; 
            if(head->next==cur)
            return cur->_value;

            Node* next=cur->next;
            Node* prev=cur->prev;
            Node* headNext=head->next;


            next->prev=prev;
            prev->next=next;

            //将该节点与head互相连接
            head->next=cur;
            cur->prev=head;
             
            cur->next=headNext;
            headNext->prev=cur;
            return cur->_value;
        
    
    
    void put(int key, int value) 
        if(mp.count(key))
            //该节点存在,修改节点的value值
            Node* cur=mp[key];

            cur->_value=value;
            if(head->next==cur)
            return;
            Node* prev=cur->prev;
            Node* next=cur->next;
            Node* headNext=head->next;

            prev->next=next;
            next->prev=prev;

            head->next=cur;
            cur->prev=head;

            cur->next=headNext;
            headNext->prev=cur;
            return;
        else
            //不存在         
            if(size==_capacity)
                //如果LRU缓存满了,删除最后一个节点
                Node* prev=tail->prev;
                Node* pprev=prev->prev;
                pprev->next=tail;

                tail->prev=pprev;
                mp.erase(prev->_key);
                delete prev;
                prev=nullptr;
                size--;
            
            //创建新的节点,并将它连接在head的后面
            Node* cur=new Node(key,value);
            mp[key]=cur;

            Node* headNext=head->next;
            cur->prev=head;
            head->next=cur;

            cur->next=headNext;
            headNext->prev=cur;
            size++;
        
    
private:
    Node* head;//双向链表的头节点
    Node* tail;//双向链表的末尾节点
    unordered_map<int,Node*> mp;//哈希表
    int _capacity;//LRU的容量
    int size=0;//LRU中存在节点的个数
;

以上是关于leetCode热题40-45 解题代码,调试代码和思路的主要内容,如果未能解决你的问题,请参考以下文章

leetcode热题Hot100——LRU缓存

leetcode热题Hot100——LRU缓存

LeetCode 热题100 1.两数之和 解题思路 哈希表知识点回顾(JavaScript解题 5种方法)

LeetCode 热题 HOT 100 完整题解笔记&知识点分类 C++代码实现

Leetcode算法热题 --- 数组篇丢失的数字

LeetCode热题通过任意两种遍历序列构造二叉树附代码,超详细,建议收藏