排序


使数组唯一的最小增量

给定整数数组 A,每次 move 操作将会选择任意 A[i],并将其递增 1。
返回使 A 中的每个值都是唯一的最少操作次数。

示例 1:
输入:[1,2,2]
输出:1
解释:经过一次 move 操作,数组将变为 [1, 2, 3]。

示例 2:
输入:[3,2,1,2,1,7]
输出:6
解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。

提示:0 <= A.length <= 40000;0 <= A[i] < 40000

解法
1)使每个数都不一样,很自然想到排序(涉及数的大小);排序之后,通过遍历保证每个数大于前面的一个数即可;
2)瓶颈在排序,所以考虑线性复杂度的排序;因为这里有条件每个数的都为正数,且小于40000,可以考虑使用计数排序。
算法复杂度为O(N)

public int minIncrementForUnique(int[] A) {
       int count = 0;
        countSort(A);

        for (int i = 1; i < A.length; i++) {
            if (A[i] <= A[i - 1]) {
                int d = A[i-1] - A[i];
                A[i] += d+1;
                count += d+1;
            }
        }

        return count;
    }

    public static void countSort(int[] nums) {

        if(nums.length <= 1){
            return;
        }

        // 取最大值
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > max) {
                max = nums[i];
            }
        }

        // 计数数组
        int[] count = new int[max + 1];
        for (int num : nums) {
            count[num]++;
        }

        // 排序
        int ind = 0;
        for (int i = 0; i < count.length; i++) {
            for (int j = 0; j < count[i]; j++) {
                nums[ind++] = i;
            }
        }
    }

合并区间

给出一个区间的集合,请合并所有重叠的区间。

示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

排序
按照区间的左边界排序后,遍历一次即可;

    public static int[][] merge(int[][] intervals) {

        if(intervals.length <= 1){
            return intervals;
        }

        // 按照第一位数排序
        Arrays.sort(intervals, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });

        List<int[]> merged = new ArrayList<>();
        int L = intervals[0][0];
        int R = intervals[0][1];
        for(int i=1; i<intervals.length; i++){
            if(intervals[i][0] > R){
                merged.add(new int[]{L,R});
                L = intervals[i][0];
                R = intervals[i][1];
            }else {
                R = Math.max(R, intervals[i][1]);
            }
        }
        merged.add(new int[]{L,R});

        return merged.toArray(new int[merged.size()][2]);
    }

数组中的第K个最大元素(https://leetcode-cn.com/problems/kth-largest-element-in-an-array/)

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

 public static int findKthLargest(int[] nums, int k) {
        buildHeap(nums);
        int len = nums.length-1;
        for(int i=0; i<k; i++){
            if(i == k-1){
                return nums[0];
            }
            swapRef(nums, 0, len--);
            percDown(nums, len, 0);
        }
        return 0;
    }

    public static void buildHeap(int[] nums) {
        for (int i = nums.length / 2 - 1; i >= 0; i--) {
            percDown(nums, nums.length, i);
        }
    }

    public static void percDown(int[] nums, int len, int k) {
        while (2 * k + 1 < len) {
            int maxChild = 2 * k + 1;
            if (maxChild + 1 < len && nums[maxChild + 1] > nums[maxChild]) {
                maxChild = maxChild + 1;
            }
            if(nums[k] < nums[maxChild]){
                swapRef(nums, k, maxChild);
            }
            k = maxChild;
        }
    }

    public static void swapRef(int[] nums, int ind1, int ind2){
        int temp = nums[ind1];
        nums[ind1] = nums[ind2];
        nums[ind2] = temp;
    }

直接利用jdk中的优先队列数据结构:

public static int findKthLargest(int[] nums, int k) {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        };
        PriorityQueue<Integer> heap = new PriorityQueue<>(comparator);
        for(int num : nums){
            heap.add(num);
        }
        for(int i=0; i<k; i++){
            int num = heap.poll();
            if(i == k-1){
                return num;
            }
        }
        return 0;
    }

快速选择法
本方法大致上与快速排序相同。简便起见,注意到第 k 个最大元素也就是第 N - k + 1个最小元素,因此可以用第 k 小算法来解决本问题(这里下标从1开始)。

假设枢纽元的下标pivot_index,枢纽元左边的数字都小于枢纽元,右边的数字都大于枢纽元,所以pivot_index表示枢纽元是第pivot_index+1个最小的数字。每次利用枢纽元划分一次,比较(sth = N - k + 1)与 (pivot_index+1)的大小:
1)若sth小于枢纽元位置,则对枢纽元左边的元素进行递归;
2)若sth大于枢纽元位置,则对枢纽元右边的元素进行递归;
3)若相等,则返回枢纽元;
这相比快速排序,只需要递归一半即可;

 public static int findKthLargest(int[] nums, int k) {
        return recursion(nums, 0, nums.length - 1, nums.length - k + 1);
    }

    public static int recursion(int[] nums, int L, int R, int sth) {
        if (L == R) {
            return nums[L];
        }

        int pivot = nums[R]; // 使用下面的随机元素作为枢纽元更加快速
        // Random random_num = new Random();
        // int pivot_index = L + random_num.nextInt(R - L);
        // int pivot = nums[pivot_index];
        // swapRef(nums, pivot_index, R);

        int left = L;
        int right = R;
        while (left < right) {
            while (left < right && nums[left] <= pivot) {
                left++;
            }
            while (right > left && nums[right] >= pivot) {
                right--;
            }
            if (left < right) {
                swapRef(nums, left, right);
            }else {
                swapRef(nums, left, R);
            }
        }

        // L -- left -- R
        if (sth == left + 1) {
            return nums[left];
        } else if (sth < left + 1) {
            return recursion(nums, L, left - 1, sth);
        } else {
            return recursion(nums, left + 1, R, sth);
        }
    }


    public static void swapRef(int[] nums, int left, int right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }

