排序算法可以分为两类:
非线性时间比较排序:通过比较来决定元素之间的相对次序,由于其时间复杂度不能突破O(nlogn)。
线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下届,以线性时间运行。
排序算法分类:
非线性时间比较类排序
交换排序:冒泡;快排;
插入排序:简单插入排序;shell;
选择排序:简单选择;堆排序;
归并排序:二路归并;多路归并;
线性时间比较类排序
基数排序;桶排序;基数排序;
相关概念:
稳定性:排序后相对顺序不变;
不稳定性:排序后相对位置改变
时间复杂度:
空间复杂度:
算法复杂度
排序算法 | 平均时间复杂度 | 最好时间复杂度 | 最差时间复杂度 | 空间复杂度 | 稳定性 |
冒泡排序 | 稳定 | ||||
快速排序 | 不稳定 | ||||
简单选择 | 不稳定 | ||||
堆排序 | 不稳定 | ||||
简单插入 | 稳定 | ||||
shell排序 | 不稳定 | ||||
归并排序 | 稳定 |
1、冒泡排序
算法思想:重复走访要排序的数列,一次比较两个元素,如果它们的顺序错误就将它们交换过来,走访数列的工作是重复的进行指导没有再需要交换,即数列已经排序完成。
算法描述:
比较相邻的元素,如果第一个比第二个大,就交换它们两个。
对每一对相邻元素做相同的工作,从开始第一对到结尾的最后一对,这样在最后的元素就是最大的数。
针对所有元素重复以上步骤,对已经排序的元素不再进行排序,重复,直至排序完成。
public class BubbleSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr= new int[] {2,9,0,5,3,8,4};
bubblesort(arr);
for(int i=0;i<arr.length;i++)
System.out.print(arr[i]+"\t");
}
public static void bubblesort(int[] arr) {
int len=arr.length;
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length-i-1;j++) {
if (arr[j]>arr[j+1]) {
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
}
选择排序
算法思想:首先在未排序序列中找到最小(大)的元素,存放在排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放在已排序序列的末尾,以此类推,直到所有元素都排序完毕。
算法描述
初始状态:无序区为R[1..n],有序区为空;
第i趟排序(i=1,2,....n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R[i...n],然后从无序区选取值最小的元素为R[k],将它与无序区的第一个记录R[i]交换,这样有序区就变成了R[1....i],无序区为R[i+1,.....n]
重复n-1趟,数组有序化
public static void selectsort(int[] arr) {
int len = arr.length;
for(int i=0;i<len-1;i++) {
int minindex=i;
for(int j=i+1;j<len;j++) {
if (arr[j]<arr[minindex]) {
minindex=j;
}
}
int temp=arr[i];
arr[i]=arr[minindex];
arr[minindex]=temp;
}
}
插入排序
算法思想:对数组中的每一个元素,在已排序序列中从后向前扫描,查找到相应的位置后插入。
算法描述:
从第一个元素开始,该元素已经排好序;
取出下一个元素,在已经排好序的序列中从后向前扫描;
如果该元素(已排序)大于新元素,该元素移动到下一个位置;
重复,直到找到已排序的元素小于等于新元素的位置;
将新元素插入到该位置后。
public static void insertsort(int[] arr) {
int len=arr.length;
for(int i=1;i<len;i++) {
int index=i;
int temp=arr[i];
for(int j=i;j>0;j--) {
if (arr[i]<arr[j-1]) {
index=j-1;
}
}
for(int j=i;j>index;j--) {
arr[j]=arr[j-1];
}
arr[index]=temp;
}
}
4、希尔排序(shell)
算法思想:缩小增量排序
算法描述:
选择一个增量序列t1、t2、......tk,其中ti>tj(i<j),tk=1;
按增量序列个数k,对序列进行k趟排序
每趟排序,根据对应的增量ti,将序列分割成若干个长度为m的子序列,分别对各子表进行直接插入排序,当增量因子为1时,整个序列作为一个表来处理,表长度为整个序列的长度。
5、快速排序
算法思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述:
从数据中挑出一个元素作为基准(pivot);(每次选择分区中最左边的元素作为基准)
重新排序数组,所有比基准值小的放在基准前面,所有比基准值大的元素放在基准元素后面,在这个分区退出之后,该基准就位于正确地位置上;
递归的把小于基准值元素的子数列和大于基准值元素的子数列排序;
public static void quicksort(int[] arr,int left,int right) {
if (left>right) {
return;
}
int i=left,j=right;
int temp=arr[left];
while(i!=j) {
while (i<j&&arr[j]>arr[left]) {
j--;
}
while(i<j&&arr[i]<=arr[left]) {
i++;
}
if (i<j) {
int t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
}
arr[left]=arr[i];
arr[i]=temp;
quicksort(arr,left,i-1);
quicksort(arr, i+1, right);
}
6、堆排序
算法思想:利用堆这一数据结构所设计的一种排序算法,堆是一个近似完全二叉树的结构,并满足堆的性质,即结点的键值或者索引总是小于(或者大于)它的父结点。
算法描述:
1、将待排序列初始化为最大堆,并将堆顶元素R[1]删除,和最后一个元素R[n]进行交换;
2、将新的堆调整为最大堆,并重复操作1,直到整个排序过程完成;
private static void buildMaxHeap(int[] arr,int len) {//建立最大堆
for(int i=len/2;i>=0;i--) {
heapify(arr,i,len);
}
}
private static void heapify(int[] arr,int i,int len) {//调整堆
int left=2*i+1;
int right=2*i+2;
int largest = i;
if (left<len&&arr[left]>arr[largest]) {
largest=left;
}
if (right<len&&arr[right]>arr[largest]) {
largest=right;
}
if (largest!=i) {
swap(arr,i,largest);
heapify(arr, largest,len);
}
}
private static void swap(int[] arr,int i,int j) {//元素交换
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
private static void heapsort(int[] arr) {
int len=arr.length;
buildMaxHeap(arr,len);
for(int i=arr.length-1;i>0;i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0,len);
}
}