zoukankan      html  css  js  c++  java
  • LeetCode中等题(二)

    题目一:

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

    满足要求的三元组集合为:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]

    分析:这是一道三元组求和问题

    1、首先定义返回的元组类型,List<List<Integer>> ans=new ArrayList<>()

    2、对刚开始的数组进行排序,Arrays.sort(nums);

    3、通过一个指针遍历整个数组for(int i=0;i<num.lenght;i++)

    4、再设置两个指针,分别是l=i+1,r=len-1

    5、接下来开始求和进行判断;if(nums[i]+nums[l]+nums[r]==0)将l++,r--;

    6、当然应该存入到ans.add(Arrays.asList(nums[i],nums[l],nums[r]))

    7、在这个过程可能产生重复,if(nums[i]>0) break,if(i>&&nums[i]==nums[i-1]) continue;while(nums[l]==nums[l+1]) l++; r同样;

    8、如果和不同sum<0,l++;sum>0,r--;

    9、返回ans;

    具体代码:

    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            
            List<List<Integer>> ans=new ArrayList<>();
            int len=nums.length;        
            if(nums==null||len<3) return ans;
            
            Arrays.sort(nums);
            
            for(int i=0;i<len;i++)
            {
                int l=i+1,r=len-1;
                if(nums[i]>0) break;
                if(i>0&&nums[i]==nums[i-1]) continue;
                while(l<r)
                {
                    int sum=nums[i]+nums[l]+nums[r];
                    if(sum==0)
                    {
                        ans.add(Arrays.asList(nums[i],nums[l],nums[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) l++;
                    else if(sum>0) r--;                    
                }
            }        
            return ans;
        }        
    }

    题目二:

    给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

    例如,给出 n = 3,生成结果为:

    [
    "((()))",
    "(()())",
    "(())()",
    "()(())",
    "()()()"
    ]

    方法一:

    1、首先产生所有括号的情况;

    2、采用递归算法,先定义一个函数实现所有括号的情况,其等于‘(’+n-1的序列和‘)’+n-1的序列和

    3、进行判断所产生的序列是否有效,设置一个balance=0,if(c=='(') balance++;否则balance--;

    4、如果balance<0 返回false;

    具体代码:

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> result=new ArrayList();
            
            GenerateAll(new char[2*n],0,result);
            return result;       
        }
        
        public void GenerateAll(char[] current,int pos,List<String> res)
        {
            if(current.length==pos)
            {
                if(Valid(current))
                {
                    res.add(new String(current));
                }
            }
            else {
                current[pos]='(';
                GenerateAll(current,pos+1,res);
                current[pos]=')';
                GenerateAll(current,pos+1,res);
            }
        }
        
        public boolean Valid(char[] current)
        {
            int balance=0;
            for(char c:current)
            {
                if(c=='(') balance++;
                else {
                    balance--;
                }
                
                if(balance<0) return false;
            }
            
            return (balance==0);
        }
    }

    题目三:

    给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

    输入: [1,8,6,2,5,4,8,3,7]
    输出: 49

    方法一:

    1、采用暴力法,双循环遍历出所有的情况;

    2、进行逐个比较取最大值,计算公式为数组最小值和两个数组值之间的距离进行相乘

    具体代码:

    class Solution {
        public int maxArea(int[] height) {
            
            int ans=0;
            int result=0;
            int minVal=0;
            for(int i=0;i<height.length;i++)
                for(int j=i+1;j<height.length;j++)
                {
                    minVal=Math.min(height[i], height[j]);
                    result=minVal*(j-i);
                    ans=Math.max(ans, result);
                }
            
            return ans;
        }
    }

    方法二:

    1、使用双指针一个数组第一位开始另一个从数组的最后一位开始

    2、保持数组最大值不动,移动最小的值

    具体代码:

    class Solution {
        public int maxArea(int[] height) {
            
            int p=0,q=height.length-1;
            int ans=0;
            int minVal=0;
            int result=0;
            
            while(p<q)
            {
                int distance=q-p;
                
                minVal=Math.min(height[p], height[q])
                if(height[p]<height[q])
                {
                    minVal=height[p];
                    p++;
                }
                else {
                    minVal=height[q];
                    q--;
                }
                result=minVal*distance;
                ans=Math.max(ans, result);
            }        
            return ans;
        }
    }

    题目四:

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

    示例:

    给定 1->2->3->4, 你应该返回 2->1->4->3.

    方法一:

    1、递归实现,如果是两两反转递归应该是swapPairse(next.next);

    2、head.next=swapPairse(next.next),next.next=head

    3、返回next:

    4、递归一定要有递归出口,递归出口,递归出口,if(head==null||head.next==null) return head

    具体代码:

    class Solution {
        public ListNode swapPairs(ListNode head) {
            
            if(head==null||head.next==null)
                return head;
            
            ListNode next=head.next;
            head.next=swapPairs(next.next);
            next.next=head;
            
            return next;
        }
    }

    方法二:

    非递归算法

    具体代码:

    class Solution {
        public ListNode swapPairs(ListNode head) {
            
            ListNode dummy=new ListNode(0);
            dummy.next=head;
            
            ListNode curr=dummy;
            
            while(curr.next!=null&&curr.next.next!=null)
            {
                ListNode start=curr.next;
                ListNode end=curr.next.next;
                curr.next=end;
                start.next=end.next;
                end.next=start;
                curr=start;
            }
            return dummy.next;
        }
    }

    题目五:

    在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

    示例 1:

    输入: [3,2,1,5,6,4] 和 k = 2
    输出: 5

    方法一:

    1、现对原始数组排序,Arrays.sort(nums);

    2、返回return nums[nums.leght-k],这种方式较为简单;

    方法二:

    1、使用大顶堆获取最大的k个数

    2、java中使用优先队列PriorityQueue类定义了最小堆的实现,PriorityQueue<Integer> heap=new PriorityQueue<>(),通过这个函数得到的是最小堆;

    3、对数组元素进行遍历,for(i:nums)  heap.add(i),值得注意的是当一个元素加入到堆当中,系统自动将最小的元素放到堆顶排序好

    4、if(heap.size()>k) heap.poll();将堆顶元素删除,并且又最后一个元素覆盖,再次进行建立最小堆;

    5、最后得到k个元素的最小堆,因为本地要输出第k个大小的值,刚好输出堆顶元素

    具体代码

    class Solution {
        public int findKthLargest(int[] nums, int k) {
            
            PriorityQueue<Integer> pQueue=new PriorityQueue<>();        
            for(int i:nums)
            {
                pQueue.add(i);
                
                if(pQueue.size()>k)
                    pQueue.poll();
            }
            
            return pQueue.poll();
      }
    }

    题目六:

    给定一个无序数组,包含正数、负数和0,要求从中找出3个数的乘积,使得乘积最大,要求时间复杂度:O(n),空间复杂度:O(1)
     
    方法:
    1、这里关键是有正数,负数,0的区别;
    2、如果是三个正数直接求前三个最大值就是最后的结果;
    3、如果负数刚开始不太容易想到,求数组中最小的两个负数,再和最大值乘积就是最大值;
    4、如何得到前三个正数,每次进行判断if(nums[i]>max1) max3=max2,max2=max1,max1=nums[i]采用这种循序渐进的方式求出;
    5、同样求最小的两个负数也类似;
    6、值得注意的是,本地如果是Int类型并不能通过,需要long类型
     
     

    具体代码

    public class Main{
            public static long MaxProduct(int n,long[] arr)
        {
            long max1=0,max2=0,max3=0,min1=0,min2=0;
            for(int i=0;i<n;i++)
            {
                if(arr[i]!=0)
                {
                    if(arr[i]>max1)
                    {
                        max3=max2;
                        max2=max1;
                        max1=arr[i];
                    }
                    else if(arr[i]>max2)
                    {
                        max3=max2;
                        max2=arr[i];
                    }
                    else if(arr[i]>max3) 
                    {
                        max3=arr[i];
                    }
                    else if(arr[i]<min1) 
                    {
                        min2=min1;
                        min1=arr[i];
                    }
                    else if (arr[i]<min2) {
                        min2=arr[i];
                    }
                }
                else {
                    continue;
                }
            }
            
            return Math.max(max1*max2*max3,max1*min1*min2);
        }
        
        public static void main(String[] arg)
        {
            Scanner input=new Scanner(System.in);
            int n=input.nextInt();
            long[] arr=new long[n];
            for(int i=0;i<n;i++)
            {
                arr[i]=input.nextLong();
            }
            
            System.out.println(MaxProduct(n,arr));
            
        }
    }

    题目七:

    有序数组进行查找目标元素

    1、最简单查找

    l=0,r=lengh-1,while(l<=r),左右值的变化都为l=mid+1,r=mid-1;

    具体代码:

    class Solution {
        public static void main(String[] args)
        {
            int[] nums= {1,2,3,4,5};
            int target=10;
            
            System.out.println(TwoDisv(nums,target));
        }
        
        public static int TwoDisv(int[] nums,int target)
        {
            int l=0,r=nums.length-1;
            
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(nums[mid]==target)
                    return mid;
                else if (nums[mid]<target) {
                    l=mid+1;
                }
                else if (nums[mid]>target) {
                    r=mid+1;
                }
            }        
            return -1;
        }
    }

    2、查找目标值左边界

    l=0,r=length,while(l<r),if(l<target) l=mid+1,if(r>target) r=mid;,因为这是一个左闭右开区间;

    最关键的点在if(nums[mid]==target) 并不是直接的返回,而是r=mid,逐步向左移动,直到最左边;

    具体代码:

    class Solution {
        public static void main(String[] args)
        {
            int[] nums= {1,2,3,3,5};
            int target=3;
            
            System.out.println(TwoDisv(nums,target));
        }
        
        public static int TwoDisv(int[] nums,int target)
        {
            int l=0,r=nums.length;
            
            while(l<r)
            {
                int mid=(l+r)/2;
                if(nums[mid]==target)
                    r=mid;
                else if (nums[mid]<target) {
                    l=mid+1;
                }
                else if (nums[mid]>target) {
                    r=mid;
                }
            }    
            return r;
        }
    }

    3、查找目标元素的右边界

    l=0,r=length,while(l<r) if(nums[mid]==target) l=mid-1,最后返回也需要修改return l-1;

    具体代码:

    class Solution {
        public static void main(String[] args)
        {
            int[] nums= {1,2,3,3,3};
            int target=3;
            
            System.out.println(TwoDisv(nums,target));
        }
        
        public static int TwoDisv(int[] nums,int target)
        {
            int l=0,r=nums.length;
            
            while(l<r)
            {
                int mid=(l+r)/2;
                if(nums[mid]==target)
                    l=mid+1;
                else if (nums[mid]<target) {
                    l=mid+1;
                }
                else if (nums[mid]>target) {
                    r=mid;
                }
            }    
            return r-1;
        }
    }

    4、得出左右集合代码:

    class Solution {
        public int[] searchRange(int[] nums, int target) {
                   
            return new int[]{left(nums,target),right(nums,target)};
        }
        
        public int left(int[] nums,int target)
        {
            int l=0,r=nums.length;
            
            while(l<r)
            {
                int mid=(l+r)/2;
                if(nums[mid]==target)
                {
                    r=mid;
                }
                else if (nums[mid]<target) {
                    l=mid+1;
                }
                else if (nums[mid]>target) {
                    r=mid;
                }
            }
            
            return r;
        }
        public int right(int[] nums,int target)
        {
            int l=0,r=nums.length;
            
            while(l<r)
            {
                int mid=(l+r)/2;
                if(nums[mid]==target)
                {
                    l=mid-1;
                }
                else if (nums[mid]<target) {
                    l=mid+1;
                }
                else if (nums[mid]>target) {
                    r=mid;
                }
            }
            
            return l-1;
        }
    }

    题目八:

    给定一个没有重复数字的序列,返回其所有可能的全排列。

    示例:

    输入: [1,2,3]
    输出:
    [
    [1,2,3],
    [1,3,2],
    [2,1,3],
    [2,3,1],
    [3,1,2],
    [3,2,1]
    ]

    方法一:

    1、整体思路是递归回溯进行求解,每次进行递归进行深度优先遍历,if(currSize==len) res.add(new ArrayList<>(path));

    2、需要设置一个是否遍历过的数值,if(!visied[i]) path.push(nums[i]);

    3、visied[i]=false,generePermuse(nums,currsize+1,res);

    4、这一步进行较为重要,将栈的最后一位去除,path.pop(),visied[i]=true;

    5、正是因为将数组最后一个元素删除,进而向上才能出现继续遍历其他可能的情况;

    具体代码:

    class Solution {
        public List<List<Integer>> permute(int[] nums) {
            
            List<List<Integer>> res=new ArrayList<>();
            int len=nums.length;
            boolean[] used=new boolean[len];
            
            GeregePremute(nums,used,0,len,new Stack<>(),res)
            return res;
        }
        
        public void GeregePremute(int[] nums,boolean[] visied,
                   int currSize,int len,Stack<Integer> path,List<List<Integer>> res)
        {
            if(currSize==len)
            {
                res.add(new ArrayList<>(path));
                return;
            }
            
            for(int i=0;i<nums.length;i++)
            {
                if(!visied[i])
                {
                    path.push(nums[i]);
                    visied[i]=true;
                    GeregePremute(nums,visied,currSize+1,len,path,res);
                    path.pop();
                    visied[i]=false;
                }
            }
        }  
    }

    题目九:

    给定一个 n × n 的二维矩阵表示一个图像。

    将图像顺时针旋转 90 度。

    说明:

    你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

    示例 1:

    给定 matrix =
    [
    [1,2,3],
    [4,5,6],
    [7,8,9]
    ],

    原地旋转输入矩阵,使其变为:
    [
    [7,4,1],
    [8,5,2],
    [9,6,3]
    ]

    方法一:

    1、先对矩阵进行转置

    2、对转置后的矩阵的每一行进行反转,最前和最后进行交换;

    具体代码:

    class Solution {
        public void rotate(int[][] matrix) {
            
            int len=matrix.length;
            
            for(int i=0;i<len;i++)
                for(int j=i;j<len;j++)
                {
                    int temp=matrix[j][i];
                    matrix[j][i]=matrix[i][j];
                    matrix[i][j]=temp;
                }
            
            for(int i=0;i<len;i++)
                for(int j=0;j<len/2;j++)
                {
                    int temp=matrix[i][j];
                    matrix[i][j]=matrix[i][len-j-1];
                    matrix[i][len-j-1]=temp;
                }
        }
    }

    方法二:

    1、矩阵旋转其实就是四个元素进行旋转,一直循环;

    2、难点就是找到这四个元素,比如某元素为(i,j),其他元素为(n-j-1,i),(n-i-1,n-j-1),(j,n-i-1)将这四个元素进行互换;

    3、这里的循环条件也不是很好理解,for(int i=0;i<n/2;i++) for(int j=j;j<n-j-1;j++)

    具体代码:

    class Solution {
        public void rotate(int[][] matrix) {
            
            int n=matrix.length;
            
            for(int i=0;i<n/2;i++)
                for(int j=i;j<n-i-1;j++)
                {
                    int temp=matrix[i][j];
                    matrix[i][j]=matrix[n-j-1][i];
                    matrix[n-j-1][i]=matrix[n-i-1][n-j-1];
                    matrix[n-i-1][n-j-1]=matrix[j][n-i-1];
                    matrix[j][n-i-1]=temp;
                }
        }
    }

    题目十:

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

    说明:每次只能向下或者向右移动一步。

    示例:

    输入:
    [
      [1,3,1],
    [1,5,1],
    [4,2,1]
    ]
    输出: 7
    解释: 因为路径 1→3→1→1→1 的总和最小。

    方法一:

    1、递归法,求左上角到右下角的最小和,可以先走一步,求grid[i+1][j]和grid[i][j+1]的最小值;

    2、从i=0,j=0开始递归

    3、需要重新递归函数,将遍历变量放入,递归出口为:if(i==grid.lengh&&j==grid[0].lenght) return Integer.MAX_VALUE;

    4、if(i==grid.lenght-1&&j==grid[0].lenght-1) return grid[i][j];

    具体代码:

    class Solution {   
        public int caculate(int[][] grid, int i,int j)
        {
            if(i==grid.length||j==grid[0].length) return Integer.MAX_VALUE;
            if(i==grid.length-1&&j==grid[0].length-1) return grid[i][j];
            
            return grid[i][j]+Math.min(caculate(grid, i, j+1), caculate(grid, i+1, j));
        }    
    public int minPathSum(int[][] grid) { return caculate(grid,0,0); } }

    方法二:

    1、动态规划,dp[i][j]表示点(i,j) 到右下角的最短距离;

    2、dp[i][j]=grid[i][j]+Math.min(dp[i+1][j],dp[i][j+1])动态方程

    3、初始化过程:if(i==grid.lenght&&j!=grid[0].lenght) dp[i][j]=grid[i][j]+dp[i][j+1];

    4、if(i!=grid.lenght&&j==grid[0].lenght) dp[i][j]=grid[i][j]+dp[i+1][j];

    5、否则为动态方程:dp[i][j]=grid[i][j]+Math.min(dp[i+1][j],dp[i][j+1])

    6、最后为:else dp[i][j]=grid[i][j]

    具体代码:

    class Solution {   
        public int minPathSum(int[][] grid) {
    
            int[][] dp=new int[grid.length][grid[0].length];
            
            for(int i=grid.length-1;i>=0;i--)
                for(int j=grid[0].length-1;j>=0;j--)
                {
                    if(i==grid.length-1&&j!=grid[0].length-1)
                        dp[i][j]=grid[i][j]+dp[i][j+1];
                    else if (i!=grid.length-1&&j==grid[0].length-1)
                    {
                        dp[i][j]=grid[i][j]+dp[i+1][j];
                    }
                    else if (i!=grid.length-1&&j!=grid[0].length-1)
                    {
                        dp[i][j]=grid[i][j]+Math.min(dp[i+1][j], dp[i][j+1]);
                    }
                    else {
                        dp[i][j]=grid[i][j];
                    }
                }    
            return dp[0][0];
        }
    }
  • 相关阅读:
    gojs常用API-画布定义
    页面开发的标准
    iis7.5做反向代理配置方法实例图文教程
    Tomcat实现反向代理
    nodejs的package.json依赖dependencies中 ^ 和 ~ 的区别
    dependencies与devDependencies的区别
    常见的cmd命令
    解决SecureCRT中文显示乱码
    ASP防XSS代码
    Android页面之间进行数据回传
  • 原文地址:https://www.cnblogs.com/Optimism/p/11327405.html
Copyright © 2011-2022 走看看