前 K 个高频元素

技巧总结:
1)类似前k个最大元素,前k个最小元素的问题,可以使用堆(优先队列)接口;
2)前k大 用小根堆(前k小 用大根堆),维护堆大小不超过 k 即可,一旦超过k,出堆最小元素即可;
3)jdk的优先队列可以接受compator对象作为构造器参数,这样内部在比较元素的时候即调用compator对象;即PriorityQueue内部的涉及比较的操作实现有两套,一套是当compator变量不为null时,使用此对象进行比较;一套是当compator变量为null时,使用元素的compareTo方法;
堆(优先队列)

 public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> numCountMap = new HashMap<>();
        for(int num :nums){
            numCountMap.put(num, numCountMap.getOrDefault(num, 0) + 1);
        }
        // 构建最小堆
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return numCountMap.get(o1) - numCountMap.get(o2);
            }
        };
        PriorityQueue<Integer> heap = new PriorityQueue<>(comparator);
        // 只保留k个元素:必然是堆中最大的元素
        for(int num : numCountMap.keySet()){
            heap.add(num);
            if(heap.size() > k){
                heap.poll();
            }
        }
        // 返回元素
        int[] ret = new int[heap.size()];
        int ind = 0;
        while (!heap.isEmpty()){
            ret[ind++] = heap.poll();
        }
        return ret;
    }

自己实现的简单的优先队列结构:这里没有实现接收compator对象的构造器;其实实现也很简单,添加compator成员变量,在比较元素大小时,使用compator.compare方法即可。这里为了代码的简洁,就不实现两套比较逻辑了。

public class MyHeap<AnyType extends Comparable<? super AnyType>> {

    private AnyType[] array;
    private int currentSize;

    public MyHeap(){
        array = (AnyType[]) new Comparable[10];
    }

    public MyHeap(int size) {
        currentSize = 0;
        array = (AnyType[]) new Comparable[size];
    }

    public MyHeap(AnyType[] items) {
        currentSize = items.length;
        array = (AnyType[]) new Comparable[currentSize * 2];
        int i = 1;
        for (AnyType item : items) {
            array[i++] = item;
        }
        buildHeap();
    }

    // 构建最小堆
    public void buildHeap() {
        for (int i = currentSize / 2; i <= currentSize; i++) {
            percDown(i);
        }
    }

    public void percDown(int hole) {
        AnyType temp = array[hole];
        int child;
        for(; hole * 2 <= currentSize; hole = child){
            // 获取儿子中较小者
            child = 2 * hole;
            if (array[child + 1].compareTo(array[child]) < 0) {
                child = child + 1;
            }

            // 比较大小
            if(temp.compareTo(array[child]) > 0){
                array[hole] = array[child];
            }else {
                break; // 一定要break, 不然仍然会执行一次 hole=child
            }
        }
        array[hole] = temp;
    }

    public void add(AnyType item){
        if(currentSize >= array.length-1){
            enlarge(array.length * 2);
        }
        int hole = ++currentSize;
        for(int parent = hole / 2; parent >= 1; parent = parent / 2){
            if(array[parent].compareTo(item) > 0){
                array[hole] = array[parent];
                hole = parent;
            }else {
                break;
            }
        }
        array[hole] = item;
    }

    // 移除堆顶最小元素
    public AnyType pop(){
        // 末尾元素移动至堆顶,currentSize大小减1
        AnyType min = array[1];
        array[1] = array[currentSize];
        array[currentSize--] = null;

        // 堆顶元素下滤
        percDown(1);
        return min;
    }

    public void enlarge(int size){
        AnyType[] old = array;
        array = (AnyType[]) new Comparable[size];
        for (int i = 0; i < old.length; i++) {
            array[i] = old[i];
        }
    }

最大数


排序
此题主要考虑两个字符串a和b比较大小的问题;不能直接使用String的比较大小的方法,
如果"a" + "b" > "b" + "a", 那么"a" 就应该在前,即 "a" > "b"。这里可以直接使用Arrays.sort方法,然后重写一个compator作为参数传入即可。这里为了复习一些归并排序的写法:

public static String largestNumber(int[] nums) {
        if(nums.length == 1){
            return String.valueOf(nums[0]);
        }
        mergeSort(nums, new int[nums.length], 0, nums.length - 1);
        StringBuilder sb = new StringBuilder();
        int start = 0;
        while (start < nums.length - 1 && nums[start] == 0) {
            start++;
        }
        for (int i = start; i < nums.length; i++) {
            sb.append(nums[i]);
        }
        return sb.toString();
    }

