常用排序算法
1、冒泡排序
冒泡排序每次都将数组中的相邻元素进行比较,每一趟排序之后,都会有一个元素有序.
时间复杂度:
最好 | 平均 | 最坏 |
---|---|---|
O(n) | O(n^2) | O(n^2) |
数据有序 | 数据逆序 |
空间复杂度:O(1)
稳定性:稳定
public static void bubbleSort(int[] array) {
int len = array.length;
for(int i = 0;i < len - 1;i++) {//比较趟数
boolean flg = true; //标记该躺是否发生交换
for(int j = 0;j < len - 1 - i;j++) { //每比较一次都有一个元素有序,减去i
if(array[j] > array[j + 1]) {
flg = false; //发生交换则改为false
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
//发生交换说明还有元素无序,没有发生交换说明已经有序,可以直接退出
if(flg) {break;}
}
System.out.println("冒泡");
System.out.println(Arrays.toString(array));
}
2、选择排序
选择排序是最简单的排序,每次拿出一个元素与其余元素比较,然后交换.
时间复杂度:O(n^2) 选择排序对数据不敏感,不管数据量多少,时间复杂度都是n^2
空间复杂度:O(1)
稳定性:不稳定
public static void selectSort(int[] array) {
for(int i = 0;i < array.length;i++) {
for(int j = i + 1;j < array.length;j++) { //j从i+1开始
if(array[j] < array[i]) {
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
}
3、插入排序
插入排序每次都把第一个元素当作有序,然后从后一个开始往回逐个比较排序。
时间复杂度:
最好 | 平均 | 最坏 |
---|---|---|
O(n) | O(n^2) | O(n^2) |
数据有序 | 数据逆序 |
空间复杂度:O(1)
稳定性:稳定
public static int[] insertSort(int[] array) {
int pre = 0;
int cur = 0;
for(int i = 0;i < array.length - 1;i++) {
cur = array[i + 1]; //默认前一个有序,取后一个
pre = i; //记住前一个下标
while(pre >= 0 && cur < array[pre]) { //后一个比前一个小则交换
array[pre + 1] = array[pre]; //交换
pre--;
}
//此时pre < 0,pre + 1位置赋值
array[pre + 1] = cur;
}
return array;
}
4、希尔排序
希尔排序与插入排序类似,不过希尔排序按照特定的步长进行插入排序。
时间复杂度:
最好 | 平均 | 最坏 |
---|---|---|
O(n) | O(n^1.3) | O(n^2) |
数据有序 | 较难构造 |
空间复杂度:O(1)
稳定性:不稳定
public static int[] shellSort(int[] array){
int gap = array.length / 2;
while(gap > 0) {
for(int i = gap;i < array.length - 1;i++) {
int cur = array[i];
int pre = i - gap;
while(pre >= 0 && cur < array[pre]) {
array[pre + gap] = array[pre];
pre -= gap;
}
array[pre + gap] = cur;
}
gap /=2;
}
return array;
}
5、快速排序
快速排序基于分治的思想,不断找基准。
时间复杂度:
最好 | 平均 | 最坏 |
---|---|---|
O(n*logn) | O(n*logn) | O(n^2) |
空间复杂度:
最好 | 平均 | 最坏 |
---|---|---|
O(logn) | O(logn) | O(n) |
稳定性:不稳定
public static int[] quickSort(int[] array,int left,int right) {
if(left < right) {
int pov = partition(array,left,right);
//递归
quickSort(array,left,pov - 1);
quickSort(array,pov + 1,right);
}
return array;
}
public static int partition(int[] array,int left,int right) {
int key = array[left]; //默认左边为基准开始找
while(left < right) {
if(left < right && array[right] >= key) {right--}
array[left] = array[right];
if(left < right && array[left] <= key) {left++}
array[right] = array[left];
}
array[left] = key;
return left;
}
6、堆排序
堆排序先建堆,升序建最大堆,降序建最小堆
时间复杂度:O(n*logn) 对数据不敏感
空间复杂度:O(1)
稳定性:不稳定
堆排序不了解的看之前写的博客 堆排序图解
public static void adjustDown(int[] array,int parent,int len) { //向下调整
int child = parent * 2 + 1;
while(child < len) { //是否有左孩子
if(child + 1 < len && array[child + 1] > array[child]) { //是否有右孩子并确定最大值
child++;
}
//父亲节点比左右孩子最大值小,则交换
if(array[parent] < array[child]) {
int temp = array[child];
array[child] = array[parent];
array[parent] = temp;
//向下调整关键
parent = child;
child = parent * 2 + 1;
}else {
break;
}
}
}
public static void createHeap(int[] array) { //建堆,从最后一个父亲节点(非叶节点)开始向下调整
for(int i = (array.length - 1 - 1) / 2;i > 0;i--) {
adjustDown(array,i,array.length);
}
}
public static void HeapSort(int[] array) {
create(array);
int len = array.length - 1;
while(len > 0) { //每次都将最一个元素与堆顶元素比较然后交换
if(array[len] < array[0]) {
int temp = array[0];
array[0] = array[len];
array[len] = temp;
//交换之后向下调整
adjustDown(array,0,len);
len--;
}
}
}
7、归并排序
时间复杂度:O(n*logn) 数据不敏感
空间复杂度:O(n) 数据不敏感
归并排序也是分治法的思想,不断地将原数据分为两部分,直到不划分为止,开始对两部分元素进行排序合并。
public static int[] MergeSort(int[] array) {
if(array.length < 2) return array;
int mid = array.length / 2;
//分成左右两部分
int[] left = Arrays.copyOfRange(array,0,mid);
int[] right = Arrays.copyOfRange(array,mid,array.length);
//对左右两部分进行递归合并
return Merge(MergeSort(left),MergeSort(right));
}
public static int[] Merge(int[] left,int[] right) {
int[] result = new int[left.length + right.length];
for(int index = 0,i = 0,j = 0;index < result.length;index++) {
if(i >= left.length) { //左边走完,直接将右边的放入result
result[index] = right[j++];
}else if(j >= right.length) { //右边走完,直接将左边的放入result
result[index] = left[i++];
}else if(left[i] > right[j]) { //左右都没走完,比较然后放入result
result[index] = right[j++];
}else {
result[index] = left[i++];
}
}
return result;
}