算法精选题总结之数组类

作者: 码上就说 | 来源:发表于2019-01-29 15:22 被阅读57次

    1.判断数组中三数之和
    2.判断数组中最接近的三数之和
    3.排序数组中剔除重复的数
    4.数组序列字典序下一个排列
    5.旋转数组中搜索特定值
    6.排序数组中起始位置和结束位置
    7.排序数组中搜索或者插入特定值
    8.最大连续子数组和
    9.快速排序
    10.寻找数组中超过一半的元素

    1.判断数组中三数之和

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

    • 关键点提炼:
      --- 三个元素
      --- a+b+c=0
      --- 不能重复

    采用双向链表的思路。解决这类问题,都是先对数组排序,然后根据有序数组的特性使用双向链表来解决问题。一个指针在start,一个指针在end,然后根据判断来决定start后移还是end前移。

    // Author:jefflee1314
    import java.util.List;
    import java.util.ArrayList;
    import java.lang.Integer;
    import java.util.Arrays;
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] num = new int[]{-1,0,1,2,-1,-4};
            System.out.println(instance.threeSum(num));
        }
    }
    
    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            Arrays.sort(nums);
            int iNum =Integer.MAX_VALUE ;
            int jNum;
            int kNum;
            List<List<Integer>> result = new ArrayList<>();
            for (int i = 0; i < nums.length && nums[i] <= 0; i++) {
                if (iNum == nums[i]) {
                    continue;
                }
                iNum = nums[i];
                int j = i + 1;
                int k = nums.length - 1;
                while (j < k) {
                    int sum = nums[i] + nums[j] + nums[k];
                    if (sum > 0) {
                        k--;
                    } else if (sum < 0) {
                        j++;
                    } else {
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[k]);
                        result.add(list);
    
                        jNum = nums[j];
                        do {
                            j++;
                            if (j>=k) {
                                break;
                            }
                        } while (jNum == nums[j]);
                        kNum = nums[k];
                        do {
                            k--;
                            if (j>=k) {
                                break;
                            }
                        } while (kNum == nums[k]);
                    }
                }
            }
            return result;
        }
    }
    

    2.判断数组中最接近的三数之和

    2.给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

    例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.

    与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

    // Author:jefflee1314
    import java.util.Arrays;
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] num = new int[]{-1,2,1,-4};
            System.out.println(instance.threeSumClosest(num,1));
        }
    }
    
    class Solution {
        public int threeSumClosest(int[] nums, int target) {
            Arrays.sort(nums);
            int res = nums[0] + nums[1] + nums[2];
            for (int i = 0; i < nums.length - 2; i++) {
                int left = i + 1, right = nums.length - 1;
                while (left < right) {
                    int threeSum = nums[i] + nums[left] + nums[right];
                    if (threeSum > target)
                        right--;
                    else if (threeSum < target)
                        left++;
                    else
                        return target;                
                    res = Math.abs(threeSum - target) > Math.abs(res - target) ? res : threeSum;
    
                }            
            }
            return res;
        }
    }
    

    3.排序数组中剔除重复的数

    3.给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

    不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

    • 示例:
      给定 nums = [0,0,1,1,1,2,2,3,3,4],
      函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
      你不需要考虑数组中超出新长度后面的元素。

    数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[i] = nums[j],我们就增加 j 以跳过重复项。

    当我们遇到 nums[j] != nums[i]时,跳过重复项的运行已经结束,因此我们必须把它(nums[j])的值复制到 nums[i + 1]。然后递增 i,接着我们将再次重复相同的过程,直到 j到达数组的末尾为止。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{1,1,2,2,2,3,3,4,4,4,4,4,5,5,6,6,7};
            int result = instance.removeDuplicates(nums);
            for(int i=0;i<result;i++) {
                System.out.println(nums[i]);
            }
        }
    }
    
    class Solution {
        public int removeDuplicates(int[] nums) {
            if (nums.length == 0) return 0;
            int i = 0;
            for (int j = 1; j < nums.length; j++) {
                if (nums[j] != nums[i]) {
                    i++;
                    nums[i] = nums[j];
                }
            }
            return i + 1;
        }
    }
    

    类似题目:给定一个数组 nums和一个值 val,你需要原地移除所有数值等于 val的元素,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组,并在使用 O(1) 额外空间的条件下完成。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{0,1,2,2,3,0,4,2};
            int result = instance.removeElement(nums,2);
            for(int i=0;i<result;i++) {
                System.out.println(nums[i]);
            }
        }
    }
    
    class Solution {
        public int removeElement(int[] nums, int val) {
            if (nums.length == 0) return 0;
            int i = 0;
            for (int j = 1; j < nums.length; j++) {
                if (nums[j] != val) {
                    i++;
                    nums[i] = nums[j];
                }
            }
            return i + 1;
        }
    }
    

    4.数组序列字典序下一个排列

    4.实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

    如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
    必须原地修改,只允许使用额外常数空间。

    • 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
      1,2,3 → 1,3,2
      3,2,1 → 1,2,3
      1,1,5 → 1,5,1


      Next_Permutation.gif
    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            //int[] nums = new int[]{1,5,8,4,7,6,5,3,1};
            int[] nums = new int[]{5,1,1};
            for(int i=0;i<nums.length;i++) {
                System.out.print(nums[i]);
                System.out.print(" ");
            }
            System.out.println();
            instance.nextPermutation(nums);
            for(int i=0;i<nums.length;i++) {
                System.out.print(nums[i]);
                System.out.print(" ");
            }
        }
    }
    
    class Solution {
        public void nextPermutation(int[] nums) {
            if(nums.length<=1)return;
            int i=nums.length-2;
            while(i>=0 && nums[i]>=nums[i+1]){
                i--;
            }
            if(i<0){
                reverseArr(nums, 0, nums.length-1);
                return;
            }
            int j=i;
            int startNums = nums[i];
            while(j<nums.length-1&& nums[j+1]>startNums) {
                j++;
            }
            swapArr(nums, i, j);
            reverseArr(nums, i+1, nums.length-1);
        }
        
        public void reverseArr(int[] nums, int start,int end) {
            while(start<end){
                int temp = nums[start];
                nums[start]=nums[end];
                nums[end]=temp;
                start++;
                end--;
            }
        }
        public void swapArr(int[] nums, int i,int j) {
            int temp = nums[i];
            nums[i]=nums[j];
            nums[j]=temp;
        }
    }
    

    5.旋转数组中搜索特定值

    5.假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

    你可以假设数组中不存在重复的元素。

    你的算法时间复杂度必须是 O(log n) 级别。

    思路:首先找到旋转的点(这个过程时间复杂度是O(logN)),这样这个index左边和右边都是有序的,分别对左边和右边的数组进行二分查找来确定最终的target所在的索引。二分查找的时间复杂度也是O(logN)

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{1,2,3,4,5,6};
            for(int i=0;i<nums.length;i++) {
                System.out.print(nums[i]);
                System.out.print(" ");
            }
            System.out.println();
            int result = instance.search(nums, 5);
            System.out.println(result);
        }
    }
    
    class Solution {
        public int search(int[] nums, int target) {
            int i=0;
            int j=nums.length-1;
            int mid = (i+j)/2;
            while(i<j&&nums[i]>nums[j]){
                if(nums[i]<nums[mid]){
                    i=mid+1;
                }
                if(nums[mid]<nums[j]){
                    j=mid-1;
                }
                mid=(i+j)/2;
            }
            int result = binarySearch(nums, 0, i-1,target);
            if (result==-1) {
                result = binarySearch(nums,i,nums.length-1, target);
            }
            return result;
        }
        public int binarySearch(int[] nums,int start, int end, int target) {
            int mid=(start+end)/2;
            while(start<=end){
                if(nums[mid]<target){
                    start=mid+1;
                }
                if(nums[mid]>target) {
                    end=mid-1;
                }
                if(nums[mid]==target) {
                    return mid;
                }
                mid=(start+end)/2;
            }
            return -1;
        }
    }
    

    6.排序数组中起始位置和结束位置

    6.给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

    你的算法时间复杂度必须是 O(log n) 级别。

    如果数组中不存在目标值,返回 [-1, -1]。**

    算法的要求是 O(log n),这实际上已经给我们暗示了,要求你在有序数组中使用二分查找法,本体不太难,重要的是把握一些边界条件,这个需要十分仔细。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{1,1,1,2,4,4,4,5,5,5,5,6,7,8,8,9,9,9,9,9,9,10};
            int target = 9;
            int[] result = instance.searchRange(nums,target);
            
            for(int i=0;i<result.length;i++){
                System.out.print(result[i]);
                System.out.print(" ");
            }
            System.out.println();
            
        }
    }
    
    class Solution {
        public int[] searchRange(int[] nums, int target) {
            int start=-1;
            int end=-1;
            int[] result = new int[]{start,end};
            if(nums.length==0||(nums.length==1&&nums[0]!=target)){
                return result;
            }
            int targetIndex = binarySearch(nums, target, 0, nums.length-1);
            System.out.println("targetIndex="+targetIndex);
            
            //1.get left index
            if(targetIndex==0){
                start=0;
            } else {
                int i=0;
                int j=targetIndex-1;
                int searchIndex=binarySearch(nums, target,i,j);
                int finalResult = -1;
                while(searchIndex!=-1){
                    finalResult = searchIndex;
                    if(searchIndex==0){
                        break;
                    }else if(searchIndex-1==0 && nums[0]==target){
                        finalResult=0;
                        break;
                    } else {
                        i=0;
                        j=finalResult-1;
                        if(i<j){
                            searchIndex=binarySearch(nums, target,i,j);
                        }else{
                            break;
                        }
                    }
                }
                if(finalResult==-1){
                    start=targetIndex;
                }else {
                    start=finalResult;
                }
            }
            System.out.println("start="+start);
            
            //2.get right index
            if(targetIndex==nums.length-1){
                end=nums.length-1;
            } else {
                int i=targetIndex+1;
                int j=nums.length-1;
                int searchIndex=binarySearch(nums, target,i,j);
                int finalResult = -1;
                while(searchIndex!=-1){
                    finalResult = searchIndex;
                    if(searchIndex==nums.length-1){
                        break;
                    }else if(searchIndex+1==nums.length-1 && nums[nums.length-1]==target){
                        finalResult=nums.length-1;
                        break;
                    }else {
                        i=finalResult+1;
                        j=nums.length-1;
                        if(i<j){
                            searchIndex=binarySearch(nums, target,i,j);
                        }else{
                            break;
                        }
                    }
                }
                if(finalResult==-1){
                    end=targetIndex;
                }else {
                    end=finalResult;
                }
            }
            System.out.println("end="+end);
            
            result[0]=start;
            result[1]=end;
            return result;
        }
        
        public int binarySearch(int[] nums, int target, int start, int end) {
            int mid=(start+end)/2;
            while(start<=end){
                if(nums[mid]<target){
                    start=mid+1;
                }
                if(nums[mid]>target){
                    end=mid-1;
                }
                if(nums[mid]==target){
                    return mid;
                }
                mid=(start+end)/2;
            }
            return -1;
        }
    }
    

    7.排序数组中搜索或者插入特定值

    7.给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{1,3,5,6};
            int target=2;
            int result = instance.searchInsert(nums,target);
            System.out.println(result);
        }
    }
    
    class Solution {
        public int searchInsert(int[] nums, int target) {
            return binarySearch(nums,target);
        }
        
        public int binarySearch(int[] nums, int target) {
            int start=0;
            int end=nums.length-1;
            int mid=(start+end)/2;
            while(start<=end){
                if(nums[mid]==target){
                    return mid;
                }
                if(nums[mid]<target){
                    start=mid+1;
                }
                if(nums[mid]>target){
                    end=mid-1;
                }
                
                mid=(start+end)/2;
            }
            return start;
        }
    }
    

    8.最大连续子数组和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] nums = new int[]{-2,1,-3,4,-1,2,1,-5,4};
            int result = instance.maxSubArray(nums);
            System.out.println(result);
    
        }
    }
    
    class Solution {
        public int maxSubArray(int[] nums) {
          int curSum = Integer.MIN_VALUE;
          int maxSum = Integer.MIN_VALUE;
          for(int i=0;i<nums.length;i++) {
            if(curSum >= 0) {
              curSum += nums[i];
            }else {
              curSum = nums[i];
            }
    
            if(curSum > maxSum) {
              maxSum = curSum;
            }
          }
          return maxSum;
        }
    }
    

    9.快速排序

    class Solution {
        public int partition(int[] arr, int start, int end) {
            int num = arr[start];
            int i=start;
            int j=end+1;
            while(true) {
                while(arr[++i]<num) {
                    if(i==end)break;
                }
                while(arr[--j]>num) {
                    if(j==start)break;
                }
                if(i>=j)break;
                swap(arr, i,j);
            }
            swap(arr, start, j);
            return j;
        }
        
        public void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        
        public void quicksort(int[] arr, int start, int end) {
            if(start>=end) return;
            int privt = partition(arr, start, end);
            quicksort(arr, start, privt-1);
            quicksort(arr, privt+1, end);
        }
    }
    

    10.寻找数组中超过一半的元素

    假设一个数组中存在一个元素,超过这个数组长度的一半,请找出这个元素。

    // Author:jefflee1314
    
    public class Main {
        public static void main(String[] args) {
            Solution instance = new Solution();
            int[] array = new int[]{7,7,1,6,7,8,2,7,3,7,7,7,9};
            int result = instance.getMuchNum(array);
            System.out.println("result = " + result);
        }
    }
    
    class Solution {
        public int partition(int[] arr, int start, int end) {
            int num = arr[start];
            int i=start;
            int j=end+1;
            while(true) {
                while(arr[++i]<num) {
                    if(i==end)break;
                }
                while(arr[--j]>num) {
                    if(j==start)break;
                }
                if(i>=j)break;
                swap(arr, i,j);
            }
            swap(arr, start, j);
            return j;
        }
    
        public void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    
        public int getMuchNum(int[] arr) {
            int mid = (0+arr.length-1)/2;
            int privt = partition(arr, 0, arr.length-1);
            while(mid-1>privt || mid+1<privt) {
                printArray(arr);
                System.out.println("\tprivt = " + privt +", mid = " + mid);
                if(mid-1>privt) {
                    privt = partition(arr, privt+1, arr.length-1);
                }else if(mid+1<privt){
                    privt = partition(arr, 0, privt-1);
                }
            }
            return arr[privt];
        }
    
        public void printArray(int[] arr) {
            for(int i=0;i<arr.length;i++){
                System.out.print(arr[i]);
                System.out.print(" ");
            }
        }
    }
    

    上面这种思路是从快速排序的思路中得出的,既然超过数组中存在超过一半的元素,那么在有序的情况下取中间元素一定是这个元素,但是如果全部使用快排,那么一种会超时,时间复杂度是O(NlogN),复杂的情况下甚至能到O(N^2)。部分快排也是不行的,因为最好的情况下是第一次partition的时候就找到了正确的位置,此时也经历了O(N)了,但是正常情况下不会一次就成功的,那么多次的情况下,实际上最终的复杂度是O(mN),m>1


    下面提供一种新的思路:已知数组中存在超过一半的元素,那么我们使用两个变量,一个变量Num标记当前元素,另外一个变量count标记当前元素出现的次数。遍历数组,如果后一个元素不同,那么count--,当count==0,Num赋值为当前元素。这样可以保证在遍历完成之后,Num肯定是我们要找的元素。

    class Solution {
        public int getMuchNum(int[] arr) {
            int Num = arr[0];
            int count = 0;
            for(int i=0;i<arr.length;i++) {
                if(count == 0) {
                    Num = arr[i];
                    count++;
                }else if(arr[i] != Num) {
                    count--;
                }else {
                    count++;
                }
            }
            return Num;
        }
    }
    

    相关文章

      网友评论

        本文标题:算法精选题总结之数组类

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