参考博客:https://blog.csdn.net/hellozhxy/article/details/79911867
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
算法总结:
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 | 是否占用额外内存 |
冒泡 | O(n2) | O(1) | 稳定 | 否 |
插入 | O(n2) | O(1) | 稳定 | 否 |
选择 | O(n2) | O(1) | 不稳定 | 否 |
希尔 | O(nlogn) | O(1) | 不稳定 | 否 |
堆 | O(nlogn) | O(1) | 不稳定 | 否 |
快速 | O(nlogn) | O(logn) | 不稳定 | 否 |
归并 | O(nlogn) | O(n) | 稳定 | 是 |
基数排序 | O(nXk) | O(n+k) | 稳定 | 是 |
冒泡排序法(稳定):元素两两比较,交换位置。经过一轮比较后,最大的元素会出现在最大索引处。
时间复杂度:O(n2) 空间复杂度:O(1)
算法实现:
public static int[] bubbleSort(int[] array){
if (array.length == 0) {
return array;
}
for (int j = 0; j < array.length-1; j++) {
for (int i = 0; i < array.length - 1-j; i++) {
if (array[i] > array[i + 1]) {
//交换位置
int temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
}
}
}
return array;
}
选择排序法(不稳定):从第一个元素开始,去跟后面每一个元素进行比较,交换位置,经过一轮排序后,最小的元素会出现在最小索引处。
时间复杂度: O(n2) 空间复杂度:O(1)
算法实现:
public static int[] selectSort(int[] array){
if(array.length==0){
return array;
}else for (int index = 0; index < array.length-1; index++) {
for (int j = index+1; j <array.length ; j++) {
if(array[index]>array[j]){//找到最小的数
int temp=array[index];
array[index]=array[j];
array[j]=temp;
}
}
}
return array;
}
直接插入排序(稳定):每一次把后面一个元素插入到前一个有序列表中,插入进去后,使之仍保持有序。从第一个元素,取出下一个元素向前扫描,插到所有比他大的数前。
时间复杂度:O(n2) 空间复杂度:O(1)
算法实现:
public static int[] insertoinSort(int[] array){
if(array.length==0){
return array;
}
for (int i = 1; i < array.length; i++) {
int j=i;
while (j>0&&array[j]<array[j-1]){
int temp=array[j];
array[j]=array[j-1];
array[j-1]=temp;
j--;
}
}
return array;
}
快速排序法(不稳定):https://blog.csdn.net/adusts/article/details/80882649
•分治法:比大小,再分区
1.从数组中取出一个数,作为基准数。
2.分区:将比这个数大或等于的数全放到他的右边,小于他的数全放到他的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
元素 | 5 | 3 | 9 | 1 | 6 | 7 | 2 | 4 | 0 | 8 |
坑位 | 坑位1 |
| 坑位3 |
| 坑位5 | 坑位7 | 坑位6 | 坑位4 | 坑位2 |
|
| 0 |
| 4 |
| 2 | 5 | 7 | 6 | 9 |
|
| K1 |
| K3 |
| K5 | K7 | K6 | K4 | K2 |
|
| 0 | 3 | 4 | 1 | 2 | 5 | 7 | 6 | 9 |
|
|
|
|
|
|
|
|
|
|
|
|
时间复杂度:O(nlogn) 空间复杂度:O(logn)
算法实现:
public class QuickSort {
//start 默认是0
//end 是数组长度-1
public static void quickSort(int[] arr, int start, int end) {
if (start < end) {
//获取分区索引
int index = getIndex(arr, start, end);
//对左右两个分区 再进行同样的步骤 ,即是递归调用
quickSort(arr, start, index - 1);//左半部分
quickSort(arr, index + 1, end);//右半部分
}
}
private static int getIndex(int[] arr, int start, int end) {
int i = start;
int j = end;
//定义基准数
int x = arr[i];
//循环
while (i < j) {
//从右往左比较
while (i < j && arr[j] >= x) {
j--;
}
//从右往左找到比基准数小的数了后,填坑
if (i < j) {
//把这个数填到上一个坑位
arr[i] = arr[j];
//让 i++;
i++;
}
//从左往右找
while (i < j && arr[i] < x) {
i++;
}
// 找比基准数大的数,找到后填坑
if (i < j) {
arr[j] = arr[i];
j--;
}
}
//当上面的循环结束后把基准数填到最后一个坑位,也就一基准数为界,分成了左右两部分
arr[i] = x; //把基准数填进去
return i; //返回基准数所在位置的索引
}
}
归并排序(稳定):先分再合
将每一个元素都看成是一个有序序列,两两合并。
采用分治法,将所有的子序列合并成一个完全有序的序列。
时间复杂度:O(nlogn) 空间复杂度:O(n)
算法实现:
public static int[] mergeSort(int[] array){
if (array.length <2) {
return array;
}
//拆分
depart(array,0,array.length-1);
//合并
merge(array,0,(array.length/2)-1,array.length-1);
return array;
}
private static void depart(int[] array, int start, int end) {
int center=(start+end)/2;
if(start<end){
depart(array,start,center);
depart(array,center+1,end);
merge(array,start,center,end);
}
}
private static void merge(int[] array, int startIndex, int centerIndex, int endIndex) {
//定义一个临时数组
int[] tempArr=new int[endIndex-startIndex+1];
//定义左边数组的起始索引
int i=startIndex;
//定义右边数组的起始索引
int j=centerIndex+1;
//定义临时数组的起始索引
int index=0;
//比较左右两个数组的元素大小往临时数组中放
while (i<=centerIndex&&j<=endIndex){
if(array[i]<=array[j]){
tempArr[index]=array[i];
i++;
}else {
tempArr[index]=array[j];
j++;
}
index++;
}
//处理剩余元素
while (i<=centerIndex){
tempArr[index] = array[i];
i++;
index++;
}
while (j <= endIndex) {
tempArr[index] = array[j];
j++;
index++;
}
//将临时数组中的元素放到原数组中
for (int k = 0; k < tempArr.length; k++) {
array[k+startIndex]=tempArr[k];
}
}
希尔排序(不稳定):插入排序的改进,插入排序其实就是增量为1的希尔排序
将数组按照增量X分组,每个子数组中按照插入排序。用下一个增量X/2对子数组再进行分组,直到X=1。
时间复杂度:O(nlogn) 空间复杂度:O(1)
算法实现:
public static int[] shellSort(int[] array){
//定义一个增量
if (array.length == 0) {
return array;
}
//使用克努特序列选择增量 h=1 h=3*h+1
int jiange=1;
while (jiange<=array.length/3){
jiange=jiange*3+1;
}
for (int h = jiange; h > 0; h=(h-1)/3) {
for (int i = h; i < array.length; i++) {
int j = i;
while (j > 0 && array[j] < array[j - h]) {
int temp = array[j];
array[j] = array[j - h];
array[j - h] = temp;
j -= h;
}
}
}
return array;
}
基数排序(稳定):不能比较有负数和0的数组,是一种非比较排序
只需要对元素进行分配与收集进行排序 按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。
时间复杂度:O(nXk)O(n+k)
代码实现:
public static int[] RadixSort(int[] array){
if(array==null||array.length<2){
return array;
}
//确定排序轮次
//获取数组中的最大值
int max=getMax(array);
int len = String.valueOf(max).length();
//定义二维数组,放10个基
int[][] tempArr=new int[10][array.length];
//定义统计数组
int[] counts=new int[10];
//循环轮次
for (int i = 0,n=1; i < len; i++,n*=10) {
for (int j = 0; j < array.length; j++) {
//获取每个位上的数字
int ys =array[j]/n%10;
tempArr[ys][counts[ys]++]=array[j];
}
//取出统计数组中的元素
int index=0;
for (int k = 0; k < counts.length; k++) {
if(counts[k]!=0){
for (int h = 0; h < counts[k]; h++) {
//从统计数组中取出元素放回原数组
array[index]=tempArr[k][h];
index++;
}
counts[k]=0;//清除上一次统计的次数
}
}
}
return array;
}
private static int getMax(int[] array) {
int max=array[0];
for (int i = 1; i < array.length; i++) {
if(array[i]>max){
max=array[i];
}
}
return max;
}
堆排序(不稳定):堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
将待排序的数组构造成一个大顶堆,从最后一个非叶子节点开始转 最大值就是堆顶的根节点 将根节点与末尾元素互换,末尾就是最大值 将剩余的n-1个元素重新构造成一个堆,得到n个元素的次小值。重复
升序用大顶堆 arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2]
降序用小顶堆 arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2]
二叉堆:
#1 - Shape Property: 它是一个完全二叉树。
#2 - Heap Property: 主要分为max-heap property和min-heap property(这就是我以前说过的术语,很重要)
|--max-heap property :对于所有除了根节点(root)的节点 i,A[Parent]≥A[i]A[Parent]≥A[i]
|--min-heap property :对于所有除了根节点(root)的节点 i,A[Parent]≤A[i]
首先将一个数组初始化成一个堆:
Input:
Initialization:
*堆的根节点(root)为A[0];
时间复杂度:O(nlogn)空间复杂度:O(1)
算法实现:
public static int[] HeapSort(int[] array){
//定义开始调整的位置
int startIndex=(array.length-1)/2;
for(int i=startIndex;i>=0;i--){
toMaxHeap(array,array.length,i);
}
//数组已经调整为大顶堆,调换根元素与最后一个元素
for(int i=array.length-1;i>0;i--){
//进行调换
int t=array[0];
array[0]=array[i];
array[i]=t;
//再把剩余元素掉换成大顶堆
toMaxHeap(array,i,0);
}
return array;
}
/**
*
* @param array 要排序的数组
* @param size 调整的元素个数
* @param index 从哪里开始调整
*/
private static void toMaxHeap(int[] array, int size, int index) {
//获取左右子节点的索引
int leftNodeIndex=index*2+1;
int RightNodeIndex = index * 2 + 2;
//查找最大节点所对应的索引
int MaxIndex=index;
if(leftNodeIndex<size&&array[leftNodeIndex]>array[MaxIndex]){
MaxIndex=leftNodeIndex;
}
if (RightNodeIndex<size&&array[RightNodeIndex] > array[MaxIndex]) {
MaxIndex = RightNodeIndex;
}
//调换位置,构造最大堆
if(MaxIndex!=index){
int temp=array[MaxIndex];
array[MaxIndex]=array[index];
array[index]=temp;
//调换完之后可能影响到下面的子树不是大顶堆,需要递归
toMaxHeap(array,size,MaxIndex);
}
}