时间、空间复杂度比较
排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 |
---|---|---|---|---|
冒泡排序 | O(n2) | O(n2) | O(1) | 稳定 |
选择排序 | O(n2) | O(n2) | O(1) | 数组不稳定、链表稳定 |
插入排序 | O(n2) | O(n2) | O(1) | 稳定 |
快速排序 | O(n*log2n) | O(n2) | O(log2n) | 不稳定 |
堆排序 | O(n*log2n) | O(n*log2n) | O(1) | 不稳定 |
归并排序 | O(n*log2n) | O(n*log2n) | O(n) | 稳定 |
希尔排序 | O(n*log2n) | O(n2) | O(1) | 不稳定 |
计数排序 | O(n+m) | O(n+m) | O(n+m) | 稳定 |
桶排序 | O(n) | O(n) | O(m) | 稳定 |
基数排序 | O(k*n) | O(n2) | 稳定 |
冒泡排序
算法思想
void bubbleSort(int a[], int n)
{
for(int i=0;i<n-1;++i)
{
for(int j=0;j<n-i-1;s++j)
{
if(a[j] > a[j+1])
{
int tmp = a[j] ; //交换
a[j] = a[j+1] ;
a[j+1] = tmp;
}
}
}
}
选择排序
算法思想
public static void select(int[] a){
for(int i=0;i<a.length-1;i++){
int index = i;
for(int j=i+1;j<a.length-1;j++){
if(a[j] < a[index]){
index = j;
}
}
if(i != index){
int temp = a[i];
a[i] = a[index];
a[index] = temp;
}
}
}
插入排序
算法思想
public static void insert(int[] a){
int i,j;
for(i=1;i<a.length;i++){
int temp = a[i];
for(j=i-1;j>=0;j--){
if(a[j] > temp) {
a[j + 1] = a[j];
}else{
break;
}
}
a[j+1] = temp;
}
}
快速排序
算法思想
public static void quick(int[] a,int low,int high){
int i,j,t,temp;
if(low > high){
return;
}
i = low;
j = high;
temp = a[low];
while(i < j){
while(a[j] >= temp && i < j){
j--;
}
while(a[i] <= temp && i < j){
i++;
}
if(i < j){
t = a[j];
a[j] = a[i];
a[i] = t;
}
}
a[low] = a[i];
a[i] = temp;
quick(a,low,j-1);
quick(a,j+1,high);
}
堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点
算法思想
public static void heapSort(int[] arr){
//创建堆
for(int i=(arr.length-1)/2;i>=0;i--){
//从第一个非叶子节点开始,从下至上调整左右结构
adjustHeap(arr, i ,arr.length);
}
//调整堆结构+交换堆顶元素与末尾与元素
for(int i=arr.length-1;i>0;i--) {
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//重新对堆进行调整
adjustHeap(arr, 0,i);
}
}
public static void adjustHeap(int[] arr, int parent, int length){
//将temp作为父节点
int temp = arr[parent];
int lChild = 2*parent + 1;
while(lChild < length){
//右孩子
int rChild = lChild + 1;
//如果有右孩子节点,并且右孩子节点的值大于左孩子的值,则选取右孩子节点
if(rChild < length && arr[lChild] < arr[rChild]){
lChild++;
}
//如果节点的值已经大于孩子节点的值,则直接结束
if(temp >= arr[lChild]){
break;
}
//把孩子节点的值赋给父节点
arr[parent] = arr[lChild];
//选取孩子节点的左孩子节点,继续向下筛选
parent = lChild;
lChild = 2*lChild + 1;
}
arr[parent] = temp;
}
归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并
算法思想
public static void merge(int[] a,int low,int mid,int high,int[] temp){
int i = 0;
int j = low ;int k = mid + 1;
while(j <= mid && k <= high){
if(a[j] < a[k]){
temp[i++] = a[j++];
}else{
temp[i++] = a[k++];
}
}
while(j <= mid){
temp[i++] = a[j++];
}
while(k <= high){
temp[i++] = a[k++];
}
for(int t=0;t<i;t++){
a[low+t] = temp[t];
}
}
public static void mergeSort(int[] a,int low,int high,int[] temp){
if(low < high){
mergeSort(a,low,(low + high) / 2,temp);
mergeSort(a,((low + high) / 2) + 1,high,temp);
merge(a,low,(low + high) / 2,high,temp);
}
}
希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序
算法思想
public static void shell(int[] a){
int i,j;
int temp = 0;
int ic = 0;
for(ic=a.length/2;ic>0;ic=ic/2){
for(i=ic;i<a.length;i=i+ic){
temp = a[i];
for(j=i-ic;j>=0;j=j-ic){
if(temp < a[j]){
a[j+ic] = a[j];
}else{
break;
}
}
a[j+ic] = temp;
}
}
}
计数排序
计数排序统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)
算法思想
public static int[] countSort(int[] a, int k){
int[] C = new int[k+1];
int length = a.length,sum = 0;
int[] B = new int[length];
for(int i=0;i<length;i++){
C[a[i]] += 1;
}
for(int i=0;i<k+1;i++){
sum += C[i];
C[i] = sum;
}
for(int i=length-1;i>=0;i--){
B[C[a[i]]-1] = a[i];
C[a[i]]--;
}
return B;
}
桶排序
将值为i的元素放入i号桶,最后依次把桶里的元素倒出来
算法思想
public static void bucketSort(float[] arr) {
// 新建一个桶的集合
ArrayList<LinkedList<Float>> buckets = new ArrayList<LinkedList<Float>>();
for (int i = 0; i < 10; i++) {
// 新建一个桶,并将其添加到桶的集合中去。
// 由于桶内元素会频繁的插入,所以选择 LinkedList 作为桶的数据结构
buckets.add(new LinkedList<Float>());
}
// 将输入数据全部放入桶中并完成排序
for (float data : arr) {
int index = getBucketIndex(data);
insertSort(buckets.get(index), data);
}
// 将桶中元素全部取出来并放入 arr 中输出
int index = 0;
for (LinkedList<Float> bucket : buckets) {
for (Float data : bucket) {
arr[index++] = data;
}
}
}
/**
* 计算得到输入元素应该放到哪个桶内
*/
public static int getBucketIndex(float data) {
// 这里例子写的比较简单,仅使用浮点数的整数部分作为其桶的索引值
// 实际开发中需要根据场景具体设计
return (int) data;
}
/**
* 我们选择插入排序作为桶内元素排序的方法 每当有一个新元素到来时,我们都调用该方法将其插入到恰当的位置
*/
public static void insertSort(List<Float> bucket, float data) {
ListIterator<Float> it = bucket.listIterator();
boolean insertFlag = true;
while (it.hasNext()) {
if (data <= it.next()) {
it.previous(); // 把迭代器的位置偏移回上一个位置
it.add(data); // 把数据插入到迭代器的当前位置
insertFlag = false;
break;
}
}
if (insertFlag) {
bucket.add(data); // 否则把数据插入到链表末端
}
}
基数排序
一种多关键字的排序算法,可用桶排序实现
算法思想
public class RadixSort {
private static void radixSort(int[] array,int d)
{
int n=1;//代表位数对应的数:1,10,100...
int k=0;//保存每一位排序后的结果用于下一位的排序输入
int length=array.length;
int[][] bucket=new int[10][length];//排序桶用于保存每次排序后的结果,这一位上排序结果相同的数字放在同一个桶里
int[] order=new int[length];//用于保存每个桶里有多少个数字
while(n<d)
{
for(int num:array) //将数组array里的每个数字放在相应的桶里
{
int digit=(num/n)%10;
bucket[digit][order[digit]]=num;
order[digit]++;
}
for(int i=0;i<length;i++)//将前一个循环生成的桶里的数据覆盖到原数组中用于保存这一位的排序结果
{
if(order[i]!=0)//这个桶里有数据,从上到下遍历这个桶并将数据保存到原数组中
{
for(int j=0;j<order[i];j++)
{
array[k]=bucket[i][j];
k++;
}
}
order[i]=0;//将桶里计数器置0,用于下一次位排序
}
n*=10;
k=0;//将k置0,用于下一轮保存位排序结果
}
}