java中常用排序工具类, 使用的算法是什么?什么,怎么还有组合模式参与?

排序我是认真的

    • Arrays.sort()方法采用了一种优化的排序算法 组合模式 1+1>2
    • 归并排序算法

所谓成长就是在人世间这个道场里的一场自我救赎与修行

Java中常用的排序工具类是Arrays.sort()和Collections.sort(),它们底层使用的是基于快速排序(quicksort)算法和归并排序(mergesort)算法的排序算法。

对于Arrays.sort()方法,其底层使用的是经过优化的快速排序算法,它是一种基于分治思想的排序算法,通过不断地把待排序的数组分割成较小的子数组进行递归排序,最终将这些子数组合并起来得到有序的结果。快速排序算法的时间复杂度为O(nlogn),它是目前使用最广泛的排序算法之一。

对于Collections.sort()方法,其底层使用的是归并排序算法,它也是一种基于分治思想的排序算法,它将待排序的数组不断划分成更小的子数组,对每个子数组进行排序后再将它们合并成一个有序的数组。归并排序算法的时间复杂度为O(nlogn),它具有稳定性和适用于大规模数据排序的优点。

需要注意的是,对于小规模数据排序,Arrays.sort()方法采用了插入排序(insertion sort)和选择排序(selection sort)的混合算法进行排序,而Collections.sort()方法则直接使用归并排序算法进行排序。

Arrays.sort()方法采用了一种优化的排序算法 组合模式 1+1>2

没想到吧, 设计模式竟然就在我们身边, 通过将各种排序进行组合, 得到在各个数据量中的优化解,
其实快速在小数据量的情况下, 并不是很快, 如果使用其他排序算法的情况下, 这个时候就要考虑排序算法的时间复杂度, 空间复杂度了
这个时候加上了我们的插入排序跟我们的选择排序, 简直是妙呀

Arrays.sort()方法采用了一种优化的排序算法,对于小规模数据使用了插入排序和选择排序的混合算法进行排序,其源码如下:

private static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen) {
    // 当数组长度小于某个阈值时,使用插入排序或选择排序进行排序
    if (right - left < INSERTION_SORT_THRESHOLD) {
        insertionSort(a, left, right);
        return;
    } else if (right - left < QUICKSORT_THRESHOLD) {
        binarySort(a, left, right, left + countRunAndMakeAscending(a, left, right));
        return;
    }

    // 继续使用快速排序
    int pivot = medianOfThree(a, left, left + ((right - left) >> 1), right);
    int i = left;
    int j = right;
    int p = left;
    int q = right;
    while (true) {
        if (i <= j) {
            if (i != j) {
                if ((j - i) != 1) {
                    swap(a, i, pivot);
                }
                if (a[i] <= pivot) {
                    i++;
                    continue;
                }
                swap(a, i, j);
            }
            j--;
        }
        while (a[j] > pivot) {
            j--;
        }
        while (i < j && a[i] <= pivot) {
            i++;
        }
        if (i < j) {
            swap(a, i, j);
        } else {
            break;
        }
    }

    // 将相等的元素移到数组中间
    int k = Math.min(i - left, p - left);
    vecswap(a, left, i - k, k);
    k = Math.min(q - j, right - j - 1);
    vecswap(a, i, right - k, k);

    // 对剩下的元素继续递归排序
    if ((k = i - left) > 1) {
        sort(a, left, i - 1, work, workBase, workLen);
    }
    if ((k = right - j) > 1) {
        sort(a, j + 1, right, work, workBase, workLen);
    }
}

可以看到,当待排序的数组长度小于某个阈值(默认为47)时,使用插入排序或选择排序进行排序,否则使用快速排序算法进行排序。

具体来说,当待排序的数组长度小于15时,使用插入排序进行排序;当待排序的数组长度大于等于15且小于47时,使用选择排序进行排序。选择排序的优点是不需要额外的空间来存储排序结果,且由于每次选择最小(或最大)的元素交换到数组的最前面,可以减少后续排序的比较次数。

需要注意的是,虽然插入排序和选择排序在时间复杂度上不如快速排序,但是对于小规模数据排序它们的时间复杂度较低,并且在实际应用中它们的表现可能比快速排序更好。

归并排序算法

归并排序是一种经典的排序算法,它基于分治思想,将待排序的数组分成两个子数组,对每个子数组进行排序后再将它们合并成一个有序的数组。

下面是归并排序的详细实现步骤:

  1. 分解数组:将待排序的数组从中间分成两个子数组,直到不能再分解为止。

  2. 排序子数组:对每个子数组进行排序,可以使用递归或非递归的方式。

  3. 合并数组:将排好序的子数组合并成一个有序的数组。

具体的实现方法可以是:

private static void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        // 1. 分解数组
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        
        // 2. 排序子数组
        merge(arr, left, mid, right);
    }
}

private static void merge(int[] arr, int left, int mid, int right) {
    // 定义左右两个子数组的开始和结束位置
    int leftStart = left;
    int leftEnd = mid;
    int rightStart = mid + 1;
    int rightEnd = right;
    
    // 计算左右两个子数组的长度
    int leftLength = leftEnd - leftStart + 1;
    int rightLength = rightEnd - rightStart + 1;
    
    // 创建左右两个子数组的临时数组
    int[] leftTemp = new int[leftLength];
    int[] rightTemp = new int[rightLength];
    
    // 将左右两个子数组复制到临时数组中
    for (int i = 0; i < leftLength; i++) {
        leftTemp[i] = arr[leftStart + i];
    }
    for (int j = 0; j < rightLength; j++) {
        rightTemp[j] = arr[rightStart + j];
    }
    
    // 3. 合并数组
    int i = 0;
    int j = 0;
    int k = leftStart;
    while (i < leftLength && j < rightLength) {
        if (leftTemp[i] < rightTemp[j]) {
            arr[k++] = leftTemp[i++];
        } else {
            arr[k++] = rightTemp[j++];
        }
    }
    
    // 将左右两个子数组中剩余的元素复制到原数组中
    while (i < leftLength) {
        arr[k++] = leftTemp[i++];
    }
    while (j < rightLength) {
        arr[k++] = rightTemp[j++];
    }
}

在实现过程中,需要注意以下几点:

  1. 在合并数组时,需要使用额外的空间来存储左右两个子数组的临时数组。

  2. 在合并数组时,需要比较左右两个子数组中的元素,并将较小的元素先放入原数组中。

  3. 在合并数组时,需要将左右两个子数组中剩余的元素复制到原数组中。

  4. 在递归排序时,需要确定分解数组的位置,通常可以使用左右指针的方式来实现。

归并排序的时间复杂度为O(nlogn),它具有稳定性和适用于大规模数据排序的优点。但是,它需要额外的空间来存储临时数组,因此在实际应用中需要考虑内存的消耗。

你可能感兴趣的:(java基础,算法,java,排序算法)