关于排序的一些题目

关于排序的一些题目

1、合并区间

  • 描述

    1. 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

    2. 示例

      输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
      输出:[[1,6],[8,10],[15,18]]
      解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
      
  • 解法

    ​ 先将区间根据第一个元素排序,然后根据两个区间之间的交集关系,确定怎么往结果集里面放

  • 源码

    class Solution {
        public int[][] merge(int[][] intervals) {
            //排序
            Arrays.sort(intervals,(o1, o2)->{
                return o1[0]-o2[0];
            });
            int[][] res = new int[intervals.length][2];
            res[0] = intervals[0];
            int index = 0;
            //寻找可合并的情况
            for(int i=1; i<intervals.length; i++){
                //(1)、前者完全包含后者
                //啥也不做
                    //(2)、两者有交集但是 前者尾部未包含后者尾部
                if(res[index][1]>=intervals[i][0] && res[index][1]<=intervals[i][1]){
                    res[index] = new int[]{res[index][0],intervals[i][1]};
                }
                //(3)、两者没有交集
                else if(res[index][1] < intervals[i][0]){
                    //注意这里的 ++index,否则会将前面的值覆盖
                    res[++index] = intervals[i];
                }
            }
            return Arrays.copyOfRange(res, 0, index+1);
        }
    }
    

2、数组的相对排序

  • 描述

    1. 给你两个数组,arr1 和 arr2,arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。

      对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

      数据约束:
      1 <= arr1.length, arr2.length <= 1000
      0 <= arr1[i], arr2[i] <= 1000
      arr2 中的元素 arr2[i]  各不相同 
      arr2 中的每个元素 arr2[i] 都出现在 arr1 中
      
    2. 示例

      输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
      输出:[2,2,2,1,4,3,3,9,6,7,19]
      
  • 解法

    ​ 因为数据的范围不是太大,可以使用计数排序的方法,我这里用了count的二维数组,count[0]表示arr1里面出现的数字和次数,count[1]表示这个数字是否在arr2里面出现过,如果有的话置为1。

  • 源码

    class Solution {
        public int[] relativeSortArray(int[] arr1, int[] arr2) {
            int[] res = new int[arr1.length];
            int[][] count = new int[2][1001];
            //将arr1里面出现的数字和次数放入 count[0] 里面
            for (int k : arr1) {
                count[0][k]++;
            }
            //结果数组res的下标
            int index = 0;
            //遍历数组arr2,将出现的数字和count里面对应的次数放入res里面
            for (int k : arr2) {
                //标记出现的数字
                count[1][k] = 1;
                for (int j = 0; j < count[0][k]; j++) {
                    res[index++] = k;
                }
            }
            //将未出现的数字按序放入res里面
            for(int i=0; i<count[0].length; i++){
                if((count[1][i]!=1) && (count[0][i]!=0)){
                    for(int j=0; j<count[0][i]; j++){
                        res[index++] = i;
                    }
                }
            }
            return res;
        }
    }
    

