排序算法的评价标准主要是从两方面:1.时间复杂度2.空间复杂度
稳定排序算法只有三种:也就是 冒泡排序,归并排序,以及直接插入排序
插入排序:每一趟将一个待排序的记录,按其关键字的大小插入到已经排序好一组记录的适当位置,直到所有待排序记录全部插入为止;
public static void charusort(int[] arr){
int temp,j;
for(int i=1;i<arr.length;i++){
if(arr[i]<arr[i-1]){
temp=arr[i];
arr[i]=arr[i-1];
// arr[i-1]=temp;
//从后往前寻找第一个比它小的元素,将其往后移,然后将该元素插入到空闲的位置
for(j=i-1;j>=0&&arr[j]>temp;j--){
arr[j+1]=arr[j];
}
arr[j+1]=temp;
System.out.println("第"+i+"趟排序的结果是:"+ Arrays.toString(arr));
}
}
}
插入排序又可以分为三种:
直接插入排序,折半插入排序以及希尔排序。
下面依次介绍这三种排序:
直接插入排序:其时间复杂度为O(n2),空间复杂度为O(1);
比较适合于链式结构,无需移动位置,只需移动指针即可;(如果使用顺序结构存储数据的话 适用于有序数组)
折半插入排序:其主要是在查找的过程中,不是按照直接插入排序一般,主要耗时在关键字的比较上,因此这种排序适合无序的数组进行排序;
交换排序:又包括冒泡排序和快速排序
冒泡排序:常见的冒泡排序算法以及其优化
package com.study.exer;
public class Maopao {
public static void main(String[] args){
int[] arr=new int[]{8,5,7,6,4,3,1,2};
Bubblesort(arr,0,arr.length-1);
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+" ");
}else{
System.out.print(arr[i]);
}
}
}
public static void Bubblesort(int[] arr,int left,int right){
//设置一个变量用来标记是否已经交换过
//是因为当数据基本有序的时候 如果没有交换过的话就不需要对其进行排序,直接跳出得到排序后的结果;
boolean flag=false;
int temp;
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
flag=true;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if(!flag){
break;
}else{
flag=false;
}
}
}
}
快速排序:基本思想:就是选定pivot中心轴,将大于pivot的关键字放在pivot的右边,将小于pivot的关键字放在其左边,分别再对左右子序列重复前面的步骤;最坏时间复杂度是O(n2),也就是排序列表基本有序的时候,所以最坏情况下的空间复杂度也为O(n)
public class Partitionsort {
public static void main(String[] args){
int[] arr=new int[]{8,5,7,6,4,3,1,2};
Quicksort(arr,0,arr.length-1);
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+" ");
}else{
System.out.print(arr[i]);
}
}
}
public static void Quicksort(int[] arr,int left,int right){
if(left<right){
int pivot=partition(arr,left,right);
Quicksort(arr,left,pivot-1);
Quicksort(arr,pivot+1,right);
}
}
public static int partition(int[] arr,int left,int right){
//一般都选择第一个数字为枢轴数字
int pivot_1=arr[left];
while(left<right){
while(left<right&&arr[right]>=pivot_1) right--;
arr[left]=arr[right];
while(left<right&&arr[left]<pivot_1)left++;
arr[right]=arr[left];
}
arr[left]=pivot_1;
return left;
}
}
基于上述的最坏时间复杂度下,所以考虑对快排进行优化:也就是根据三数取中法,主要思想是取合适的枢轴元素,取left,mid,right中关键字居中的数作为枢轴元素并将其放置到元素末尾,然后依次进行比较从左开始找到第一个比枢轴大的数,从右开始找到第一个比枢轴小的元素,然后将其进行交换,并将枢轴数放置到合适的位置上;代码如下:
package com.study.exer;
public class QuickSort_youhua {
public static void main(String[] args){
//s三数取中法
int[] arr=new int[]{8,4,5,7,1,3,6,2};
// Runtime cur= getRuntime();
Quicksort(arr,0,arr.length-1);
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+" ");
}else{
System.out.print(arr[i]);
}
}
}
public static void Quicksort(int[] arr,int left,int right){
if(left<right){
dealPivot(arr,left,right);
//获取枢轴元素
int pivot=right-1;
int i=left,j=right-1;
while(true){
while(arr[++i]<arr[pivot]){
}
while(j>left&&arr[--j]>arr[pivot]){}
if(i<j){
swap(arr,i,j);
}else{
break;
}
}
if(i<right){
swap(arr,i,right-1);
}
Quicksort(arr,left,i-1);
Quicksort(arr,i+1,right);
}
}
public static void dealPivot(int[] arr,int left,int right){
int mid=(left+right)/2;
//选取三者中的最大值;也就是通过相应的数组的交换
if(arr[left]>arr[mid]){
swap(arr,left,mid);
}
if(arr[left]>arr[right]){
swap(arr,left,right);
}
if(arr[mid]>arr[right]){
swap(arr,mid,right);
}
//也就是将枢轴元素放在最后一位;
swap(arr,mid,right-1);
}
public static void swap(int[] arr,int a,int b){
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
}
选择排序又分为简单选择排序和堆排序两种:
**简单选择排序:**其基本思想是每一趟从待排序的数组中选出关键字最小的记录,按顺序放在已经排好序的记录序列的最后,直到全部排完为止;
public static void selectsort(int[] arr){
int temp,index;
for(int i=0;i<arr.length;i++){
temp=arr[i];
index=i;
for(int j=i+1;j<arr.length;j++){
if(temp>arr[j]){
temp=arr[j];
index=j;
}
}
if(index!=i){
arr[index]=arr[i];
arr[i]=temp;
}
}
}
简单选择排序的优化,其主要思想是每次可以找到最小值也可以找到最大值;这样的话其整体的找的时间会减少到一半
public static void selectsort(int[] arr){
int left=0,right=arr.length-1;
int min,max,minindex,maxindex,temp;
// int min=arr[left],max=arr[right];
for(int i=0;i<arr.length/2;i++){
min=arr[left];
max=arr[right];
minindex=left;
maxindex=right;
if(min>max){
temp=arr[left];
arr[left]=arr[right];
arr[right]=temp;
min=arr[left];
max=arr[right];
}
for(int j=left+1;j<right;j++) {
if (arr[j] < min) {
min = arr[j];
minindex = j;
}
if (arr[j] > max) {
max = arr[j];
maxindex = j;
}
}
if(minindex!=left){
arr[minindex]=arr[left];
arr[left]=min;
}
if(maxindex!=right){
arr[maxindex]=arr[right];
arr[right]=max;
}
left++;
right--;
System.out.println("第"+i+"趟排序的结果为:"+ Arrays.toString(arr));
}
}
堆排序:
package com.study.exer;
import java.util.Arrays;
public class Heapsort {
public static void main(String[] args) {
int[] arr=new int[]{8,5,7,6,4,3,1,2};
//首先建立大根堆或者小根堆,这里我选择建立大根堆,大根堆的定义就是 其每一颗左子树及其右子树的值都小于根节点的值
//从最后一个非叶子节点开始;
for(int i=arr.length/2-1;i>=0;i--){
heapadjust(arr,i,arr.length);
}
for(int i=arr.length-1;i>0;i--){
int temp=arr[i];
arr[i]=arr[0];
arr[0]=temp;
heapadjust(arr,0,i);
System.out.println("第"+i+"趟排序的结果是:"+ Arrays.toString(arr));
}
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+" ");
}else{
System.out.print(arr[i]);
}
}
}
public static void heapadjust(int[] arr,int parent,int len){
int temp=arr[parent];
for(int j=2*parent+1;j<len;j=2*j+1){
if(j+1<len&&arr[j+1]>arr[j]) ++j;
//如果得到的数序需要对其左右脚进行交换,
if(temp>=arr[j]) break;
arr[parent]=arr[j];
parent=j;
}
arr[parent]=temp;
}
}
归并排序: 将两个或者两个以上的有序表合并成一个有序表的过程,它的思想是假设初始序列含有n个记录,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或者1的有序子序列,再两两归并,如此重复直到得到一个长度为n的有序序列为止;
采用经典的分治法,
1.将序列中的待排序数组分为若干组,每个数字分为一组,将若干个数组两两合并,保证合并后的数组是有序的 3重复第二步操作直到只剩下一组,排序完成;
该算法的时间复杂度是O(nlogn) 其最好和最坏的时间复杂度都是O(nlogn);空间复杂度是O(n);
将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列;
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并
public class Guibingsort {
public static void main(String[] args){
int[] arr=new int[]{8,4,5,7,1,3,2,6};
int[] temp=new int[arr.length];
mergesort(arr,temp,0,arr.length-1);
for(int i=0;i<arr.length;i++){
if(i!=arr.length-1){
System.out.print(arr[i]+" ");
}else{
System.out.print(arr[i]);
}
}
}
public static void mergesort(int[]arr,int[]temp,int left,int right){
if(left<right){
int mid=(left+right)/2;
mergesort(arr,temp,left,mid);
mergesort(arr,temp,mid+1,right);
merge(arr,temp,left,mid,right);
}
}
public static void merge(int[] arr,int[] temp,int left,int mid,int right){
int i=left,j=mid+1,t=0;
while(i<=mid&&j<=right){
if(arr[i]<=arr[j]){
temp[t++]=arr[i++];
}else{
temp[t++]=arr[j++];
}
}
while(i<=mid) temp[t++]=arr[i++];
while(j<=right) temp[t++]=arr[j++];
//将新数组拷贝到原数组中
t=0;
while(left<=right){
arr[left++]=temp[t++];
}
}
}
归并排序与快速排序的区别是:归并排序的递归是在函数一开始的时候,将数组分为两部分,这两部分数据有序之后再进行合并,此时整个数列是有序的,而对于快速排序来说是函数在结束的时候才进行递归,也就是先将一个元素放置到正确的位置,随后再以此位置为枢轴,排序其左右两部分;