排序算法一览表
1.冒泡排序[稳定]
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
public static int[] Bubblesort(int []arr){
for(int i =0;i<arr.length-1;i++){//外层控制排序的趟数
for(int j = i;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;
}
}
}
return arr;
}
2.选择排序[不稳定]
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
public static int[] selectSort(int []arr){
for(int i =0;i<arr.length;i++){
int minIndex =i;
for(int j = i+1;j<arr.length;j++){
if(arr[j]< arr[minIndex]){
minIndex = j;
}
}
int temp =arr[i];
arr[i] =arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
3.插入排序[稳定]
每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
public static int[] insertSort(int []arr){
for(int i =1;i<arr.length;i++){
for(int j = i;j>0;j--){
//后一个数小于前一个数则交换
if(arr[j]<arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
return arr;
}
4.归并排序[稳定]
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6 , 202 , 100 , 301 , 38 , 8 , 1
第一次归并后:{6,202} , {100,301}, {8,38}, {1} ,比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
public static void mergeSort(int arr[],int low,int high){
if(low<high){
int mid = (low+high)/2;
mergeSort(arr,low,mid);
mergeSort(arr,mid+1,high);
//两路归并
merge(arr,low,mid,high);
}
}
private static void merge(int[] arr, int low,int mid,int high) {
int []tmp = new int[high-low+1];//汇总两个有序区的临时区域
int i = low;//左边序列起始索引
int j = mid+1;//右边序列起始索引
int k = 0; //临时区域的索引
//把较小的数先移到新数组中
while(i<=mid && j<= high){
if(arr[i]<arr[j]){
tmp[k++] = arr[i++];
}else{
tmp[k++] = arr[j++];
}
}
//若左边序列还有剩余,则将其全部拷贝进tmp[]中
while(i <= mid){
tmp[k++] = arr[i++];
}
//若右边序列还有剩余,则将其全部拷贝进tmp[]中
while(j <= high){
tmp[k++] = arr[j++];
}
// 将排序后的元素,全部都整合到数组中。
for(int t = 0;t < tmp.length ;t++)
arr[t+low] = tmp[t];
}
5.计数排序[稳定]–升级为桶排序
工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来记得到有序序列
public static void countSort(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
//桶数
int bucketNum = max + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0;i<bucketNum;i++){
bucketArr.add(new ArrayList<Integer>());
}
//将每个元素放入桶
for(int i = 0;i<arr.length;i++){
bucketArr.get(arr[i]).add(arr[i]);
}
//对每个桶进行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
for(int i = 0;i<bucketArr.size();i++){
if(!bucketArr.get(i).isEmpty()){
for(Integer a:bucketArr.get(i))
System.out.print(a+" ");
}
}
}
6.基数排序[稳定]
将整数按位数切割成不同的数字,然后按每个位数分别比较。
准备10个桶,按照个位将数放入桶中,在倒出来,再按照十位数放入桶中再倒出来…
public static void Redixsort(int[] arr, int d)//d表示最大的数有多少位
{
int k = 0; //保存每一位排序后的结果用于下一位的排序输入
int n = 1; //表示位数对应的数
int len = arr.length;
int [][]bucket = new int[10][len];//排序桶,用于保存每次排序后的结果,这样位排序结果相同的数字放在同一个桶里
int []order = new int[len]; // 用于保存每个桶里面有多少个数字
while(n< Math.pow(10,d)){
for(int i = 0;i<arr.length;i++){//将数组array里的每个数字放在相应的桶里
int digit = (arr[i]/n)%10; //求位上的数
bucket[digit][order[digit]] = arr[i];
order[digit]++;
}
//将前一个循环生成的桶里的数据覆盖到原数组中,用于保存这一位的排序结果
for(int i = 0;i<len;i++){
//桶里有数据
if(order[i]!=0){
for(int j = 0;j<order[i];j++){
arr[k] = bucket[i][j];
k++;
}
}
//桶里计数器计0,用于下一次排序
order[i] =0;
}
n =n*10;
k = 0; //将k置0,用于下一轮保存位排序结果
}
}
7.快速排序[不稳定]
选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
简单记忆为:
“挖坑填数+分治法”,首先令i =L; j = R; 将a[i]挖出形成第一个坑,称a[i]为基准数。然后j–由后向前找比基准数小的数,找到后挖出此数填入前一个坑a[i]中,再i++由前向后找比基准数大的数,找到后也挖出此数填到前一个坑a[j]中。重复进行这种“挖坑填数”直到i==j。再将基准数填入a[i]中,这样i之前的数都比基准数小,i之后的数都比基准数大。
public static void quicksort(int arr[], int low, int high)
{
if(low < high)
{
int position = partition(arr, low, high);
quicksort(arr, low, position - 1);
quicksort(arr, position + 1, high);
}
}
public static int partition(int arr[], int low, int high) {
//设置基准值
int key = arr[low];
while(low < high){
//从右到左,直到找到一个小于key的值
while(low < high && arr[high] >= key) --high;
//将该值填入前的坑
arr[low] = arr[high];
//从左到右,直到找到一个大于key的值
while(low < high && arr[low] <= key) ++low;
//将该值填入前一个坑
arr[high] = arr[low];
}
//将基准值填入最后一个坑
arr[low] = key;
//最后一个坑划分了左边小于该值,右边大于该值
return low;
}
8.希尔排序[不稳定]–插入排序的优化
现在有一个array,希尔排序就是设定一个增量incrementNum (0<incrementNum<array.length)
(1)先从array[0]开始,以incrementNum为增量的进行直接插入排序,直到数组末尾,然后从array[1]开始重复:以incrementNum为增量的进行直接插入排序; 然后从array[1]开始重复…一直到array[n]。
然后取一个小于上一步增量的新的增量(比如设置为incrementNum/2),对前一个步骤的结果array进行遍历,直接插入排序…
再取小于上一步增量的新的增量,重复进行:遍历,直接插入排序,直到新的增量小于1之后再退出循环
public static void shellSort(int []arr){
int incrementNums = arr.length/2;
while(incrementNums>=1){
for(int i = 0;i<arr.length;i++){
//进行插入排序
for(int j =i+incrementNums;j<arr.length;j+=incrementNums){
if(arr[j]<arr[j-incrementNums]){
int tmp = arr[j];
arr[j] =arr[j-incrementNums];
arr[j-incrementNums] = tmp;
}
}
}
//设置新的增量
incrementNums =incrementNums/2;
}
}
9.堆排序[不稳定]
public static void HeapSort(int []arr){
int len = arr.length;
//叶子节点为(n/2+1~n,每个叶子本身就是一个大根堆),故需要遍历的非叶子结点数位len/2
for(int i = len/2 ; i>=0;i-- ){
//从第一个非叶子结点从下往上,从右至左调整,构造大根堆
// i为最后一个根节点,n为数组最后一个元素的下标
HeapAdjust(arr,i,len-1);
}
//调整堆结构+交换堆顶元素与末尾元素
for(int i = len-1;i>0;i--){
//最后一个元素跟第一个元素交换
int tmp = arr[i];
arr[i] = arr[0];
arr[0] = tmp;
HeapAdjust(arr,0,i);
}
}
//大根堆的构建
private static void HeapAdjust(int[] arr, int parent, int length) {
int largest = parent;
//获得左孩子
int leftchild = 2 * parent + 1;
int rightchild = 2* parent + 2;
if(leftchild<length && arr[leftchild]>arr[largest]){
largest= leftchild;
}
if(rightchild<length && arr[rightchild]>arr[largest]){
largest = rightchild;
}
if(parent != largest){
int temp = arr[parent];
arr[parent] = arr[largest];
arr[largest]= temp;
//递归
HeapAdjust(arr,largest,length);
}
}