常见的排序算法
冒泡排序
两两比较相邻记录的关键字,如果是反序则交换,直到没有反序的记录位置
public static void bubbleSort(int[] arr) {
boolean flag = true;
for(int i = 1; i < arr.length; i ++) {
flag = false;
for(int j = arr.length - 1; j <= i; j++) {
if(arr[j - 1] > arr[j]) {
swap(arr, j - 1, j);
}
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
最好时间复杂度:O(n)
最坏时间复杂度O( n 2 n^2 n2)
平均时间复杂度:O( n 2 n^2 n2)
空间复杂度:O(1)
稳定性:稳定
简单选择排序
通过n - i次关键字之间的比较,从n - i + 1个记录中选出关键字最小的记录,并和第i 个记录交换。
public static void selectSort(int[] arr) {
int min;
for(int i = 1; i < arr.length; i++) {
min = i;
for(int j = i + 1; j < arr.length; j++) {
if(arr[min] > arr[j]) {
min = j;
}
}
if(min != i) {
swap(arr, i, min);
}
}
}
public static void swap(int arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
最好时间复杂度:O( n 2 n^2 n2)
最坏时间复杂度:O( n 2 n^2 n2)
平均时间复杂度:O( n 2 n^2 n2)
空间复杂度:O(1)
稳定性:不稳定
直接插入排序
将n个待排序的元素看成一个有序表和无序表,从无序表中取出一个元素将其插入到有序表的指定位置,重复n-1次完成排序
public static void insertSort(int[] arr) {
int j, tmp;
for(int i = 1; i< arr.length; i++) {
if(arr[i] < arr[i - 1]) {
tmp = arr[i];
for(j = i + 1; arr[j] > tmp; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = tmp;
}
}
}
最优时间复杂度:O( n n n)
最坏时间复杂度:O( n 2 n^2 n2)
平均时间复杂度:O( n 2 n^2 n2)
空间复杂度:O(1)
稳定性:稳定
希尔排序
取一个增量increment作为间隔将带排序列分为increment个子序列,距离increment的元素在同一子序列,对每一个子序列进行
直接插入排序
,然后缩小增量increment并重复上面操作,直到increment = 1
public static void shellSort(int[] arr) {
int j, tmp, increment = arr.length;
do {
for(int i = increment + 1; i < arr.length; i++) {
if(arr[i] < arr[i - increment]) {
tmp = arr[i];
for(j = i - increment; j > 0 && arr[j] > tmp; j -= increment) {
arr[j + increment] = arr[j];
}
arr[j + increment] = tmp;
}
}
} while(increment > 1);
}
最优时间复杂度:O(n)
最坏时间复杂度:O( n 2 n^2 n2)
平均时间复杂度:O( n 1.3 n^{1.3} n1.3)
空间复杂度:O(1)
稳定性:不稳定
归并排序
将初始序列看作是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2或1的有序子序列,再次两两归并,直到得到一个长度为n的有序序列为止
递归实现
- 从上到下递归
public static void mergeSortUp2Down(int[] arr, int start, int end) {
if(arr == null || start >= end) return;
int mid = (start + end) / 2;
mergeSortUp2Down(arr, start, mid);
mergeSortUp2Down(arr, mid + 1, end);
merge(arr, start, mid, end);
}
public static void merge(int[] arr, int start, int mid, int end) {
int[] temp = new int[end - start + 1];
int i = start;
int j = mid + 1;
int k = 0;
while(i <= mid && j <= end) {
if(arr[i] < arr[j]) {
temp[k++] = arr[i++];
}
else {
temp[k++] = arr[j++];
}
}
while(i <= mid) {
temp[k++] = arr[i++];
}
while(j <= end) {
temp[k++] = arr[j++];
}
for(i = 0; i < k; i++) {
arr[start + i] = temp[i];
}
temp = null;
}
- 从下往上递归:
public static void mergeSortDown2Up(int[] arr) {
for(int i = 1; i < arr.length; i *= 2) {
mergeGroup(arr, arr.length, i);
}
}
public static void mergeGroup(int[] arr, int len, int gap) {
int i;
for(i = 0; i + 2 * gap - 1 < len; i += (2 * gap)) {
merge(arr, i, i + gap - 1, i + 2 * gap - 1);
}
if(i + gap - 1 < len - 1) {
merge(arr, i, i + gap - 1, len - 1);
}
}
非递归实现
public static void mergeSortNon(int[] arr) {
int gap = 1;
for(int i = 0 ; i + gap - 1 < arr.length; i += (2 * gap)) {
int start = i, mid = i + gap - 1, end = i + 2 * gap - 1;
if(end > arr.length - 1) {
end = arr.length - 1;
}
merge(arr, start, mid, end);
}
gap *= 2;
}
最优时间复杂度:O( n log 2 n n\log_2 n nlog2n)
最坏时间复杂度:O( n log 2 n n\log_2 n nlog2n)
平均时间复杂度:O( n log 2 n n\log_2 n nlog2n)
空间复杂度:O(n)
稳定性:稳定
堆排序
将待排序序列构建成一个大顶堆,将最大值(堆顶)和序列最后一个元素交换,然后对于剩余的n-1个序列重新构成一个堆,重复操作。
public static void maxHeapSort(int[] arr) {
int len = arr.length - 1;
for(int i = len /2 - 1; i >= 0; i--) {
maxHeapAdjust(arr, i, len);
}
for(int i = len; i >= 0; i--) {
swap(arr, 0, i);
maxHeapAdjust(arr, 0, i - 1);
}
}
public static void maxHeapAdjust(int[] arr, int s, int m) {
int i, temp = arr[s];
for(i = 2 * s; i <= m; i *= 2) {
if(arr[i + 1] > arr[i]) {
++i;
}
if(temp >= arr[i]) {
break;
}
arr[s] = arr[i];
s = i;
}
arr[s] = temp;
}
最优时间复杂度:O( n log 2 n n\log_2 n nlog2n)
最坏时间复杂度:O( n log 2 n n\log_2 n nlog2n)
平均时间复杂度:O( n log 2 n n\log_2 n nlog2n)
空间复杂度:O(1)
稳定性:不稳定
快速排序
从数列中选取一个基数,将比基数小的放在基数前面,比基数大的摆在基数后面,该分区退出后,该基准就处于数列的中间,递归堆基数前子序列和后序列进行上述操作,完成排序
- 递归操作
public static void quickSort(int[] arr, int start, int end) {
int pivot;
if(start < end) {
pivot = Partition(arr, start, end);
Partition(arr, start, pivot - 1);
Partition(arr, pivot + 1, end);
}
}
public static void Partition(int[] arr, int start, int end) {
int pivotkey = arr[start];
while(start < end) {
while((start < end) && (arr[end] >= pivotkey)) {
end--;
}
swap(arr, start, end);
while((start < end) && (arr[start] <= pivotkey)) {
start--;
}
swap(arr, start, end);
}
return low;
}
- 非递归操作
用栈来保存边界值(start和end)
public static void quickSortNon(int[] arr) {
int[] stack = new int[arr.length];
int top = 0;
int start = 0;
int end = arr.length - 1;
int pivok = Partition(arr, start, end);
if(pivot > start + 1) {
stack[top++] = start;
stack[top++] = pivok - 1;
}
if(pivok < end - 1) {
stack[top++] = pivok + 1;
stack[top++] = end;
}
while(top > 0) {
end = stack[--top];
start = stack[--top];
pivok = Partition(arr, start, end);
if(pivok > start + 1) {
stack[top++] = start;
stack[top++] = pivok - 1;
}
if(pivok < end - 1) {
stack[top++] = pivok + 1;
stack[top++] = end;
}
}
}
public static int Partition(int[] arr, int start, int end) {
int pivokkey = arr[start];
while(start < end) {
while((start < end) && (arr[end] >= pivokkey)) {
end--;
}
swap(arr, start, end);
while((start < end) && (arr[start] <= pivokkey)) {
start++;
}
swap(arr, start, end);
}
return start;
}
最优时间复杂度:O( n log 2 n n\log_2 n nlog2n)
最坏时间复杂度:O( n 2 n^2 n2)
平均时间复杂度:O( n log 2 n n\log_2 n nlog2n)
空间复杂度:O(n)
稳定性:不稳定