2021-05-25 LeetCode “阿里巴巴企业题库” -------15. 三数之和 23. 合并K个升序链表 42. 接雨水

在这里插入图片描述
在这里插入图片描述

/*因为是三数之和,所以当
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;   
    }    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的,我来用中文回复这个链:https://leetcode-cn.com/tag/dynamic-programming/ 这个链LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值