1. 冒泡排序
时间复杂度:最好O(n),平均和最坏情况O(n2)
空间复杂度:O(1)
稳定排序
原理:从第一个元素开始,依次比较相邻两个元素,如果前者比后者大,那么就交换者两个元素,然后处理下一组,依次类推,直到排序完成。
实现:
publicvoid bubbleSort(int[] arr){
boolean isChanged =false;
for(int i =0; i < arr.length -1; i++){
isChanged =false;
for(int j =0; j < arr.length -1- i; j++){
if(arr[j]> arr[j+1]){
int tmp = arr[j];
arr[j]= arr[j+1];
arr[j+1]= tmp;
isChanged =true;
}
}
if(!isChanged)break;//本次没有发生交换,说明已经排好序了
}
}
2. 选择排序
时间复杂度:最好、平均和最坏情况O(n2)
空间复杂度:O(1)
不是稳定排序
原理:从第一个元素开始,每次逐一扫描选择未排序部分的最小值,排在已排序部分后面,然后从下一个位置开始,继续进行相同的操作,直到排序完成。
实现:
/**
* 选择排序
*/
publicstaticvoid sort(int[] arr){
//判断arr是否为空
if(arr ==null)return;
int minIndex;
for(int i =0; i < arr.length -1; i++){
minIndex = i;
for(int j = i +1; j < arr.length; j++){
if(arr[j]< arr[minIndex]){
minIndex = j;
}
}
if(minIndex != i){//最小值不是当前值,需要交换
int tmp = arr[i];
arr[i]= arr[minIndex];
arr[minIndex]= tmp;
}
}
}
3. 快速排序
时间复杂度:最好、平均情况O(nlogn),最坏情况O(n2)
空间复杂度:平均情况O(logn),最坏情况O(n)
不是稳定排序
原理:每次选择一个数,将数组按照这个数分成左右两个部分,右边的比它大,左边的比他小然后对左右两部分分别进行同样的操作,直到数组排序完成
实现:
publicstaticvoid sort(int[] arr){
sortCore(arr,0, arr.length -1);
}
/**
* @param arr 排序的数组
* @param start 开始位置
* @param end 结束位置
*/
privatestaticvoid sortCore(int[] arr,int start,intend){
int poiv = partion(arr, start,end);
if(poiv > start){
sortCore(arr, start, poiv -1);
}
if(poiv >= start && poiv <end){
sortCore(arr, poiv +1,end);
}
}
privatestaticint partion(int[] arr,int start,intend){
int tmp = arr[start];
while(start <end){
while(start <end&& arr[end]>= tmp)end--;
if(start <end){
arr[start]= arr[end];
}
while(start <end&& arr[start]< tmp) start++;
if(start <end){
arr[end]= arr[start];
}
}
arr[start]= tmp;
return start;
}
4. 归并排序
时间复杂度:最好、平均和最坏情况O(nlogn)
空间复杂度:O(n)
稳定排序
原理:首选将要排序的数组对半分,对各自部分进行排序。每部分继续进行相同的操作,直至最底层。然后合并两个相邻的部分,直到所有元素都排序完成
实现:
publicstaticvoid sort(int[] arr){
//创建数组,辅助排序
int[] copy =newint[arr.length];
sortCore(arr, copy,0, arr.length -1);
}
/**
* 归并排序核心实现
* @param arr 排序的数组
* @param copy 辅助空间
* @param start 开始位置
* @param end 结束位置
* @param offset 索引相对于原数组的偏移
*/
privatestaticvoid sortCore(int[] arr,int[] copy,int start,intend){
if(start ==end){
copy[start]= arr[start];
return;}
int mid =(end- start)/2+ start;
//分成两部分,递归
sortCore(arr, copy, start, mid);
sortCore(arr, copy, mid +1,end);
//合并两个部分,将合并结果存入
int forward = mid;
int behand =end;
intlast=end;
while(forward >= start && behand > mid){
if(copy[forward]> copy[behand]){
arr[last--]= copy[forward --];
}else{
arr[last--]= copy[behand --];
}
}
while(forward >= start){
arr[last--]= copy[forward --];
}
while(behand > mid){
arr[last--]= copy[behand --];
}
//拷贝到copy数组
for(int i = start; i <=end; i++){
copy[i]= arr[i];
}
}
5. 插入排序
时间复杂度:最好O(n),平均和最外情况O(n2)
空间复杂度:O(1)
稳定排序
原理:从数组第一个元素开始,依次比较前面已经排序的部分,插入合适的位置,前面排序部分比当前值大的部分向后移动一个。
实现:
publicstaticvoid sort(int[] arr){
int tmp;//每次排序,存储当前的值
for(int i =1; i < arr.length; i++){
tmp = arr[i];//保存当前值
int j;
for(j = i; j >= fromIndex && tmp < arr[j -1]; j --){//遇到比当前值大的元素,则元素后移一位
arr[j]= arr[j-1];
}
arr[j]= tmp;|
}
}
6. 希尔排序
时间复杂度:平均情况O(n1.25)
空间复杂度:O(1)
不是稳定排序
原理:将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序……最后选择增量为1,即使用直接插入排序,使最终数组成为有序。
实现:
publicstaticvoid sort(int[] arr){
//checkRange(arr.length, fromIndex, toIndex);
int adder = arr.length /2;//增量
while(adder >0){
//从adder开始,每次排序均与前面的adder(当adder是1时就是插入排序)处的元素比较
for(int i = adder ; i < arr.length; i++){
int j;
int tmp = arr[i];
for(j = i; j >= adder && tmp < arr[j - adder]; j = j - adder){
arr[j]= arr[j - adder];
}
arr[j]= tmp;
}
adder /=2;
}
}
7. 堆排序
时间复杂度:最好、平均和最坏情况均为O(nlogn)
空间复杂度:O(1)
不是稳定排序
原理:建立初始堆,从最后一个非叶结点开始,往前遍历,判断以该节点的开始的堆是否是符合,不符合则调整需要建立大顶堆,每次将子节点中较大地一个数往上移动,直到叶结点(堆:结点n的父节点为(n-1)/ 2,其左右子节点为2n+1和2n+2大根堆为根结点的值大于等于左右子结点的值),然后依次将堆顶值与为排序的最后一个值交换,然后调整前面的值为大顶堆,每次将最大的值排好序。
实现:
/**
* 堆排序
*/
publicstaticvoid sort(int[] arr){
//建立初始堆,从最后一个非叶结点开始,往前遍历,判断以该节点的开始的堆是否是符合,不符合则调整
//需要建立大顶堆,每次将子节点中较大地一个数往上移动,直到叶结点
//节点n的父节点为(n-1)/ 2,其左右子节点为2*n+1和2*n+2
//大根堆为根结点的值大于等于左右子结点的值
for(int i =(arr.length -1-1)/2; i >=0; i--){
adjustHeap(arr, i, arr.length-1);
}
//依次将堆顶值与为排序的最后一个值交换,然后调整前面的值为大顶堆
for(int i = arr.length -1; i >=1; i--){
//交换
arr[0]^= arr[i];
arr[i]^= arr[0];
arr[0]^= arr[i];
//调整
adjustHeap(arr,0, i -1);
}
}
/**
* 调整为大顶堆
* @param arr
* @param i 以i为堆的堆顶
* @param last 堆顶的最后一个结点的索引
*/
privatestaticvoid adjustHeap(int[] arr,int i,intlast){
//建立以i结点为根的堆,判断子结点是否大于该节点,并将较大地值拷贝,然后继续判断
int tmp = arr[i];
for(int j = i *2; j <=last; j *=2){
//获得左右子树中较大的一个数的下标
if(j <last&& arr[j]< arr[j+1]) j++;//存在右子结点且右子结点较大
if(tmp >= arr[j])break;//根结点比较大,则完成
//将较大的值作为根
arr[i]= arr[j];
i = j;//继续往下判断,j的位置的值是最初的根结点
}
arr[i]= tmp;//最后确定的位置,没有子结点或者比子结点的值大;
}