常见的排序算法大体可以分为七中,分别为 冒泡排序,选择排序,插入排序,希尔排序,堆排序,归并排序,快速排序,
下表为各自的时间复杂度以及稳定性。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn)~O(n) | 不稳定 |
冒泡排序:
//冒泡排序,依次递增
// 时间复杂度: O(N ^ 2)
// 空间复杂度: O(1)
// 稳定性: 稳定排序
public void BubbleSort(int[] arr){
int tmp;
for(int i=0;i<arr.length;i++){
for(int j=arr.length-1;j>i;j--){
if(arr[j]<arr[j-1]){
tmp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=tmp;
}
}
}
PrintArr(arr);
}
选择排序:
//选择排序,依次递增
// 时间复杂度: O(N ^ 2)
// 空间复杂度: O(1)
// 稳定性: 不稳定
public void SelectSort(int []arr){
int tmp;
for(int i=0;i<arr.length;i++){
for(int j=i;j<arr.length;j++){
if(arr[j]<arr[i]){
tmp=arr[j];
arr[j]=arr[i];
arr[i]=tmp;
}
}
}
PrintArr(arr);
}
插入排序:
//插入排序,依次递增
//时间复杂度: O(N ^ 2)
//空间复杂度: O(1)
//稳定性: 稳定排序
public void InsertSort(int []arr){
int bound;
for(bound=1;bound<arr.length;bound++){
int tmp=arr[bound];
int i=bound;
while(i>0&&arr[i-1]>tmp){//每次只移一个
arr[i]=arr[i-1];
i--;
}
arr[i]=tmp;
}
PrintArr(arr);
}
堆排序:
// 堆排序,依次递增
// 时间复杂度: O(NlogN)
// 空间复杂度: O(1)
// 稳定性: 不稳定排序
//向下调整,建大堆
public static void AdjustDown(int []arr,int size,int parent){
int child=parent*2+1;
while(child<size){
if(child+1<size&&arr[child]<arr[child+1])
{
child=child+1;
}
if(arr[child]>arr[parent]){
int tmp;
tmp=arr[parent];
arr[parent]=arr[child];
arr[child]=tmp;
}
else{
break;
}
parent=child;
child=parent*2+1;
}
}
//建堆
public static void HeapMake(int []arr,int size){
if (size <= 1) {
return;
}
for(int i=(size-2)/2;i>=0;i--){
AdjustDown(arr, size,i);
}
}
//删除堆顶元素
public static void Pop(int []arr,int size){
if(size<=1){
return ;
}
int tmp;
tmp=arr[size-1];
arr[size-1]=arr[0];
arr[0]=tmp;
AdjustDown(arr,size-1, 0);
}
public void HeapSort(int []arr){
HeapMake(arr, arr.length);
for(int i=0;i<arr.length;i++){
Pop(arr, arr.length-i);
}
PrintArr(arr);
}
希尔排序:
// 希尔(Shell)排序,,依次递增, 一种改进版本的插入排序
// 时间复杂度: 对于希尔序列, O(N ^ 2);
// 对于最优序列, 复杂度达到 O(N ^ 1.3)
// 空间复杂度: O(1)
// 稳定性: 不稳定排序
public void ShellSort(int []arr)
{
int length=arr.length;
int gap=length/2;
for(;gap>0;gap/=2)//分组
{
int bound=gap;//希尔排序的要点,1.分组
//2.采用插入排序(注意每一个元素的下标)
for(;bound<arr.length;bound++){
//直接从每组的第二个元素开始插入
int tmp=arr[bound];
int i=bound;
while(i>=gap&&arr[i-gap]>tmp){
arr[i]=arr[i-gap];//循环搬移
i=i-gap;
}
arr[i]=tmp;
}
}
PrintArr(arr);
}
递归版归并排序:
// 归并排序,依次递增
// 时间复杂度: O(NlogN)
// 空间复杂度: 对于数组来说, O(N)
// 稳定性: 稳定排序
public void MergeSort(int []arr){
int length=arr.length;
int []tmp=new int[length];
_MergeSort(arr,0,length-1,tmp);
PrintArr(arr);
}
private static void _MergeSort(int[] arr, int beg, int end, int[] tmp) {
if(end-beg<1){
return ;
}
//分成一个个数组
int mid=beg+(end-beg)/2;
_MergeSort(arr,beg,mid,tmp);//左边归并排序,使左边有序
_MergeSort(arr,mid+1,end,tmp);//右边归并排序,使右边有序
MergeArray(arr,beg,mid,end,tmp);//合并
}
//将数组有序合并
private static void MergeArray(int[] arr, int beg, int mid, int end,
int[] tmp) {
int output_index=0;//临时数组指针
int cur1=beg;//左序列指针
int cur2=mid+1;//右序列指针
while (cur1<=mid && cur2<=end) {
if(arr[cur1]<arr[cur2]){
tmp[output_index++]=arr[cur1++];
}
else{
tmp[output_index++]=arr[cur2++];
}
}
while(cur1<=mid){
tmp[output_index++]=arr[cur1++];
}
while(cur2<=end){
tmp[output_index++]=arr[cur2++];
}
output_index=0;
while(beg<=end){
arr[beg++]=tmp[output_index++];
}
}
非递归归并排序:
//非递归归并排序
public void MergeSortByLoop(int []arr) {
int length=arr.length;
int []tmp=new int [length];
int gmp=1;//当前的步数
for(;gmp<arr.length;gmp=gmp*2) {
for(int i=0;i+gmp<arr.length;i=i+gmp*2) {
//循环处理相邻的两个区间
int beg=i;
int mid=i+gmp-1;
if(mid>arr.length-1) {
mid=arr.length-1;
}
int end=i+2*gmp-1;
if(end>arr.length-1) {
end=arr.length-1;
}
MergeArray(arr, beg, mid, end, tmp);
}
}
PrintArr(arr);
}
递归快速排序:
// 快速排序,依次递增
// 时间复杂度: 最坏 O(N ^ 2)(如果数组是逆序的) 平均水平 O(NlogN)
// 空间复杂度: O(N) 递归. 最坏情况下要递归 N 层
// 稳定性: 不稳定排序
public static int Partion(int[]arr,int beg,int end) {
int key=arr[end];
int left=beg;
int right=end;
while(left<right) {
//找到比基准值大的数字,左指针加一
while(left<right&&arr[left]<=key) {
left++;
}
//找到比基准值小的数字,右指针减一
while(left<right&&arr[right]>=key) {
right--;
}
//左边比基准值大,右边比基准值小。
int tmp=arr[left];
arr[left]=arr[right];
arr[right]=tmp;
}
//将基准值放在应该在的位置
int tmp=arr[left];
arr[left]=arr[end];
arr[end]=tmp;
return left;
}
public static void _QuickSort(int[]arr,int left,int right) {
if(left>=right) {
return ;
}
int mid=Partion(arr, left, right);
_QuickSort(arr, left, mid-1);
_QuickSort(arr, mid, right);
}
public void QuickSort(int[]arr) {
int right=arr.length-1;
_QuickSort(arr,0,right);
PrintArr(arr);
}
非递归快速排序:
//非递归快速排序,借助栈
//操作思路和归并排序很像, 都是倒腾区间
public void QuickSortByloop(int []arr) {
int length=arr.length;
int beg=0;
int end=length-1;
Stack<Integer>stack=new Stack<Integer>();
stack.push(beg);
stack.push(end);
while(!stack.empty()) {
int right=stack.pop();
int left=stack.pop();
if(left<right) {
int mid=Partion(arr, left, right);
// 此时又得到了两个待整理区间:
// [beg, mid)
// [mid+1, end)
if((mid-1)>left) { //左边待排序区间
stack.push(beg);
stack.push(mid);
}
if((mid+1)<right) { //右边已经排序好的区间
stack.push(mid+1);
stack.push(end);
}
}
}
PrintArr(arr);
}
以上为七种排序算法的代码,代码为个人所思所写,若有错误,欢迎随时指教,不胜感激。