5.希尔排序
排序思路:又称缩小增量排序,给定一个数值gap,将原数组按照gap分为若干个组,组内再进行插入排序,直到gap=1,整个数组已经被调整的接近有序,最后再全集和上进行一次插入排序即可; 时间复杂度:O(n^1.3~1.5) 稳定性:不稳定
排序思路:
1.将待排序数组按gap分组 2.将分组后的数组组内进行插入排序,最后再合并 3.继续以上操作,直至gap=1 4.最后gap=1,整个数组已经接近有序,最后在数组内进行一次插入排序,整个数组的排序完成
代码实现:
public static void shellSort(int[] arr){
int gap =arr.length>>1;
while (gap>1){
insertSortByGap(arr,gap);
gap /=2;
}
insertSort(arr);
}
public static void insertSort(int[] arr){
for (int i=1;i<arr.length;i++){
for (int j=i;j>0 &&arr[j]<arr[j-1];j--){
swap(arr,j,j-1);
}
}
}
private static void insertSortByGap(int[] arr, int gap) {
for (int i=gap;i<arr.length;i++){
for (int j=i; j-gap >=0 && arr[j]<arr[j-gap]; j-=gap){
swap(arr,j,j-gap);
}
}
}
private static void swap(int[] arr, int i, int j) {
int temp =arr[i];
arr[i] =arr[j];
arr[j]=temp;
}
6. 归并排序
排序思路:归并排序,分为两个过程,一是归而为一,就是将原数组不断拆分,直至数组元素个数为1;二是合而为整,就是将拆分的数组不断合并,同时合并的过程中将数组有序化。 时间复杂度:O(nlogN) 稳定性:稳定 排序过程:
代码实现:
public static void mergeSort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
private static void mergeSortInternal(int[] arr, int l, int r) {
if (r-l <=15){
insertSort(arr,l,r);
}
int mid =l+((r-l)>>1);
mergeSortInternal(arr,l,mid);
mergeSortInternal(arr,mid+1,r);
if (arr[mid] >arr[mid+1]){
merge(arr,l,mid,r);
}
}
private static void insertSort(int[] arr, int l, int r) {
for (int i=l+1;i<=r;i++){
for (int j=i; j>l&& arr[j]<arr[j-1];j--){
swap(arr,j,j-1);
}
}
}
private static void swap(int[] arr, int i, int j) {
int temp =arr[i];
arr[i] =arr[j];
arr[j]=temp;
}
private static void merge(int[] arr, int l, int mid, int r) {
int[] temp=new int[r-l+1];
for (int i=0;i<temp.length;i++){
temp[i] =arr[i+l];
}
int i=l;
int j=mid+1;
for (int k=l;k<=r;k++){
if ( i >mid){
arr[k]=temp[j-l];
j++;
}else if (j > r){
arr[k] =temp[i-l];
i++;
}else if (temp[i-l] <= temp[j-l]){
arr[k]=temp[i-l];
i++;
}else {
arr[k] =temp[j-l];
j++;
}
}
7.快速排序
时间复杂度:O(nlogN) 稳定性:不稳定
排序思路:
<2.1>如果当前遍历到的元素arr[i] > v 时,i++,红色区域增加即可; i++后,就把大于等于v的元素加入到红色区域,i继续向后遍历; <2.2>如果在遍历的过程中,arr[i] <v 时:
for (int i = l+1; i <=r ; i++) {
if (arr[i] <val){
swap(arr,i,j+1);
j++;
}
}
- 3.整个集合扫描完毕,整个区间就被我们分隔为以下情况:
4.经过以上步骤,arr[j]左侧元素均小于v,arr[j]之后的元素均 大于等于v,分区完成。继续在小于v的区间和大于v的区间继续递归上述过程,直至整个集合有序。
代码实现:
public class QuickSort {
public static void quickSort(int[] arr){
quickSortInternal(arr,0,arr.length-1);
}
private static void quickSortInternal(int[] arr, int l, int r) {
if (r-l<=15){
insertSort(arr,l,r);
return;
}
int p=partition(arr,l,r);
quickSortInternal(arr,l,p-1);
quickSortInternal(arr,p+1,r);
}
private static int partition(int[] arr, int l, int r) {
int val=arr[l];
int j=l;
for (int i = l+1; i <=r ; i++) {
if (arr[i] <val){
swap(arr,i,j+1);
j++;
}
}
swap(arr,j,l);
return j;
}
private static void swap(int[] arr, int i, int j ) {
int temp=arr[i];
arr[i] =arr[j];
arr[j] = temp;
}
private static void insertSort(int[] arr, int l, int r) {
for (int i=l+1;i<=r;i++){
for (int j=i; j>l&& arr[j]<arr[j-1];j--){
swap(arr,j,j-1);
}
}
}
}
8.算法性能测试
以上算法中,有的时间复杂度是O(N^2),有的则是O(NlogN),这两种时间复杂度的算法,在实际应用中的性能到底是怎样的呢?下面,我们设计算法性能测试用例,来实际感受一下不同算法性能上的优略。
- 定义一个算法测试辅助类,包含生成随机数组、判断数组是否有序、深拷贝数组等方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
public class SortHelper {
private static final ThreadLocalRandom random =ThreadLocalRandom.current();
public static int[] generateRandomArray(int n,int left,int right ){
int[] arr =new int[n];
for (int i = 0; i < arr.length; i++) {
arr[i]=random.nextInt(left,right);
}
return arr;
}
public static int[] generateSortedArray(int n,int times){
int[] arr =new int[n];
for (int i = 0; i < arr.length; i++) {
arr[i]=i;
}
for (int j = 0; j < times; j++) {
int a=random.nextInt(n);
int b=random.nextInt(n);
int temp =arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
return arr;
}
public static void testSort(String sortName,int[] arr){
Class<SevenSort> cls =SevenSort.class;
try {
Method method =cls.getDeclaredMethod(sortName,int[].class);
long start =System.nanoTime();
method.invoke(null,arr);
long end =System.nanoTime();
if (isSorted(arr)){
System.out.println(sortName+"排序结束,共耗时:"+(end-start)/1000000.0+"ms");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static int[] arrCopy(int[] arr){
return Arrays.copyOf(arr,arr.length);
}
public static boolean isSorted(int[] arr){
for (int i = 0; i < arr.length-1 ; i++) {
if (arr[i]>arr[i+1]){
System.out.println("Sort error");
return false;
}
}
return true;
}
}
public class SortTest {
public static void main(String[] args) {
int n=50000;
int[] arr =SortHelper.generateRandomArray(n,0,Integer.MAX_VALUE);
int[] arrCopy1 =SortHelper.arrCopy(arr);
int[] arrCopy2 =SortHelper.arrCopy(arr);
int[] arrCopy3 =SortHelper.arrCopy(arr);
int[] arrCopy4 =SortHelper.arrCopy(arr);
int[] arrCopy5 =SortHelper.arrCopy(arr);
int[] arrCopy6 =SortHelper.arrCopy(arr);
SortHelper.testSort("heapSort",arrCopy2);
SortHelper.testSort("bubbleSort",arrCopy3);
SortHelper.testSort("selectionSort",arrCopy1);
SortHelper.testSort("selectionSortOP",arrCopy4);
SortHelper.testSort("insertionSort",arrCopy5);
SortHelper.testSort("insertionSortBS",arrCopy6);
}
}
- 测试结果
简单总结一下,在长度为50000的数组,数值在【0~整形最大值】的数组上进行排序测试,O(N^2)的算法和O(NlogN)算法性能上确实是差了很多,包括优化后的排序算法也比优化前的性能好了很多,故在某些场景下,选择合适的排序算法才是成功之道。
|