美文网首页
1.数组(一)

1.数组(一)

作者: 今天柚稚了么 | 来源:发表于2020-03-22 21:39 被阅读0次

    https://leetcode-cn.com/tag/array/

    题目汇总

    1. 两数之和简单

    4. 寻找两个有序数组的中位数困难

    11. 盛最多水的容器中等

    15. 三数之和中等

    16. 最接近的三数之和中等

    18. 四数之和中等

    26. 删除排序数组中的重复项简单

    27. 移除元素简单

    31. 下一个排列中等

    33. 搜索旋转排序数组中等

    1. 两数之和简单

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
    你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。给定 nums = [2, 7, 11, 15], target = 9,返回 [0, 1]

    思路一:暴力法,时间复杂度O(n*n),空间复杂度O(1)
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int length = nums.length;
            int[] result = new int[2];
            for(int i=0;i<length;i++){
                for(int j=i+1;j<length;j++){
                    if(nums[i]+nums[j]==target)
                    {
                        result[0] = i;
                        result[1] = j;
                    }   
                }          
            }
            return result;
        }
    }
    
    思路二:哈希表,时间复杂度O(n),空间复杂度O(n)
    import java.util.HashMap;
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            Map<Integer,Integer> map = new HashMap<>();
            int[] result = new int[2];
            for(int i=0; i< nums.length; i++){
                int other = target - nums[i];
                if(map.containsKey(other)){
                    result[0] = map.get(other);
                    result[1] = i;
                }
                map.put(nums[i], i);
            }
            return result;
        }
    }
    

    4. 寻找两个有序数组的中位数困难

    给定两个大小为 m 和 n 的有序数组 nums1nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))
    你可以假设 nums1nums2 不会同时为空。
    nums1 = [1, 3],nums2 = [2],则中位数是 2.0

    思路一:两个数组合并后排序,然后根据奇数偶数,返回中位数。

    时间复杂度:遍历全部数组 O(m+n),不符合要求。

    class Solution {
        public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int[] nums = new int[nums1.length+nums2.length];
            //两个数组合并
            for(int i=0;i<nums1.length;i++){
                nums[i] = nums1[i];
            }
            for(int j=0;j<nums2.length;j++){
                nums[nums1.length+j] = nums2[j];
            }
            //合并后的数组进行排序
            for(int i=0;i<nums.length-1;i++){
                for(int j=0;j<nums.length-1-i;j++){ 
                    if(nums[j+1]<nums[j]){
                        int temp = nums[j+1];
                        nums[j+1] = nums[j];
                        nums[j] = temp;
                    }
                }
            }
            
            if(nums.length%2 == 0){
                return (nums[nums.length/2]+nums[nums.length/2-1])/2.0;
            }
            else if(nums.length%2 == 1) {
                return nums[nums.length/2];
            }
            return 0.0;
        }
    }
    
    思路二:二分法

    这个解法三太牛了,自己完全想不明白https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/

    11. 盛最多水的容器中等

    给你 n 个非负整数 a1a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
    说明:你不能倾斜容器,且 n 的值至少为 2。


    图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
    思路一:暴力法,时间复杂度O(n*n)
    class Solution {
        public int maxArea(int[] height) {
            if (height == null || height.length == 0) {
                return 0;
            }
            int maxarea = 0;
            for(int i = 0; i<height.length; i++){
                 for(int j = i + 1; j<height.length; j++){
                     maxarea = Math.max(maxarea, Math.min(height[i],height[j])*(j-i)) ;     
                     }             
                 }
            return maxarea;
            } 
    }
    
    思路二:双指针,时间复杂度O(n)

    移动指向较短线段的指针尽管造成了矩形宽度的减小,但却可能会有助于面积的增大。因为移动较短线段的指针会得到一条相对较长的线段,这可以克服由宽度减小而引起的面积减小。

    class Solution {
        public int maxArea(int[] height) {
            int left = 0;
            int right = height.length-1;
            int maxArea = 0;
            while(left<right){
                maxArea = Math.max(maxArea,Math.min(height[left],height[right])*(right-left));
                if(height[left]<height[right])
                    left++;          
                else 
                    right--;          
            }
        return maxArea;
        }
    }
    

    15. 三数之和中等

    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
    注意:答案中不可以包含重复的三元组。
    给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]

    思路:排序+双指针,时间复杂度O(n*n)

    参考https://leetcode-cn.com/problems/3sum/solution/pai-xu-shuang-zhi-zhen-zhu-xing-jie-shi-python3-by/
    遍历排序后的数组:
    若 nums[i]>0:后面不可能有三个数加和等于 0,直接返回结果;
    对于重复元素:跳过,避免出现重复解;
    令左指针 L=i+1,右指针 R=n-1,当 L<R 时,执行循环:
    当 nums[i]+nums[L]+nums[R]==0,执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 L,R 移到下一位置,寻找新的解
    若和大于 0,说明 nums[R]太大,R左移
    若和小于 0,说明 nums[L]太小,L右移

    class Solution {
         public List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            Arrays.sort(nums);
            if(nums == null||nums.length<3) 
                return res;
            for(int i = 0;i < nums.length;++i) {
                if(nums[i] > 0) 
                    return res;
                if(i > 0 && nums[i] == nums[i-1])
                    continue;
                int L = i+1;
                int R = nums.length-1;
                while (L < R) {
                    int sum = nums[i] + nums[L] + nums[R];
                    if(sum == 0) {
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[L]);
                        list.add(nums[R]);
                        res.add(list);
                        while(L < R && nums[L+1] == nums[L]) ++L;
                        while (L < R && nums[R-1] == nums[R]) --R;
                        ++L;
                        --R;
                    } else if(sum > 0) {
                        --R;
                    } else {
                        ++L;
                    }
                }
            }
            return res;
        }   
    }
    

    16. 最接近的三数之和中等

    给定一个包括 n个整数的数组 nums和 一个目标值 target。找出 nums中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
    例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
    与 target 最接近的三个数的和为 2. 因为(-1 + 2 + 1 = 2).

    思路:排序+双指针,时间复杂度O(n*n)

    和上道题目类似

    class Solution {
        public int threeSumClosest(int[] nums, int target) {
            int len = nums.length;
            Arrays.sort(nums);
            int ans = nums[0] + nums[1] + nums[2];
            for(int i = 0; i<len; i++){
                int l = i + 1;
                int r = len - 1;
                while(l < r){
                    int sum = nums[i]+nums[l]+nums[r];
                    if(Math.abs(target - sum) < Math.abs(target - ans))
                        ans = sum;
                    if(sum < target)
                        l++;
                    else if(sum > target)
                        r--;
                    else
                        return ans;
                }
            }
            return ans;
            
        }
    }
    

    18. 四数之和中等

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
    注意:答案中不可以包含重复的四元组。
    给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。满足要求的四元组集合为:
    [
    [-1, 0, 0, 1],
    [-2, -1, 1, 2],
    [-2, 0, 0, 2]
    ]

    思路:排序+双指针

    和三数之和的思路一样,增加一层循环即可

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

    26. 删除排序数组中的重复项简单

    给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,并在使用 O(1) 额外空间的条件下完成。
    给定数组 nums = [1,1,2],
    函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。

    思路:双指针,时间复杂度:O(n)

    用 2 个指针,一个i在前,一个j在后,比较 i 和 j 位置的元素是否相等。
    如果相等,j 后移 1 位;如果不相等,将 j 位置的元素复制到 i+1 位置上,i 后移一位,j 后移 1 位。重复上述过程,直到 j 等于数组长度。
    返回 i + 1,即为新数组长度。

    class Solution {
        public int removeDuplicates(int[] nums) {
            if(nums == null || nums.length == 0) 
                return 0;
            int i = 0;
            int j = 1;
            while(j < nums.length){
                if(nums[i] != nums[j]){
                    if(j - i > 1){
                        nums[i + 1] = nums[j];
                    }
                    i++;
                }
                j++;
            }
        return i + 1;
        }
    }
    

    27. 移除元素简单

    给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。
    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组**。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
    给定 nums = [3,2,2,3], val = 3,函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。

    思路:双指针,时间复杂度:O(n)

    用 2 个指针,其中 i 是慢指针,j是快指针。当 nums[j]与给定的值相等时,递增 j 以跳过该元素。只要 nums[j]!=val,我们就复制 nums[j] 到 nums[i] 并同时递增两个索引。重复这一过程,直到 j 到达数组的末尾,该数组的新长度为 i。

    class Solution {
        public int removeElement(int[] nums, int val) {
            if(nums == null || nums.length == 0) 
                return 0;
            int i = 0;
            int j = 0;
            while(j<nums.length){
                if(nums[j]!=val){
                    nums[i]=nums[j];
                    i++;
                }
                j++;
            }
        return i;
        }
    }
    

    官方题解 考虑了要删除的元素很少时的情况

    31. 下一个排列中等

    这是一道我看不懂题目的问题???
    实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
    必须原地修改,只允许使用额外常数空间。
    以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
    1,2,31,3,2
    3,2,11,2,3
    1,1,51,5,1

    思路:

    需要看题解https://leetcode-cn.com/problems/next-permutation/solution/xia-yi-ge-pai-lie-suan-fa-xiang-jie-si-lu-tui-dao-/
    (1)从后向前查找第一个相邻升序的元素对 (i,j),满足 A[i] < A[j]。此时 [j,end) 必然是降序
    (2)在 [j,end) 从后向前查找第一个满足 A[i] < A[k] 的 k。
    (3)将 A[i] 与 A[k] 交换
    (4)可以断定这时 [j,end) 必然是降序,逆置 [j,end),使其升序
    (5)如果在步骤 1 找不到符合的相邻元素对,说明当前 [begin,end) 为一个降序顺序,则直接跳到步骤 4

    class Solution {//执行用时:1 ms, 在所有 Java 提交中击败了99.71%的用户,//2020/08/03
        public void nextPermutation(int[] nums) {
            int i = nums.length - 2;
            int j = nums.length - 1;
            int k = nums.length - 1;
            while(i >= 0 && nums[i] >= nums[j]){//从后向前查找第一个相邻升序的元素对(i,j)
                i--;
                j--;
            }
            if(i >= 0){
                while(nums[i] >= nums[k]){//在 [j,end) 从后向前查找第一个满足 A[i] < A[k]的k
                    k--;
                } 
                int temp = nums[i];
                nums[i] = nums[k];
                nums[k] = temp;//交换nums[i]、nums[k]的值
            }
           
            int left = j;
            int right = nums.length - 1;
            while(left < right){
                int temp = nums[left]; 
                nums[left] = nums[right]; 
                nums[right] = temp; 
                left++; 
                right--;//逆置 [j,end),使其升序
            }
        }
    }
    

    33. 搜索旋转排序数组中等

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。
    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。
    你的算法时间复杂度必须是 O(log n) 级别。
    输入:nums = [4,5,6,7,0,1,2], target = 0,输出: 4

    思路:

    题目要找到一种O(log n)时间内的搜索方法,这提示我们可以用二分查找的方法。
    分为两种情况进行讨论:
    (1)4 5 6 7 0 1 2,此时nums[start]<=nums[mid],若nums[start]<=target<nums[mid],在前半部分进行查找
    (2)5 6 7 0 1 2 4,此时nums[start]>nums[mid],若nums[mid]<target<=nums[end],在后半部分进行查找

    class Solution {
        public int search(int[] nums, int target) {
            int start = 0;
            int end = nums.length - 1;
            
            while(start <= end){
                int mid = (end-start)/2+start;
                if(nums[mid]==target)
                    return mid;
                if(nums[start]<=nums[mid]){//前半部分有序
                    if(target>=nums[start]&&target<nums[mid]){//在前半部分查找
                        end = mid - 1;
                    }
                    else{
                        start = mid + 1;
                    }      
                }
                else{//后半部分有序
                    if(target>nums[mid]&&target<=nums[end]){//在后半部分查找
                        start = mid +1;
                    }                
                    else{
                        end = mid - 1;
                    }
                }
            }
        return -1;
        }
    }
    

    相关文章

      网友评论

          本文标题:1.数组(一)

          本文链接:https://www.haomeiwen.com/subject/xulcyhtx.html