    // 关键就是这个比较方法
    public static int compare(int a, int b) {
        String ab = String.valueOf(a) + String.valueOf(b);
        String ba = String.valueOf(b) + String.valueOf(a);
        return ab.compareTo(ba);
    }

    public static void mergeSort(int[] nums, int[] temp, int left, int right) {
        if (left == right) {
            return;
        }
        int med = (left + right) / 2;
        mergeSort(nums, temp, left, med);
        mergeSort(nums, temp, med + 1, right);
        merge(nums, temp, left, med, right);
    }

    public static void merge(int[] nums, int[] temp, int left, int med, int right) {
        int index = left;
        int ptr1 = left;
        int ptr2 = med + 1;
        while (ptr1 <= med && ptr2 <= right) {
            if (compare(nums[ptr1], nums[ptr2]) > 0) {
                temp[index++] = nums[ptr1++];
            } else {
                temp[index++] = nums[ptr2++];
            }
        }
        while (ptr1 <= med) {
            temp[index++] = nums[ptr1++];
        }
        while (ptr2 <= right) {
            temp[index++] = nums[ptr2++];
        }

        for (int i = left; i <= right; i++) {
            nums[i] = temp[i];
        }
    }

直接使用Arrays.sort方法比较:

    public static String largestNumber(int[] nums) {
        if (nums.length == 1) {
            return String.valueOf(nums[0]);
        }
        String[] numArr = new String[nums.length];
        for (int i = 0; i < nums.length; i++) {
            numArr[i] = String.valueOf(nums[i]);
        }
        Comparator<String> comparator = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                String s1 = o1 + o2;
                String s2 = o2 + o1;
                int result = s1.compareTo(s2);
                if (result > 0) {
                    return -1;
                } else if (result == 0) {
                    return 0;
                } else {
                    return 1;
                }
            }
        };

        Arrays.sort(numArr, comparator);

        StringBuilder sb = new StringBuilder();
        int start = 0;
        while (start < numArr.length - 1 && numArr[start].equalsIgnoreCase("0")) {
            start++;
        }
        for (int i = start; i < numArr.length; i++) {
            sb.append(numArr[i]);
        }
        return sb.toString();
    }

排序数组


归并排序

 public int[] sortArray(int[] nums) {
        mergeSort(nums, new int[nums.length], 0, nums.length-1);
        return nums;
    }

    public void mergeSort(int[] nums, int[] temp, int left, int right){
        if(left >= right){
            return;
        }
        int med = (left + right) / 2;
        mergeSort(nums, temp, left, med);
        mergeSort(nums, temp, med + 1, right);
        merge(nums, temp, left, med, right);
    }

    public void merge(int[] nums, int[] temp, int left, int med, int right){
        int index1 = left;
        int index2 = med + 1;
        int tempIndex = left;
        while (index1 <= med && index2 <= right){
            if(nums[index1] < nums[index2]){
                temp[tempIndex++] = nums[index1++];
            }else {
                temp[tempIndex++] = nums[index2++];
            }
        }
        while (index1 <= med){
            temp[tempIndex++] = nums[index1++];
        }
        while (index2 <= right){
            temp[tempIndex++] = nums[index2++];
        }
        for(int i = left; i <= right; i++){
            nums[i] = temp[i];
        }
    }

快排

public void quickSort(int[] nums, int start, int end){
        if(start >= end){
            return;
        }
        int pivot = end;
        int left = start;
        int right = end;
        while (left < right){
            while (left < right && nums[left] <= nums[pivot]){
                left++;
            }
            while (left < right && nums[right] >= nums[pivot]){
                right--;
            }
            if(left < right){
                swap(nums, left, right);
            }
        }
        // 交换枢纽元和left的位置
        swap(nums, left, end);
        quickSort(nums, start, left - 1);
        quickSort(nums, left + 1, end);
    }

    public void swap(int[] nums, int index1, int index2){
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }

计数排序
考虑到有条件-50000<num<50000,可以利用计数排序。

public int[] sortArray(int[] nums) {
        int max = 100001;
        int offset = 50000;
        int[] count = new int[max];
        for(int num : nums){
            count[num + offset] ++;
        }
        int index = 0;
        for(int i=0; i<max; i++){
            while (count[i] > 0){
                nums[index++] = i - offset;
                count[i] --;
            }
        }
        return nums;
    }

会议室 II


优先队列(最小堆)

    public int minMeetingRooms(int[][] intervals) {
        if (intervals.length == 0) {
            return 0;
        }
        // 按照开始时间排序
        Arrays.sort(intervals, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
        // 优先队列, 按照结束时间构建最小堆
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

        priorityQueue.add(intervals[0][1]);
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] >= priorityQueue.peek()) {
                priorityQueue.poll();
            }
            priorityQueue.add(intervals[i][1]);
        }
        return priorityQueue.size();
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容