简介
- 内部排序:待排序列完全存放在内存中所进行的排序
![内部排序](https://img-blog.csdnimg.cn/20200813195109516.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FtYXppbmdfY3hi,size_16,color_FFFFFF,t_70#pic_center)
| 平均时间性能 | 最好时间性能 | 最坏时间性能 | 辅助空间 | 稳定性 |
---|
直接插入排序 | O(n2) | O(n) | O(n2) | O(1) | √ |
希尔排序 | O(n1.3) | O(n) | O(n2) | O(1) | × |
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | √ |
快速排序 | O(nlog2n) | O(nlog2n) | O(n2) | O(log2n) | × |
简单选择排序 | O(n2) | O(n2) | O(n2) | O(1) | × |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | × |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | √ |
基数排序 | O(d(n+rd)) | O(d(n+rd)) | O(d(n+rd)) | O(rd) | √ |
- 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归并排序
- 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于希尔排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序
- 从稳定性看:直接插入排序、冒泡排序和归并排序是稳定的;而希尔排序、直接选择排序、快速排序和堆排序是不稳定排序
- 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序
插入排序
- 基本思想:每次将一个待排序的元素,按其关键字的大小插入到前面已经排好序的序列的适当位置,直到全部记录插入完成为止
直接插入排序
- 空间复杂度O(1)
- 平均时间复杂度O(n2)
- 最坏时间复杂度O(n2)
- 稳定排序
- 基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
public class InsertSort {
public static void insertSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 1; i < arrayLength; i++) {
int temp = data[i];
if (data[i] - data[i - 1] < 0) {
int j = i - 1;
for (; j >= 0 && data[j] - temp > 0; j--) {
data[j + 1] = data[j];
}
data[j + 1] = temp;
}
System.out.println(java.util.Arrays.toString(data));
}
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
insertSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
折半插入排序
- 空间复杂度O(1)
- 它的思想是利用折半查找找出插入的位置
public class Demo2 {
public static void binaryInsertSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 1; i < arrayLength; i++) {
int temp = data[i];
int low = 0;
int high = i - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (temp > data[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
for (int j = i; j > low; j--) {
data[j] = data[j - 1];
}
data[low] = temp;
System.out.println(java.util.Arrays.toString(data));
}
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
binaryInsertSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
希尔排序
- 空间复杂度O(1)==
- 平均时间复杂度O(n1.3)
- 最坏时间复杂度O(n2)
- 不稳定
- 思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上有较大提高
import java.util.Arrays;
public class Demo3 {
public static void main(String[] args) {
int[] a = {3,4,6,3,5,1,8,5,3,1};
int temp;
int j;
for(int dk = a.length /2 ; dk >=1 ; dk = dk /2){
for (int i = dk ; i < a.length ; i++){
if(a[i] < a[i - dk]){
temp = a[i];
for (j = i - dk; j >= 0 && temp < a[j] ; j -= dk){
a[j+dk] = a[j];
}
a[j+dk] = temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
交换排序
- 通过排序表中两个记录关键码的比较,若与排序要求相逆(不符合升序或降序),则将两者交换
冒泡排序
- 空间复杂度O(1)
- 平均时间复杂度O(n2)
- 最坏时间复杂度O(n2)
- 稳定
- 思想:通过对待排序序列从前向后,依次比较相邻元素的排序码,若发现逆序则交换,使排序码较大的元素逐渐从前部移向后部
public class Demo4 {
public static void bubbleSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 0; i < arrayLength - 1; i++) {
boolean flag = false;
for (int j = 0; j < arrayLength - 1 - i; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j + 1];
data[j + 1] = data[j];
data[j] = temp;
flag = true;
}
}
System.out.println(java.util.Arrays.toString(data));
if (!flag) {
break;
}
}
}
public static void main(String[] args) {
int[] data = {9, -16, 21, 23, -30, -49, 21, 30, 30};
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
bubbleSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
快速排序
- 最好空间复杂度O(log2n)
- 最坏空间复杂度O(n) 单支树
- 平均时间复杂度O(nlog2n)
- 最坏时间复杂度O(n2)
- 不稳定
- 思想:任取待排序序列中的某个元素作为标准(也称为支点、界点,一般取第一个元素),通过一次划分,将待排元素分为左右两个子序列,左子序列元素的排序码均小于基准元素的排序码,右子序列的排序码则大于或等于基准元素的排序码,然后分别对两个子序列继续进行划分,直至每一个序列只有一个或零个元素为止。最后得到的序列便是有序序列
public class QuickSort {
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);
subSort(data, high + 1, end);
}
}
public static void quickSort(int[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
选择排序
- 将待排序的元素分为已排序(初始为空)和未排序两组,依次将未排序的元素中值最小的元素放入已排序的组中
简单选择排序
- 空间复杂度O(1)
- 平均时间复杂度O(n2)
- 最坏时间复杂度O(n2)
- 不稳定
- 思想:在一组元素R[i]到R[n]中选择具有最小关键码的元素,若它不是这组元素中的第一个元素,则将它与这组元素中的第一个元素对调。除去具有最小关键字的元素,在剩下的元素中重复,直到剩余元素只有一个为止
public class SelectSort {
public static void selectSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 0; i < arrayLength - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arrayLength; j++) {
if (data[minIndex] > data[j]) {
minIndex = j;
}
}
if(minIndex != i){
int temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
System.out.println(java.util.Arrays.toString(data));
}
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
selectSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
堆排序(树形选择排序)
- 空间复杂度O(1)
- 时间复杂度O(nlog2n)
- 不稳定
- 思想:利用完全二叉树的性质完成排序。
public class HeapSort {
public static void heapSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 0; i < arrayLength - 1; i++) {
buildMaxdHeap(data, arrayLength - 1 - i);
swap(data, 0, arrayLength - 1 - i);
System.out.println(java.util.Arrays.toString(data));
}
}
private static void buildMaxdHeap(int[] data, int lastIndex) {
for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
int k = i;
while (k * 2 + 1 <= lastIndex) {
int biggerIndex = 2 * k + 1;
if (biggerIndex < lastIndex) {
if (data[biggerIndex] - data[biggerIndex + 1] < 0) {
biggerIndex++;
}
}
if (data[k] - data[biggerIndex] < 0) {
swap(data, k, biggerIndex);
k = biggerIndex;
} else {
break;
}
}
}
}
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
heapSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
归并排序
2路归并排序
- 空间复杂度O(n)
- 时间复杂度O(nlog2n)
- 稳定
- 思想:利用递归是先两两比较,合并后再两两比较
public class MergeSort {
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
private static void sort(int[] data, int left, int right) {
if(left < right){
int center = (left + right)/2;
sort(data,left,center);
sort(data,center+1,right);
merge(data,left,center,right);
}
}
private static void merge(int[] data, int left, int center, int right) {
int[] tempArr = new int[data.length];
int mid = center + 1;
int third = left;
int temp = left;
while (left <= center && mid <= right) {
if (data[left] - data[mid] <= 0) {
tempArr[third++] = data[left++];
} else {
tempArr[third++] = data[mid++];
}
}
while (mid <= right) {
tempArr[third++] = data[mid++];
}
while (left <= center) {
tempArr[third++] = data[left++];
}
while (temp <= right) {
data[temp] = tempArr[temp++];
}
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
mergeSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
基数排序(MSD、LSD)
- 空间复杂度O(n+j)
- 时间复杂度O(n+j)
- 稳定
- 思想:利用多个队列保证稳定,先比较个位,再比较十位,以此类推。以下代码是借鉴别人的。
public class MultiKeyRadixSort {
public static void radixSort(int[] data, int radix, int d) {
System.out.println("开始排序:");
int arrayLength = data.length;
int[] temp = new int[arrayLength];
int[] buckets = new int[radix];
for (int i = 0, rate = 1; i < d; i++) {
Arrays.fill(buckets, 0);
System.arraycopy(data, 0, temp, 0, arrayLength);
for (int j = 0; j < arrayLength; j++) {
int subKey = (temp[j] / rate) % radix;
buckets[subKey]++;
}
for (int j = 1; j < radix; j++) {
buckets[j] = buckets[j] + buckets[j - 1];
}
for (int m = arrayLength - 1; m >= 0; m--) {
int subKey = (temp[m] / rate) % radix;
data[--buckets[subKey]] = temp[m];
}
System.out.println("对" + rate + "位上子关键字排序:"
+ java.util.Arrays.toString(data));
rate *= radix;
}
}
public static void main(String[] args) {
int[] data = { 1100, 192, 221, 12, 13 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
radixSort(data, 10, 4);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
桶排序
public class BucketSort {
public static void bucketSort(int[] data, int min, int max) {
System.out.println("开始排序");
int arrayLength = data.length;
int[] temp = new int[arrayLength];
int[] buckets = new int[max - min];
for (int i = 0; i < arrayLength; i++) {
buckets[data[i] - min]++;
}
System.out.println(Arrays.toString(buckets));
for (int i = 1; i < max - min; i++) {
buckets[i] = buckets[i] + buckets[i - 1];
}
System.out.println(Arrays.toString(buckets));
System.arraycopy(data, 0, temp, 0, arrayLength);
for (int k = arrayLength - 1; k >= 0; k--) {
data[--buckets[temp[k] - min]] = temp[k];
}
}
public static void main(String[] args) {
int[] data = { 9, 5, -1, 8, 5, 7, 3, -3, 1, 3 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
bucketSort(data, -3, 10);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
数据结构之外部排序