LeetCode困难题(一)
题目一:
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
方法一:
1、这是一个给定一个反转数链表反转,先实现整个链表反转;
2、设置pre=null,curr=head, while(curr!=null) 定义一个新的节点保证当前节点的下一个节点 ListNode temp=curr.next;
3、curr.next=pre将当前节点指针往前指,然后将当前节点赋值给pre,curr=temp,最后返回pre,就是反转后的链表;
4、下面实现多个链表的反转,先设置一个哑结点,ListNode dummy=new ListNode(0);
5、ListNode dummy.next=head,ListNode pre=dummy,end=dummy;
6、开始通过判断end进行循环,while(end!=null) for(int i=0;i<k&&end!=null) end=end.next;
7、如果end==null表示剩下的节点小于阈值k,直接跳出循环break;
8、定义一个next节点存放end的最后指向的位置 ListNode=end.next,在定义一个开始节点ListNode start=pre.next;
9、end.next断开链表,end.next=null;
10、pre.next=reverse(start)实现链表的反转,将start放置到最后;
11、将start指向之前end存放的节点,start.next=next;
12、pre节点和end节点都赋值给start;
13、返回哑结点dummy.next;
具体代码:
class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode dummy=new ListNode(0); dummy.next=head; ListNode pre=dummy; ListNode end=dummy; while(end!=null) { for(int i=0;i<k&&end!=null;i++) end=end.next; if(end==null) break; ListNode next=end.next; ListNode start=pre.next; end.next=null; pre.next=reverse(start); start.next=next; pre=start; end=start; } return dummy.next; } public ListNode reverse(ListNode head) { ListNode pre=null; ListNode curr=head; while(curr!=null) { ListNode temp=curr.next; curr.next=pre; pre=curr; curr=temp; } return pre; } }
题目二:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
方法一:
1、对每一列进行遍历,求出每一列的接水量;
2、整体的思路是,对于每一列求出其左边最大值和右边最大值,再求其最小值,将这个最小值和这个列进行比较;
3、如果最小值>列长度,则min-nums[i],否则接水量为0;
具体代码:
class Solution { public int trap(int[] height) { int sum=0; for(int i=1;i<height.length-1;i++) { int left_max=0; for(int j=i-1;j>=0;j--) { if(height[j]>left_max) left_max=height[j]; } int right_max=0; for(int j=i+1;j<height.length-1;j++) { if(height[j]>right_max) right_max=height[j]; } int min=Math.min(left_max, right_max); if(height[i]<min) sum=sum+(min-height[i]); } return sum; } }
方法二:
1、对于上述的方法使用了二重循环时间复杂度较高,每次都需要确定某一值的左边最大值和右边最大值;
2、我们想要减少上面的重复计算,用动态规划将数值进行存储;
3、动态方程:max_left[i]=Math.max(max_left[i-1],height[i-1]);
4、对于右采用同样的方式,需要求其最小值,直接进行调用,int min=Math.min(max_left[i],max_right[i]);
5、思路和方法一基本相同,只是用空间换取时间;
具体代码:
class Solution { public int trap(int[] height) { int sum=0; int max_left[]=new int[height.length]; int max_right[]=new int[height.length]; for(int i=1;i<height.length-1;i++) max_left[i]=Math.max(max_left[i-1], height[i-1]); for(int i=height.length-2;i>=0;i--) max_right[i]=Math.max(max_right[i+1], height[i+1]); for(int i=1;i<height.length-1;i++) { int min=Math.min(max_left[i], max_right[i]); if(height[i]<min) sum=sum+(min-height[i]); } return sum; } }
方法三:
1、双指针
具体代码:
public int trap(int[] height) { int sum = 0; int max_left = 0; int max_right = 0; int left = 1; int right = height.length - 2; // 加右指针进去 for (int i = 1; i < height.length - 1; i++) { //从左到右更 if (height[left - 1] < height[right + 1]) { max_left = Math.max(max_left, height[left - 1]); int min = max_left; if (min > height[left]) { sum = sum + (min - height[left]); } left++; //从右到左更 } else { max_right = Math.max(max_right, height[right + 1]); int min = max_right; if (min > height[right]) { sum = sum + (min - height[right]); } right--; } } return sum; }
题目三:
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
方法:
1、这道题的思路不是很容易能够想到,首先将数组中所有的小于等于0和大于n的数赋值为1;
2、然后将其进行转换变为负数,这个算法最精彩的部分是将转换后的数组的元素作为另一个元素的下标,并且将其赋值为绝对值的负值;
3、这样实现了数组中每一个前n个元素,出现过就将其设定为负值;
4、最后遍历循环,直到出现大于0的数就是我们要求的值,将其下标返回;
5、当然如果开始数组没有1,那么返回1为结果;
具体代码:
class Solution { public int firstMissingPositive(int[] nums) { int n=nums.length; int contain=0; for(int i=0;i<nums.length;i++) { if(nums[i]==1) { contain++; break; } } if(contain==0) return 1; if(n==1) return 2; for(int i=0;i<n;i++) { if(nums[i]<=0||nums[i]>n) { nums[i]=1; } } for(int i=0;i<n;i++) { int a=Math.abs(nums[i]); if(a==n) nums[0]=-Math.abs(nums[0]); else { nums[a]=-Math.abs(nums[a]); } } for(int i=1;i<n;i++) { if(nums[i]>0) return i; } if(nums[0]>0) return n; return n+1; } }
题目四:
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
方法一:
1、使用递归,可能不是很容易想到,模式字符串中有' .'和‘ *’,不知道应该如何下手;
2、其实着手点就在于*号,*号出现位置至少应该在第二位,就需要先判断第一个字符是否相同;
3、boolean first_match=s.charAt(0)==p.charAt(0)||p.charAt(0)==' .';
4、如果模式串的长度大于2并且第二位是*,就可以进行递归实现,return isMatch(s,p.substring(2))||first_match&&isMacth(s.substring(1),p);
5、否则,都向后移动一位;
具体代码:
class Solution { public boolean isMatch(String s, String p) { if(p.isEmpty()) return s.isEmpty(); boolean first_match=(!s.isEmpty())&&(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.'); if(p.length()>=2&&p.charAt(1)=='*') { return (isMatch(s, p.substring(2)))||(first_match&&isMatch(s.substring(1), p)); } else { return first_match&&isMatch(s.substring(1), p.substring(1)); } } }