排序算法总结
排序
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。是《数据结构与算法》中最基本的算法之一。
排序算法复杂度
稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
-
稳定
冒泡、插入、归并和基数。 -
不稳定
选择、快速、希尔、堆。
十大排序算法
冒泡排序
相邻元素交换,最大的放到最后一个,次大的放到倒数第二个,依次类推
public static void BubbleSort(int[] nums)
{
//标志 如果某一轮没有发生交换, 则排序完成
boolean flag;
for(int i=0;i<nums.length;i++)
{
flag=false;
for(int j=0;j<nums.length-i-1;j++)
if(nums[j]>nums[j+1])
{
flag=true;
nums[j] = nums[j] + nums[j + 1];
nums[j + 1] = nums[j] - nums[j + 1];
nums[j] = nums[j] - nums[j + 1];
}
if(!flag) break;
}
}
选择排序
依次选择最小的元素放在第一个,第二个…
异或操作交换元素 基于 a=a^b^b
public static void SelectionSort(int[] nums)
{
int min;
for(int i=0;i<nums.length;i++)
{
min=i;
for(int j=i+1;j<nums.length;j++)
{
if(nums[j]<nums[min])
min=j;
}
if(i!=min)
{
nums[i]^=nums[min];
nums[min]^=nums[i];
nums[i]^=nums[min];
}
}
}
插入排序
假定左边元素有序,将右边元素选择合适位置插入
public static void insertionSort(int[] nums)
{
for(int i=1;i<nums.length;i++)
{
int j=i,tmp=nums[i];
while (j>0&&nums[j-1]>tmp)
{
nums[j] = nums[j - 1];
j--;
}
//放入合适位置
if(j!=i)
nums[j]=tmp;
}
}
快速排序
选择中间值,左边的元素都比他大,右边的元素都比他小,再对两边 进行递归
public static void quickSort(int[] nums,int left,int right)
{
if(left<right)
{
int piv=partition(nums,left,right);
quickSort(nums,left,piv-1);
quickSort(nums,piv+1,right);
}
}
public static int partition(int[] nums,int left,int right)
{
int pivot=nums[left];
while(left<right)
{
while (left<right&&nums[right] > pivot) right--;
nums[left] = nums[right];
//<=
while (left<right&&nums[left] <= pivot) left++;
nums[right] = nums[left];
}
nums[left]=pivot;
return left;
}
归并排序
自上而下的递归
public static void mergeSort(int[] nums,int left,int right)
{
if(left<right)
{
//中间值mid也需要分出去
int mid = (left + right) / 2;
mergeSort(nums, left, mid );
mergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
}
public static void merge(int[] a,int left,int mid,int right)
{
int b[]=Arrays.copyOf(a,a.length);
int i,j,k;
for(i=left,j=mid+1,k=left;i<=mid&&j<=right;)
{
if(b[i]<b[j])
a[k++]=b[i++];
else
a[k++]=b[j++];
}
while(i<=mid) a[k++]=b[i++];
while(j<=right) a[k++]=b[j++];
}
堆排序
- 构建大顶堆 数组中一半元素依次作为堆顶调整,堆大小为数组长度
- 将堆顶元素与数组中最后一个元素交换,堆中元素减一,堆调整
- 重复二,直到堆中剩下一个元素。
public static void HeapSort(int[] nums)
{
buildMaxHeap(nums);
for(int i=nums.length-1;i>0;i--)
{
swap(nums,0,i);
heapAdjust(nums,0,i);
//调试输出中间结果
for(int n:nums)
System.out.print( n+" ");
System.out.println();
}
}
private static void heapAdjust(int[] nums, int k, int len )
{
int temp=nums[k];
for(int i=2*k+1;i<len;i*=2)
{
if(i+1<len&&nums[i]<nums[i+1])
i++;
if(temp>=nums[i]) break;
else {
nums[k] = nums[i];
k=i;
}
}
nums[k]=temp;
}
//初始化堆从中间位置到开始元素
private static void buildMaxHeap(int[] nums)
{
for(int i=nums.length/2;i>=0;i--)
{
heapAdjust(nums,i,nums.length);
}
}
private static void swap(int[] nums, int l, int r)
{
int temp=nums[l];
nums[l]=nums[r];
nums[r]=temp;
}
希尔排序
插入排序的升级版
public static void ShellSort(int[] nums)
{
int temp,j;
//分组不断减小
for(int dk=nums.length/2;dk>=1;dk/=2 )
{
//插入排序
for(int i=dk;i<nums.length;i++)
{
if( nums[i] <nums[i-dk] )
{
temp=nums[i];
for(j=i-dk;j>=0&&temp<nums[j];j-=dk)
{
//往后移
nums[j+dk]=nums[j];
}
//移动到比他小的元素的后一个位置
nums[j+dk]=temp;
}
}
}
计数排序
-
找出原数组中元素值最大的,记为max。
-
创建一个新数组count,其长度是max加1,其元素默认值都为0。
-
遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
-
创建结果数组result,起始索引index。
-
遍历count数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素。
-
返回结果数组result。
public static void countSort(int[] nums)
{
int max_value=Integer.MIN_VALUE;
for(int n:nums) max_value=Math.max(max_value,n);
int [] count=new int[max_value+1];
for(int n:nums)
count[n]++;
int index=0;
for(int j=0;j<max_value+1;j++)
while (count[j]>0)
{
nums[index++] = j;
count[j]--;
}
//System.out.println(checkSort(nums));
//System.out.println(nums);
}
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
- 在额外空间充足的情况下,尽量增大桶的数量
- 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要
public static int[] bucketSort(int[] nums,int bucketSize)
{
int[] arr=Arrays.copyOf(nums,nums.length);
int minValue=Integer.MAX_VALUE;
int maxValue=Integer.MIN_VALUE;
for(int n:nums)
{
if(n<minValue)
minValue=n;
else if(n>maxValue)
maxValue=n;
}
int bucketCount=(int)(Math.floor((maxValue-minValue)/bucketSize))+1;
int[][] bucket=new int[bucketCount][0];
for(int i=0;i<arr.length;i++)
{
//分配每个值所属的桶除以bucketSize
int bucketIndex=(int)Math.floor((arr[i]-minValue)/bucketSize);
bucket[bucketIndex]=arrAppend(bucket[bucketIndex],arr[i]);
}
int pos=0;
for(int[] bt:bucket )
{
if (bt.length == 0)
continue;
insertionSort(bt);
for(int v:bt)
{
arr[pos++]=v;
}
}
return arr;
}
基数排序
public static void radixSort(int[] nums)
{
int maxDigit=getMaxDigit(nums);
int mod=10,dev=1;
for(int i=0;i<maxDigit;i++,dev*=10,mod*=10)
{
int[][] bucket=new int[mod*2][0];
// 分桶
for(int j=0;j<nums.length;j++)
{
int bucketIndex=((nums[j]%mod)/dev) + mod;
bucket[bucketIndex]=arrAppend(bucket[bucketIndex],nums[j]);
}
// 排序 取数据
int pos=0;
for(int[] b:bucket)
for(int v:b)
nums[pos++]=v;
}
}
public static int getMaxDigit(int[] nums)
{
int maxv=Integer.MIN_VALUE;
for(int n:nums)
maxv=Math.max(maxv,n);
if (maxv== 0) {
return 1;
}
int lenDigit=0;
while(maxv>0)
{
lenDigit++;
maxv/=10;
}
return lenDigit;
}
public static int[] arrAppend(int[] n,int temp )
{
int[] res=Arrays.copyOf(n,n.length+1);
res[res.length-1]=temp;
return res;
}