Java实现常见排序算法
日常操作中常见排序方法有:冒泡排序、快速排序、选择排序、插入排序、堆排序等
以下是各排序算法定义及代码实现:
1、冒泡排序(Bubble Sort):是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来,直到没有相邻元素需要交换,也就是说该元素已经排序完成。该排序算法不稳定
/**
* 冒泡排序算法普通版(由小到大排列)
* 依次比较相邻的两个元素,如果位置相反就交换,直到没有需要交换的元素为止
* 时间复杂度:O(n^2)
* 该排序算法不稳定
*
* @param arr
*/
public static void bubbleSort(int[] arr){
int temp;
for(int i = 1; i < arr.length; i++){
for(int j = 0; j < arr.length - 1; j++){
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
以上冒泡排序的时间复杂度始终为O(n^2),现在对代码进行优化
/**
* 冒泡排序算法优化版
* 时间复杂度:最好O(n),最坏平均O(n^2)
*/
public static void bubbleSort01(int[] arr){
int flag = 1;//设置标志位
for(int i = 1; i < arr.length; i++){//外层循环控制循环次数
for(int j = 0; j < arr.length-1; j++){//内层循环控制数据交换
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = 0;//当存在数据交换的时候就将flag置为0
}
}
if(flag == 1){
//内层循环结束没有数据交换说明剩下的数据有序,不需要再次进入循环
return;
}
}
}
2、快速排序(Quicksort)是对冒泡排序的一种改进。
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,以此达到整个数据变成有序序列。
/**
* 快速排序递归实现(由小到大)
* 时间复杂度:最好平均O(nlogn),最坏O(n^2)
* 最坏情况为,每次所取得的基准值都是需要排列数据的最大值
* 该排序算法不稳定
* @param arr
* @param start
* @param end
*/
public static void quickSort(int[] arr, int start, int end){
if(start > end){//排序完成
return;
}
int div = arr[start];//取数组第一个数为基准值
int left = start;
int right = end;
while (left <= end){
while (left < right && arr[left] <= div){
//判断使得基准值左边的数都比基准值小
left++;
}
//比基准值大的数据与右边小的数据交换
arr[right] = arr[left];
while (left < right && arr[right] < div){
//判断使得基准值右边的数据都比基准值大
right--;
}
//比基准值小的数据与左边大的数据交换
arr[left] = arr[right];
}
arr[right] = div;//放置基准值
//递归排列基准值两边的数据
quickSort(arr, 1, left-1);
quickSort(arr, left+1, right);
}
3、选择排序(Selection sort) 是一种简单直观的排序算法。
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。
/**
* 选择排序(由小到大)
* 找出当前序列的最小值,放在数据最开始,再寻找剩余数据的最小值放在已经排序完成的后面以此类推
* 时间复杂度:O(n)
* 该排序算法不稳定
* @param arr
*/
public static void selectSort(int[] arr){
for(int i = 0; i < arr.length-1; i++){
int k = i;
for(int j = arr.length-1; j > i; j--){
if(arr[j] < arr[k]){
k = j;
}
}
int temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
4、插入排序(insert sort)
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。
/**
* 直接插入排序(由小到大)
* 稳定排序
* @param arr
*/
public static void insertSort(int[] arr){
for(int i = 1; i < arr.length; i++){
int j;
int temp = arr[i];//存放本次循环需要排序的数
for(j = i-1; j>=0 && arr[j]>temp; j--){
//在已经有序的数据中找到该数据的正确位置,并将其他数据向后移动
arr[j+1] = arr[j];
}
arr[j+1] = temp;//插入该数据
}
}
插入排序还可以使用分治的思想完成
/**
* 二分法插入排序
* @param arr
*/
public static void insertSort1(int[] arr){
for(int i=1;i<arr.length;i++){
int temp = arr[i];//当前循环需要排序的数据
int low=0;
int high=i-1;
while(low <= high){
int mid = (low+high)/2;//找到中间位置
if(arr[mid]>temp){
high = mid-1;
}else{
low = mid+1;
}
}
for(int j=i-1;j>=low;j--){
//数据向后移动
arr[j+1] = arr[j];
}
//插入该数据
arr[low] = temp;
}
}
5、堆排序:Heapsort是指利用堆这种数据结构所设计的一种排序算法
堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的索引总是小于(或者大于)它的父节点。
/**
* 堆排序
* 通过完全二叉树的结构建大堆,由此找到最大元素
* 时间复杂度:O(nlogn)
* @param arr
*/
public static void Heapsort(int []arr){
//建堆
for(int i=arr.length/2-1;i>=0;i--){
//第一个非叶子结点从下至上,从右至左调整结构
adjustDown(arr,i,arr.length);
}
//将堆顶元素与最后一个交换
for(int j=arr.length-1;j>0;j--){
int temp = arr[0];
arr[0] = arr[j];
arr[j] = temp;
adjustDown(arr,0,j);//向下调整
}
}
//堆调整
public static void adjustDown(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1; k<length; k=k*2+1){//从i结点的左子结点开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点
arr[i] = arr[k];
i = k;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
测试代码:
public static void main(String []args){
int[] a = {3,1,4,2,8,5,9,7,6};
//bubbleSort(a);
//quickSort(a, 0, a.length-1);
//selectSort(a);
//insertSort1(a);
Heapsort(a);
print(a);
}
//打印数组
public static void print(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}