/*因为是三数之和,所以当
1、“数组为空或者长度<3”直接返回,当满足这个条件我们再去下一步,因为找三树之和,我们可以先对
2、“数组排序”
3、最后“遍历,考虑的就是去重”的问题,因为我们将用一个for循环,[排序完了(优化:可以判断一下每次循环的第一个值是否大于0,如果大于0就没必要继续下去)],然后遍历数组,也就是三个数中的第一个为for循环中的数,然后第二个和第三个分别用指针L,R来表示,
(降重:因为排序了,所以如果有重复的一定是相邻,所以我们此时可以判断是否相等。如果相等,就移动指针))
时间复杂度O(n*n + nlogn)->O(n^2) 空间复杂度O(1)
*/
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> threeSums = new ArrayList<List<Integer>>();
//1、“数组为空或者长度<3”直接返回,当满足这个条件我们再去下一步,因为找三树之和,我们可以先对
int len = nums.length;
if(nums==null||len<3){
return threeSums;
}
//2、“数组排序”,排序完了(优化:可以判断一下第一个值是否大于0,如果大于0就没必要继续下去),
Arrays.sort(nums);
//3、(1)、最后“遍历,考虑的就是去重”的问题,因为我们将用一个for循环,[排序完了(优化:可以判断一下每次循环的第一个值是否大于0,如果大于0就没必要继续下去)]
for(int i =0 ; i< len ; i++){
//[排序完了(优化:可以判断一下每次循环的第一个值是否大于0,如果大于0就没必要继续下去)]
if(nums[i]>0){
break;
}
//进一步去重
if(i>0&&nums[i]==nums[i-1]){
continue;
}
// (2)、三个数中的第一个为for循环中的数,然后第二个和第三个分别用指针L,R来表示,(降重:因为排序了,所以如果有重复的一定是相邻,所以我们此时可以判断是否相等。如果相等,就移动指针))
int L = i+1;
int R = len-1;
while(L<R){
int sum = nums[i] + nums[L] + nums[R];
if(sum==0){
threeSums.add(Arrays.asList(nums[i],nums[L],nums[R]));
//去重,因为我们考虑的是L<R,则去重就是进一步改变L,R
while(L<R && nums[L]==nums[L+1]){
L++;
}
while(L<R && nums[R]==nums[R-1]){
R--;
}
L++;
R--;
}
else if(sum <0){
//小于0了,我们将最小值增大
L++;
}else if(sum>0){
//大于0了,我们要将最大值减小
R--;
}
}
}
return threeSums;
}
}
/**
* 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; }
* }
*/
// 两个两个链表合并 时间复杂度O(n*k^2) 空间复杂度O(1)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
//先写边界,数组链表长度为>=0
int len = lists.length;
if(len == 0)
return null;
//两两链表进行合并
ListNode ans = null;
for(int i=0 ; i<len ; i++){
ans = mergeTwoList(ans,lists[i]);
}
return ans;
}
private ListNode mergeTwoList(ListNode l1 , ListNode l2){
if(l1==null || l2==null)
return l1==null ? l2 : l1;
//往下都不为空
if(l1.val > l2.val){
l2.next = mergeTwoList(l2.next , l1);
return l2;
}else{
l1.next = mergeTwoList(l1.next , l2);
return l1;
}
}
}
//时间复杂度O() 空间复杂度O(1)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return merge(lists , 0 , lists.length-1);
}
private ListNode merge(ListNode[] lists , int l , int r){
if(l == r)
return lists[l];
if(l > r)
return null;
int mid = (l + r)>>1;
return mergeTwoList(merge(lists,l,mid) , merge(lists,mid+1,r));
}
private ListNode mergeTwoList(ListNode l1 , ListNode l2){
if(l1==null || l2==null){
return l1==null ? l2 : l1;
}
if(l1.val > l2.val){
l2.next = mergeTwoList(l2.next , l1);
return l2;
}else{
l1.next = mergeTwoList(l1.next , l2);
return l1;
}
}
}
//动态规划:每一个位置能盛放的雨水的高度为:当前节点的左右两边比当前结点值大的高度最小值-当前节点的高度
//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public int trap(int[] height) {
if(height.length == 0){
return 0;
}
//先求做数组和有数组
int len = height.length;
int [] leftMax = new int[len];
int [] rightMax = new int[len];
leftMax[0] = height[0];
rightMax[len-1] = height[len-1];
for(int i=1 ; i<len ; i++)
leftMax[i] = Math.max(leftMax[i-1] , height[i]);
for(int i=len-2 ; i>=0 ; i--)
rightMax[i] = Math.max(rightMax[i+1] , height[i]);
int nums = 0;
for(int i=0 ; i<len ; i++)
nums += Math.min(leftMax[i] , rightMax[i]) - height[i];
return nums;
}
}
//动态规划时间复杂度,空间复杂度都是O(n),O(n),怎么减小空间复杂度呢,有必要用两个数组么,我们用一个左右指针来表示
//左右指针:时间复杂度O(n) , 空间复杂度O(1)
class Solution {
public int trap(int[] height) {
if(height.length == 0){
return 0;
}
int len = height.length;
int left = 0 , right = len-1;
int nums = 0;
int leftMax = 0 , rightMax = 0;
while(left<right){
leftMax = Math.max(leftMax , height[left]);
rightMax = Math.max(rightMax , height[right]);
if(leftMax < rightMax){
nums += leftMax - height[left];
left++;
}else{
nums+= rightMax - height[right];
right--;
}
}
return nums;
}
}
//也可以通过栈来做(一步一步填平))
//时间复杂度O(n)、空间复杂度O(n)
class Solution {
public int trap(int[] height) {
if(height.length == 0){
return 0;
}
int len = height.length;
Deque<Integer> stack = new LinkedList<Integer>();
int nums = 0;
for(int i=0 ; i<len ; i++){
while(!stack.isEmpty() && height[i] > height[stack.peek()]){//此时就完成了一个凹槽可以盛放水
int top = stack.pop(); //取出栈顶端的值
if(stack.isEmpty()){ //为空,说明不够成凹槽了,就没必要继续下去了
break;
}
int left = stack.peek();
int curWidth = i-1-left;
int curHeight = Math.min(height[left] , height[i]) - height[top];
nums += curHeight * curWidth;
}
stack.push(i);
}
return nums;
}
}