• 排序算法


    5.1 常用的排序算法

    快速排序(Quicksort)

    此处采用左闭右闭的二分写法。

    private static void quickSort(int[] nums, int start, int end){
            if(start+1 >= end) return;
    
            int left=start, right=end-1, key=nums[left];
            while (left < right){
                while (left<right && nums[right]>= key){
                    right--;
                }
                nums[left] = nums[right];
                while (left<right && nums[left]<=key){
                    left++;
                }
                nums[right] = nums[left];
            }
            nums[left] = key;
            quickSort(nums, start, left);
            quickSort(nums, left+1, end);
        }
    

    归并排序(Merge Sort)

    private static void mergeSort(int[] nums, int[] tmp, int left, int right){
            if(left>=right) return;
            int mid = (left+right)/2;
            // 二路归并,所以是2个mergeSort()
            mergeSort(nums, tmp, left, mid);
            mergeSort(nums, tmp, mid+1, right);
            merge(nums, tmp, left, mid, right);
        }
    
        private static void merge(int[] nums, int[] tmp, int start, int mid, int end){
            int left_start=start, right_start=mid+1, i=0;
            while (left_start<=mid && right_start<= end){
                // tmp中为升序,挑取nums[]左右两分支中的,小的那个(从左往右遍历)
                tmp[i++] = nums[left_start]<nums[right_start]? nums[left_start++]: nums[right_start++];
            }
            // 若左边分支还有剩余,则全部拷贝进tmp[]
            while (left_start<=mid){
                tmp[i++] = nums[left_start++];
            }
            // 若右边分支还有剩余,全部拷贝进tmp[]
            while (right_start<=end){
                tmp[i++] = nums[right_start++];
            }
            // tmp[:i] => nums[start: start+i] 左闭右开
            System.arraycopy(tmp, 0, nums, start, i);
        }
    

    插入排序(Insertion Sort)

    private static void insertionSort(int[] nums){
            int tmp;
            for(int i=0; i<nums.length; i++){
                for(int j=i; j>0 && nums[j]<nums[j-1]; j--){
                    tmp = nums[j];
                    nums[j] = nums[j-1];
                    nums[j-1] = tmp;
                }
            }
        }
    

    冒泡排序(Bubble Sort)

    private static void bubbleSort(int[] nums){
            boolean swapped;
            int tmp;
            for(int i=1; i<nums.length; i++){
                swapped = false;
                for(int j=1; j<(nums.length-i)+1; j++){
                    if(nums[j] < nums[j-1]){
                        tmp = nums[j];
                        nums[j] = nums[j-1];
                        nums[j-1] = tmp;
                        swapped = true;
                    }
                }
                if(!swapped) break;
            }
        }
    

    选择排序(Selection Sort)

    private static void selectionSort(int[] nums){
            int mid, tmp;
            for(int i=0; i<nums.length-1; i++){
                mid = i;
                for(int j=i+1; j<nums.length; j++){
                    if(nums[j] < nums[mid]){
                        mid = j;
                    }
                }
                tmp = nums[mid];
                nums[mid] = nums[i];
                nums[i] = tmp;
            }
        }
    

    5.2 快速选择

    1. Kth Largest Element in an Array

      题目描述

      在一个未排序的数组中,找到第K大的数字。

      输入输出样例

      输入一个数组和一个目标值K,输出第K大的数字,默认有解。

      Input: [3,2,1,5,6,4] and k = 2
      Output: 5
      

      题解

      快速选择一般用于求解Kth Element问题,可以在O(n)时间复杂度和O(1)空间复杂度完成求解。

      快速选择的实现和快速排序相似,只不过需要找到第K大的枢(pivot)即可,不需要对其左右再进行排序,与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为O(n^2)。此处省略打乱步骤。

      private static int quickSelect(int[] nums, int k){
              int left=0, right=nums.length-1, mid, target=nums.length-k;
              while (left < right){
                  mid = helpFunc(nums, left, right);
                  if(mid < target){
                      left = mid + 1;
                  }else if(mid == target){
                      return nums[mid];
                  }else{
                      right = mid + 1;
                  }
              }
              return nums[left];
          }
          // 辅助函数 - 快速选择
          private static int helpFunc(int[] nums, int left, int right){
              int i=left+1, j=right, tmp;
              while (true){
                  while (i<right && nums[i]<=nums[left]){
                      i++;
                  }
                  while (left<j && nums[j]>=nums[left]){
                      j--;
                  }
                  if(i>=j) break;
                  tmp = nums[i];
                  nums[i] = nums[j];
                  nums[j] = tmp;
              }
              tmp = nums[left];
              nums[left] = nums[j];
              nums[j] = tmp;
              return j;
          }
      

    5.3 桶排序

    1. Top K Frequent Elements(Medium)

      题目描述

      给定一个数组,求前K个最频繁的数字。

      输入输出样例

      输入是一个数组和一个目标值K,输出是一个长度为K的数组。

      Input: nums = [1,1,1,1,2,2,3,4], k = 2
      Output: [1,2]
      

      题解

      ​ 顾名思义,桶排序的意思是为每个值设立一个桶,桶内记录这个值出现的次数(或其他属性),然后对桶进行排序。先通过桶排序得到4个桶[1,2,3,4],他们的值分别为[4,2,1,1],表示每个数字出现的次数。

      ​ 紧接着,按桶内的属性对4个桶进行排序(升序),这里可以使用任意排序算法,后K个桶就是需要的。

      
      private static Integer[] topKFrequent(int[] nums, int k){
              HashMap<Integer, Integer> map = new HashMap<>();
              for(int num: nums){
                  map.put(num, map.containsKey(num)? map.get(num)+1: 0);
              }
              // 桶数组->map的键数组
              Integer[] keys = map.keySet().toArray(new Integer[0]);
              // 按值的大小,即出现的频率排序,并截取前k个
              return Arrays.stream(keys).sorted(new Compare(map)).limit(k).toArray(Integer[]::new);
          }
      
          private static class Compare implements Comparator<Integer>{
              private final HashMap<Integer, Integer> map;
      
              public Compare(HashMap<Integer, Integer> map) {
                  this.map = map;
              }
              // 按map的value降序排列
              @Override
              public int compare(Integer o1, Integer o2) {
                  return map.get(o2)-map.get(o1);
              }
          }
      
      

    5.4 练习

    基础难度

    1. Sort Characters By Frequency(Medium)

      桶排序的变形题。

      题目描述

      给定一个字符串,按照字符的出现频率降序排列。

      输入输出样例

      Input: "tree"
      Output: "eert"
       
      Explanation:
      'e' appears twice while 'r' and 't' both appear once.
      So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
      

      题解

      和桶排序思路相同,只不过第二步从对桶的排序变成了对原数组的排序

      private static String sortCharacterByFreq(String str){
              HashMap<Character, Integer> map = new HashMap<>();
              for (char c: str.toCharArray()) {
                  map.put(c, map.containsKey(c)? map.get(c)+1: 0);
              }
              // 原字符串转Character[]
              Character[] keys = str.chars().mapToObj(c->(char)c).toArray(Character[]::new);
              StringBuilder stringBuilder = new StringBuilder();
              Arrays.stream(keys).sorted(new Compare(map)).forEach(stringBuilder::append);
              return stringBuilder.toString();
          }
      
          // 按字符出现的频率降序排列
          private static class Compare implements Comparator<Character>{
              private final HashMap<Character, Integer> map;
              public Compare(HashMap<Character, Integer> map) {
                  this.map = map;
              }
      
              @Override
              public int compare(Character o1, Character o2) {
                  return map.get(o2)-map.get(o1);
              }
          }
      

      进阶难度

      1. Sort Colors(Medium)

        经典的荷兰国旗问题,考察如何对三个重复且打乱的值进行排序。

        题目描述

        给一个数组,里面有红白蓝三种颜色,分别使用0,1,2来表示。要求对它进行排序,相同颜色需要挨着,不同颜色需要间隔。不能使用库的sort方法。

        样例输入输出

        input: [2,0,2,1,1,0]
        output: [0,0,1,1,2,2]
        

        题解

        和上一个题目相同,只是从字符变成了指定的数字0,1,2。

        private static int[] sortColors(int[] colors){
                HashMap<Integer, Integer> map = new HashMap<>();
                for(int color: colors){
                    map.put(color, map.containsKey(color)? map.get(color)+1: 1);
                }
                int[] res = new int[colors.length];
                int index=0;
                for(int i=0;i<=2;i++){
                    for(int j=0;j<map.get(i);j++){
                        res[index] = i;
                        index++;
                    }
                }
                return res;
            }
        
  • 相关阅读:
    数据以Excel形式导出导服务器,再将文件读取到客户端另存 以HSSFWorkbook方式实现
    PL/SQL常用设置 可看引用位置更清晰直观 引自:http://blog.csdn.net/xiaoqforever/article/details/27695569
    Spring定时任务配置
    Log4日志配置及使用
    数据库 知识
    Magic-Club开发—第六天
    Magic-Club原型设计--第五天
    Magic-Club原型设计—第四天
    Magic-Club原型设计--第三天
    Magic-Club原型设计--第二天
  • 原文地址:https://www.cnblogs.com/pangqianjin/p/14257356.html
Copyright © 2020-2023  润新知