算法精选题总结之数组类

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


    3768281-df061345ccb1921d.gif
    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;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_33916256/article/details/87307738
今日推荐