本文将介绍几种常用的排序算法
- 选择排序
- 插入排序
- 希尔排序
- 归并排序
- 快速排序
- 堆排序
文中部分封装代码
/*
交换算法
输入数组,大值索引,小值索引
无输出
*/
public static void exch(Integer[] num, Integer max, Integer min){
Integer type = num[max];
num[max] = num[min];
num[min] = type;
}
选择排序
算法思想
①找到数组中最小的元素
②将它和数组中第一个元素交换位置(如果第一个元素最小那么就和自己交换)
③在剩下的元素中找到最小的元素,将它与数组第二个元素交换
④如此往复,直到整个数组排序
/*
选择排序
算法思想: 找到数组中最小的元素 --> 将它和数组中第一个元素交换位置(如果第一个元素最小那么就和自己交换)
--> 在剩下的元素中找到最小的元素,将它与数组第二个元素交换 --> 如此往复,直到整个数组排序
*/
public static void Selection(Integer[] num){
Integer N = num.length;
Integer min; //用于存储最小数字索引,定义在循环外节省内存空间
for(int i = 0 ; i < N ; i++){
min = i;
for (int j = i+1 ; j < N ; j++){
if (num[j] < num[i] )
min = j;
}
exch(num,i,min);
}
}
插入排序
算法思想:与选择排序类似,索引右侧数据不被访问,索引左侧进行排序,交换相邻元素
/*
插入排序
算法思想:索引右侧数据不被访问,索引左侧进行排序,交换相邻元素
*/
public static void Insertion(Integer[] num){
Integer N = num.length;
for(int i = 1 ; i < N ; i++){
for (int j = i ; j > 0 && (num[j] < num[j-1]) ; j--){
exch(num,j-1,j);
}
}
}
希尔排序
基于插入排序的快速的排序算法
算法思想: 使数组中任意间隔为 h 的元素都是有序的,对于每个 h 用插入排序将 h 个子数组独立地排序
更简洁的,在 h-子数组中将每个元素交换到比它大的元素之前去 --> 只需在插排代码中将移动距离由1变为h
/*
希尔排序 -- 基于插入排序的快速的排序算法
算法思想: 使数组中任意间隔为 h 的元素都是有序的,对于每个 h 用插入排序将 h 个子数组独立地排序
更简洁的,在 h-子数组中将每个元素交换到比它大的元素之前去 --> 只需在插排代码中将移动距离由1变为h
*/
public static void Shell(Integer[] num){
Integer N = num.length;
Integer h = 1;
while( h < N/3 ) h = 3*h + 1; //得到步长h
//嵌套外循环来得到最简洁的希尔排序
while( h>=1 ){
for(int i = h ; i < N ; i++){
for(int j = i ; j >= h && (num[j] < num[j-h]) ; j -= h){
exch(num,j-h,j);
}
}
h = h/3;
}
}
归并排序
算法思想:将两个有序的数组归并成一个更大的数组,利用递归循环实现
/*
归并排序
算法思想:将两个有序的数组归并成一个更大的数组
*/
public static void Merge(Integer[] num,Integer low,Integer mid,Integer high){
//将已经排好序的 num[low...mid] 和 num[mid+1...high]归并
int i = low, j = mid+1;
Integer[] numm = new Integer[num.length];
for (int k = low ; k <= high ; k++){
numm[k] = num[k];
}
for (int k = low ; k <= high ; k++){ //归并
if(i > mid) num[k] = numm[j++]; //左半边用尽(取右半边元素)
else if (j > high) //右半边用尽(取左半边元素)
num[k] = numm[i++];
else if(numm[j] < numm[i]) //右半边当前元素小于左半边当前元素
num[k] = numm[j++];
else //右半边当前元素大于等于左半边当前元素
num[k] = numm[i++];
}
}
public static void MSort(Integer[] num ,Integer low,Integer high){
//递归归并,对每一小段进行归并排序
if(high <= low) return;
Integer mid = (low + high)/2;
MSort(num,low,mid);
MSort(num,mid+1,high);
//合并
Merge(num,low,mid,high);
}
快速排序
算法思想:将一个数组分为两个数组,两部分独立排序,当两个子数组都有序时整个数组也有序了
切分位置取决于数组的内容,切分位置左侧所有元素都不大于该元素,右侧都不小于该元素
切分办法:
先随意取num[low]为切分元素;
从左端向右扫描,找到大于等于该元素的位置;
从右端向左扫描,找到小于等于该元素的位置;
交换扫描到的两个数字;
如此继续,当两指针相遇,将切分元素num[low]与左子数组最右侧元素交换,并返回索引即可。
/*
快速排序
算法思想:将一个数组分为两个数组,两部分独立排序,当两个子数组都有序时整个数组也有序了
切分位置取决于数组的内容,切分位置左侧所有元素都不大于该元素,右侧都不小于该元素
切分办法:先随意取num[low]为切分元素 --> 从左端向右扫描,找到大于等于该元素的位置
--> 从右端向左扫描,找到小于等于该元素的位置
--> 交换扫描到的两个数字
--> 如此继续,当两指针相遇,将切分元素num[low]与左子数组最右侧元素交换,并返回索引即可
*/
//切分
public static int partition(Integer[] num, Integer low, Integer high){
int i = low, j = high+1; //左右扫描指针
Integer flag = num[low]; //切分元素
while(true){
while(num[++i] < flag) if(i == high) break;
while(flag < num[--j]) if(j == low) break;
if(i >= j) break;
exch(num,i,j);
}
exch(num,low,j); //将切分元素放入正确位置
return j; //返回切分索引
}
//快排
public static void Quick(Integer[] num,Integer low,Integer high){
if(low >= high) return;
int j = partition(num,low,high);
Quick(num,low,j-1);
Quick(num,j+1,high);
}
堆排序
算法思想:
将无序序列建成一个堆 (输出堆顶的最小(大)值)
使剩余的n-1个元素又调整成一个堆,则可得到n个元素的次小值
重复执行,得到一个有序序列
/*
堆排序
算法思想: 将无序序列建成一个堆 (--> 输出堆顶的最小(大)值)
-->使剩余的n-1个元素又调整成一个堆,则可得到n个元素的次小值
-->重复执行,得到一个有序序列
*/
//建立堆
public static void BuildHeap(Integer[] num,Integer N){
for (int k = N/2 ; k >= 0 ; k--){
sink(num,k,N); //初始化
}
}
//块下沉
public static void sink(Integer[] num,int k,Integer N){
while(2*k <= N){
int j = 2 * k; //顶点所对左孩子
if (j < N && (num[j] < num[j+1])) ++j; //判断左孩子与右孩子的大小
if (num[k] >= num[j]) break; //
exch(num,k,j);
k = j;
}
}
//堆排序
public static void Sort(Integer[] num){
Integer N = num.length-1;
BuildHeap(num,N);
while (N > 0){
exch(num,0,N--);
sink(num,0,N);
}
}