1. 选择排序(Selection Sort)

基本思想:

首先找到数组中最小的那个元素,将它和数组的第一个元素交换位置。然后在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序

算法分析:

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

稳定性:不稳定排序

public class SelectIonSort {
    public SelectIonSort() {
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        selectionSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void selectionSort(int[] arr) {
        for(int i = 0; i < arr.length - 1; ++i) {
            int min_index = i;
            for(int j = i + 1; j < arr.length; ++j) {
                if (arr[j] < arr[min_index]) {
                    min_index = j;
                }
                swap(arr, i, min_index);
            }
        }
    }
}

2. 直接插入排序(Insertion Sort)

基本思想:

把第一个元素作为有序部分,从第二个元素开始将剩余元素逐个插入有序部分的合适位置,最终得到排序的序列。

算法分析:

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

稳定性:稳定排序

public class InsertSort {

    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    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);
            }
        }
    }
}

3. 冒泡排序(Bubble Sort)

基本思想:

把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置……对每一对相邻元素执行同样操作,一趟比较完成后,排在最右的元素便是最大的数。除去最右的元素,对剩余的元素做同样的工作,最终得到排序序列

算法分析:

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

稳定性:稳定排序

public class BubbleSort {
    public static void bubbleSort(int[] array) {
        for(int i = 0; i < array.length - 1; ++i) {
            for(int j = 0; j < array.length - 1 - i; ++j) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;   }}}    
                    } 
    public static void main(String[] args) {
        int[] arr = new int[]{1, 6, 2, 2, 5};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

4. 希尔排序(Shell Sort)

基本思想:

希尔排序可以说是插入排序的一种变种。无论是插入排序还是冒泡排序,如果数组的最大值刚好是在第一位,要将它挪到正确的位置就需要 n - 1 次移动。也就是说,原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置,这样是相对比较花时间了。

希尔排序就是为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。

算法分析:

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

稳定性:不稳定排序

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void shellSort(int[] arr) {
        for(int gap = arr.length / 2; gap > 0; gap /= 2) {
            for(int i = gap; i < arr.length; ++i) {
                for(int j = i; j >= gap && arr[j] < arr[j - gap]; j -= gap) {
                    swap(arr, j, j - gap);
                }
            }
        }
    }
}

5. 归并排序(Merge Sort)

基本思想:

归并排序是建立在归并操作上的一种有效的排序算法。该算法是分治法(Divide and Conquer)的典型应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式。

从下往上(循环):将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直到合并成一个数列为止。这样就得到了最终的排序结果。

从上往下(递归):它基本包括3步:

分解:将当前区间一分为二,即求分裂点 mid = (low + high) / 2; 求解:递归地对两个子区间 [low…mid] 和 [mid+1…high] 进行归并排序。递归的终结条件是子区间长度为1; 合并:将已排序的两个子区间 [low…mid] 和 [mid+1…high] 归并为一个有序的区间 [low…high]

算法分析:

时间复杂度: O ( n ∗ l o g n ) O(n*logn) O(n∗logn)

空间复杂度: O ( n ) O(n) O(n)

稳定性:稳定排序

public class MergeSort {
    public static void main(String[] args) {
        int[] array = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        mergeSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }
    public static void mergeSort(int[] array, int low, int height) {
        if (low < height) {
            int mid = low + height >>> 1;
            mergeSort(array, low, mid);
            mergeSort(array, mid + 1, height);
            merge(array, low, mid, height);
        }
    }
    public static void merge(int[] array, int low, int mid, int height) {
        int s1 = low;
        int s2 = mid + 1;
        int[] ret = new int[height - low + 1];
        int var7 = 0;
        while(s1 <= mid && s2 <= height) {
            if (array[s1] <= array[s2]) {
                ret[var7++] = array[s1++];
            } else {
                ret[var7++] = array[s2++];
            }
        }
        while(s1 <= mid) {
            ret[var7++] = array[s1++];
        }
        while(s2 <= height) {
            ret[var7++] = array[s2++];
        }
        for(int j = 0; j < ret.length; ++j) {
            array[j + low] = ret[j];
        }
    }
}

6. 快速排序(Quick Sort)

基本思想:

从数组中选择一个元素,称为中轴元素。把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置的是有序的。也就是说,我们无需再移动中轴元素的位置。

以中轴元素为界把大的数组切割成两个小的数组(分割操作,partition),且两个数组都不包含中轴元素。对中轴元素左右两边的数组进行递归操作,直到数组的大小为1。此时每个元素都处于有序的位置。

算法分析:

时间复杂度: O ( n ∗ l o g n ) O(n*logn) O(n∗logn)

空间复杂度: O ( l o g n ) O(logn) O(logn)

稳定性:不稳定排序

public class QuickSort {
    public static void quickSort(int[] arrays, int left, int right) {
        if (left <= right) {
            int l = left;
            int r = right;
            int pivot = arrays[left];
            boolean var6 = false;
            while(l < r) {
                while(pivot <= arrays[r] && l < r) {
                    --r;
                }
                while(pivot >= arrays[l] && l < r) {
                    ++l;
                }
                if (l <= r) {
                    int temp = arrays[r];
                    arrays[r] = arrays[l];
                    arrays[l] = temp;
                }
            }
            arrays[left] = arrays[l];
            arrays[l] = pivot;
            quickSort(arrays, left, l - 1);
            quickSort(arrays, l + 1, right);
        }
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }
}

7. 堆排序(Heap Sort)

