稳定的排序:冒泡,插入,选择,归并排序
不稳定的排序:希尔,堆排,快排
1.插入排序 —-稳定的排序,O(n^2)
首先讲插入排序是因为,希尔排序由插入排序改进而来。
下面代码通过画图的方式来理解。i依次从0位开始遍历,比较data[i]和data[i+1]的大小,如果data[i]>data[i+1]则需要将data[i+1]的值插入到前面合适的位置,令j=i,让j依次往前遍历,当data[j]>temp,将数据后移;当j移动到最前面或者有data[j]
public static void InsertSort(int [] data){
int temp,j;
for(int i =0;i<data.length;i++){
if(data[i]>data[i+1]){
temp = data[i+1];
for(j = i;(j>=0)&&data[j]>temp;j--){
data[j+1]=data[j];//向后移动
}
data[j+1]=temp;
}
}
}
2.希尔排序 —O(n^1.5)
希尔排序可以理解为插入排序的改进版,只是将插入排序的间隔1的比较改成动态的gap,实现组内有序,通过缩小gap的多次遍历,达到最终的全局有序。
public static void ShellSort(int []data){
int i,j,temp;
int gap = data.length;
do{
gap = gap/3+1;
for(i=0;i<data.length-gap;i++){
if(data[i]>data[i+gap]){
temp = data[i+gap];
for(j=i;(j>=0)&&data[j]>temp;j-=gap){
data[j+gap]=data[j];
}
data[j+gap]=temp;
}
}
}while(gap >1);
}
3.堆排序 —O(nlogn)
堆排序的基本思想是,将待排序的序列构造成一个大顶堆,此时,堆顶的根节点就是最大元素,将其与最后一个元素交换,重新将前N-1个元素构造大顶堆。如此反复执行,便可排序。
注意:按最大堆来说,父节点比孩子节点大,但是左右孩子大小不一定。大顶堆是一个完全二叉树,满足父节点序号为i,其子节点序号为2*i,2*i+1。
public static void heapSort(int [] data){
int n= data.length;
//创建初始最大堆
for(int i=n/2;i>0;i--){
maxHeap(data,i,n);
}
//循环n-1次
for(int i=n-1;i>0;i--){
swap(data,0,i);//将大顶堆的最大元素放到最后面
maxHeap(data,1,i);
// System.out.println(Arrays.toString(data));
}
}
public static void maxHeap(int [] data,int i,int heapsize){
int left=2*i;
int right=left+1;
int largest=i;
if((left<=heapsize)&&(data[left-1]>data[largest-1]))
largest=left;
if((right<=heapsize)&&(data[right-1]>data[largest-1]))
largest=right;
if(largest!=i){
swap(data,largest-1,i-1);
maxHeap(data,largest,heapsize);//递归更新
}
}
4.归并排序 —O(nlogn) 稳定
递归方式:
//递归拆解方式
private static void sort(int[] data, int[] temp, int low, int high) {
int mid = (low+high)/2;
if(low<high){
sort(data,temp,low,mid);
sort(data,temp,mid+1,high);
merge(data,temp, low, mid, high);
}
}
/**
* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
* @param data 数组对象
* @param low 左数组的第一个元素的索引
* @param mid 左数组的最后一个元素的索引,mid+1是右数组第一个元素的索引
* @param high 右数组最后一个元素的索引
*/
private static void merge(int []data,int []temp,int low,int mid,int high){
int i=low;
int j=mid+1;//data的右边的游标
int k=low;//标识temp的游标
while(i<=mid&&j<=high){
if(data[i]<data[j])
temp[k++] = data[i++];
else
temp[k++]= data[j++];
}
while(i<=mid)//把左边剩余的移到temp
temp[k++]=data[i++];
while(j<=high)//右边剩余的移到temp
temp[k++]=data[j++];
//把temp中的排序后的内容覆盖data中的数据
while(low<=high)
data[low]=temp[low++];
}
5.快速排序 —O(nlogn),最坏O(n^2)
基础代码:
public static void Qsort(int []data,int low ,int high){
if(low<high){
int pivot = partition(data,low,high);
Qsort(data,low,pivot-1);
Qsort(data,pivot+1,high);
}
}
public static void partition(int []data,int low ,int high){
int pivotkey = data[low];
while(low<high){
while((low<high)&&(data[high]>=pivotkey))
high--;
swap(data,low,high);
while((low<high)&&(data[low]<=pivotkey))
low++;
swap(data,low,high);
}
return low;
}
优化代码一(减少不必要的交换):
public static void Qsort(int[] data,int low,int high){
if(low<high){
int pivot = partition(data,low,high);
Qsort(data,low,pivot-1);
Qsort(data,pivot+1,high);
}
}
public static void partition(int []data,int low,int high){
int pivotkey = data[low];
while(low<high){
while((low<high)&&(data[high]>=pivotkey))
high--;
data[low]=data[high];//减少不必要的交换
while((low<high)&&(data[low]<=pivotkey))
low++;
data[high]=data[low];
}
data[low]=pivotkey;
return low;
}
注意:如果数组比较小,利用直接插入排序的效率要比快排的效率高;
优化代码二(优化递归,对Qsort实时尾递归):
public static void Qsort(int []data,int low ,int high){
int pivot;
if((high-low)>MAX_LENGTH_INSERT_SORT){
while(low<high){
pivot = partition(data,low,high);
Qsort(data,low,pivot-1);//对低子表递归排序
low=pivot+1; //尾递归
}
}
else
InsertSort(data);//数组较小时采用插入排序
}
其中,将if改成while,并且,更新low,而且只递归低子表。采用迭代的方式,减小堆栈的深度,从而提高性能。
优化三:
快排的非递归实现,利用数组模拟栈来存储子数组的左右边界:
void quicksort_unrecursion(vector<int>&arr)//快速排序非递归
{
int mystack[2000];//假设递归不超过1000层
//栈中保存下次需要排序的子数组的开始位置和结束位置
int top = -1;
mystack[++top] = 0;
mystack[++top] = arr.size() - 1;
while(top > 0)//栈非空
{
int high = mystack[top--], low = mystack[top--];
int middle = mypartition(arr, low, high);
if(middle+1 < high)//右边子数组入栈
{
mystack[++top] = middle+1;
mystack[++top] = high;
}
if(low < middle-1)//左边子数组入栈
{
mystack[++top] = low;
mystack[++top] = middle-1;
}
}
}