3、数组中第 K 大的数

  • 描述

    1. 给定整数数组 nums 和整数 k,请返回数组中第 **k **个最大的元素。

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

    2. 示例

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

    出题人的本意是想实现快排或者堆排。面试时也可能会让手撕快排或者优先队列,还是要掌握的。

  • 源码

    快排:
    class Solution {
        Random random = new Random();
        public int findKthLargest(int[] nums, int k) {
            return quickSelect(nums, 0, nums.length - 1, nums.length - k);
        }
        public int quickSelect(int[] a, int l, int r, int index) {
            int q = randomPartition(a, l, r);
            if (q == index) {
                return a[q];
            } else {
                return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
            }
        }
    
        public int randomPartition(int[] a, int l, int r) {
            int i = random.nextInt(r - l + 1) + l;
            swap(a, i, r);
            return partition(a, l, r);
        }
    
        public int partition(int[] a, int l, int r) {
            int x = a[r], i = l - 1;
            for (int j = l; j < r; ++j) {
                if (a[j] <= x) {
                    swap(a, ++i, j);
                }
            }
            swap(a, i + 1, r);
            return i + 1;
        }
    
        public void swap(int[] a, int i, int j) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    
    ============================================================
    
    堆排
    class Solution {
        public int findKthLargest(int[] nums, int k) {
            int heapSize = nums.length;
            buildMaxHeap(nums, heapSize);
            for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
                swap(nums, 0, i);
                --heapSize;
                maxHeapify(nums, 0, heapSize);
            }
            return nums[0];
        }
    
        public void buildMaxHeap(int[] a, int heapSize) {
            for (int i = heapSize / 2; i >= 0; --i) {
                maxHeapify(a, i, heapSize);
            } 
        }
    
        public void maxHeapify(int[] a, int i, int heapSize) {
            int l = i * 2 + 1, r = i * 2 + 2, largest = i;
            if (l < heapSize && a[l] > a[largest]) {
                largest = l;
            } 
            if (r < heapSize && a[r] > a[largest]) {
                largest = r;
            }
            if (largest != i) {
                swap(a, i, largest);
                maxHeapify(a, largest, heapSize);
            }
        }
    
        public void swap(int[] a, int i, int j) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    

4、链表排序

  • 描述:

    1. 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

    2. 示例

      输入:head = [4,2,1,3]
      输出:[1,2,3,4]
      
  • 解法:

    ​ 使用归并的基本方法,涉及链表找中点和归并的基本知识

  • 源码:

    class Solution {
        public ListNode sortList(ListNode head) {
            //特殊情况
            if(head == null) return null;
            //递归返回情况
            if(head.next == null) return head;
            //找到中间节点
            ListNode mid = findMid(head);
            ListNode h2 = mid.next;
            //断开原链表
            mid.next = null;
            //排序合并
            ListNode t1 = sortList(head);
            ListNode t2 = sortList(h2);
            return merge(t1,t2);
        }
    
        //快慢指针寻找链表中点
        public ListNode findMid(ListNode head){
            ListNode fast = head;
            ListNode slow = head;
            //偶数节点时,使用这个找到的是前面一个节点
            while(fast.next != null){
                if(fast.next.next != null) fast = fast.next.next;
                else break;
                if(slow.next != null) slow = slow.next;
            }
            return slow;
        }
    
        //合并有序链表
        public ListNode merge(ListNode h1, ListNode h2){
            //用来迭代的节点
            ListNode temp = new ListNode();
            //记录返回位置的节点
            ListNode head = temp;
            //归并排序的基本方法
            while(h1!=null && h2!=null){
                if(h1.val <= h2.val){
                    temp.next = h1;
                    h1 = h1.next;
                }
                else {
                    temp.next = h2;
                    h2 = h2.next;
                }
                temp = temp.next;
            }
            //归并:将后续节点加上来
            if(h1 != null) temp.next=h1;
            if(h2 != null) temp.next=h2;
            return head.next;
        }
    }
    

5、合并排序链表

  • 描述:

    1. 给你一个链表数组,每个链表都已经按升序排列。

      请你将所有链表合并到一个升序链表中,返回合并后的链表。

    2. 示例

      输入:lists = [[1,4,5],[1,3,4],[2,6]]
      输出:[1,1,2,3,4,4,5,6]
      解释:链表数组如下:
      [
        1->4->5,
        1->3->4,
        2->6
      ]
      将它们合并到一个有序链表中得到。
      1->1->2->3->4->4->5->6
      
  • 解法:

    ​ 借助上一题的链表合并,这题是很简单的,各个链表内部已经升序排好了,只需要合并即可。但是我使用的是顺序合并,时间复杂度略高。如果使用二分的方式,会快很多。

  • 源码:

    ---------------顺序合并 101ms---------------
        
    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            if(lists==null || lists.length == 0) return null;
            for(int i=1; i<lists.length; i++){
                ListNode temp = merge(lists[i-1],lists[i]);
                lists[i] = temp;
            }    
            return lists[lists.length-1];
        }
    
        //合并有序链表
        public ListNode merge(ListNode h1, ListNode h2){
            ListNode temp = new ListNode();
            ListNode head = temp;
            while(h1!=null && h2!=null){
                if(h1.val <= h2.val){
                    temp.next = h1;
                    h1 = h1.next;
                }
                else {
                    temp.next = h2;
                    h2 = h2.next;
                }
                temp = temp.next;
            }
            if(h1 != null) temp.next=h1;
            if(h2 != null) temp.next=h2;
            return head.next;
        }
    }
    
    -----------二分递归 1ms范例-----------
    
    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            return marge(lists, 0, lists.length - 1);
        }
        public ListNode marge(ListNode[] lists, int l, int r){
            if(l > r)
                return null;
            int mid = l + (r - l) / 2;
            if(l == r)
                return lists[l];
            return mergeTwoLists(marge(lists, l, mid), marge(lists, mid + 1, r)); 
        }
    
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            
            ListNode res = new ListNode(0);
            ListNode cur = res;
            while(l1 != null && l2 != null){
                if(l1.val <= l2.val){
                    cur.next = l1;
                    l1 = l1.next;
                }else{
                    cur.next = l2;
                    l2 = l2.next; 
                }
                cur = cur.next;
            }
            cur.next = l1 == null ? l2 : l1;
            return res.next;
        }
    }
    
posted @ 2022-06-22 11:45  心是冰冰的  阅读(60)  评论(0)    收藏  举报