基本思想:

堆的特点就是堆顶的元素是一个最值,大顶堆的堆顶是最大值,小顶堆则是最小值。

堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,我们再把堆中剩余的元素再次构成一个大顶堆,然后再把堆顶元素与最后第二个元素交换….如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序的了。

算法分析:

时间复杂度: O ( n ∗ l o g n ) O(n*logn) O(n∗logn)

空间复杂度: O ( 1 ) O(1) O(1)

稳定性:不稳定排序

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void heapSort(int[] arr) {
        int i;
        for(i = arr.length / 2 - 1; i >= 0; --i) {
            heapify(arr, i, arr.length - 1);  }  
        for(i = arr.length - 1; i > 0; --i) {
            swap(arr, 0, i);
            heapify(arr, 0, i - 1);    }
        }
    public static void heapify(int[] arr, int i, int last_index) {
        int max = i;
        if (2 * i + 1 <= last_index) {
            max = 2 * i + 1;       } 
        if (2 * i + 2 <= last_index && arr[2 * i + 2] > arr[max]) {
            max = 2 * i + 2;     }   
        if (max != i) {
            swap(arr, i, max);
            heapify(arr, max, last_index);      }    } 
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

8. 计数排序(Counting Sort)

基本思想:

计数排序适合于最大值和最小值的差值不是很大的情况。

把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,例如 temp[i] = m, 表示元素 i 一共出现了 m 次。最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。

算法分析:

时间复杂度: O ( n + k ) O(n+k) O(n+k),其中k为临时数组大小

空间复杂度: O ( k ) O(k) O(k)

稳定性:稳定排序

public class CountSort {
    public static void countSort(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        int[] count = new int[max + 1];
        for (int i = 0; i < arr.length; i++) {
            count[arr[i]]++;
        }
        int k = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[k++] = i;
                count[i]--;
            }
        }
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        CountSort.countSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

9. 桶排序(Bucket Sort)

基本思想:

桶排序就是把最大值和最小值之间的数进行瓜分,例如分成 10 个区间,10个区间对应10个桶,我们把各元素放到对应区间的桶中去,再对每个桶中的数进行排序,可以采用归并排序、快速排序等方法。之后每个桶里面的数据就是有序的了,按顺序遍历各桶即可得到排序序列。桶排序也可用于浮点数排序。

算法分析:

时间复杂度: O ( n + k ) O(n+k) O(n+k)

空间复杂度: O ( k ) O(k) O(k)

稳定性:稳定排序

public class BucketSort {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 6, 8, 78, 9, 48, 8, 56};
        CountSort.countSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void bucketSort(int[] arr) {
        int max = arr[0];
        int min;
        for(min = 1; min < arr.length; ++min) {
            if (arr[min] > max) {
                max = arr[min];
            }
        }
        min = arr[0];
        for(int i = 1; i < arr.length; ++i) {
            if (arr[i] < min) {
                min = arr[i];
            }
        }
        ArrayList<ArrayList<Integer>> buckets = new ArrayList();
        int count = (max - min) / arr.length + 1;

        int k;
        for(k = 0; k < count; ++k) {
            buckets.add(new ArrayList());
        }
        for(k = 0; k < arr.length; ++k) {
            ((ArrayList)buckets.get((arr[k] - min) / arr.length)).add(arr[k]);
        }
        for(k = 0; k < buckets.size(); ++k) {
            Collections.sort((List)buckets.get(k));
        }
        k = 0;
        for(int i = 0; i < buckets.size(); ++i) {
            ArrayList<Integer> list = (ArrayList)buckets.get(i);
            for(int j = 0; j < list.size(); ++j) {
                arr[k++] = (Integer)list.get(j);
            }
        }
    }
}

10. 基数排序(Radix Sort)

基本思想:

先以个位数的大小来对数据进行排序,接着以十位数的大小来多数进行排序,接着以百位数的大小……

排到最后,就是一组有序的元素了。在以某位数进行排序的时候,是用“桶”来排序的。

由于某位数(个位/十位….,不是一整个数)的大小范围为0~9,所以我们需要10个桶,然后把具有相同数值的数放进同一个桶里,之后再把桶里的数按照0号桶到9号桶的顺序取出来。一趟下来按照某位数的排序就完成了。

算法分析

时间复杂度: O ( k ∗ n ) O(k*n) O(k∗n)

空间复杂度: O ( k + n ) O(k+n) O(k+n)

稳定性:稳定排序

public class RadixSort {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 322, 68, 8, 78, 9, 48, 8, 569, 999, 148, 56};
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void radixSort(int[] arr) {
        int max = arr[0];
        int exp;
        for(exp = 1; exp < arr.length; ++exp) {
            if (arr[exp] > max) {
                max = arr[exp]; }
           
        }
        for(exp = 1; max / exp > 0; exp *= 10) {
            int[] buckets = new int[10];
            int[] temp = new int[arr.length];
            int i;
            for(i = 0; i < arr.length; ++i) {
                ++buckets[arr[i] / exp % 10];
            }
            for(i = 1; i < buckets.length; ++i) {
                buckets[i] += buckets[i - 1];
            }
            for(i = arr.length - 1; i >= 0; --i) {
                temp[buckets[arr[i] / exp % 10] - 1] = arr[i];
                --buckets[arr[i] / exp % 10];
            }
            for(i = 0; i < temp.length; ++i) {
                arr[i] = temp[i];
            }
        }
    }
}