您的位置:首页 > 编程语言 > Java开发

常用排序算法总结---Java实现

2015-08-29 11:15 696 查看

各个排序总结,以及时间,空间复杂度分析
一.冒泡排序:
/*
冒泡排序:
排序算法思想:进行n-1趟排序,每趟,相邻元素,两两相互比较,将其中如果前一个元素比后一个元素小
则令其交换。(最后的结果是,小的往后移(从大到小的冒泡))

*/

class BubleSort
{
public static void main(String[] args)
{
int[] arr = {1,4,6,3,7,4,9,8};
bSort_1(arr);
for (int a: arr)
{
System.out.print(a+".");
}
}
/*
算法的优化:可以设置一个boolean类型的变,初值为false,如果发生交换,则将其改为true,如果某次
其值没有变化,仍为false,则可以提前结束。
*/
//从大到小的冒泡(外层控制趟数,内层)
public static void bSort(int[] arr)
{
boolean flag = false;
for (int i=0;i<arr.length-1 ;i++ )
{
flag = false;
for (int j=0;j<arr.length-i-1 ;j++ )
{
if(arr[j]<arr[j+1])
{
flag = true;
swap(arr,j,j+1);
}
}
if(!flag)
break;
}
}

//从小到大的冒泡(如果前一个数比后一个数大,则进行交换)
//2 4 6 7 5 3 4
//7
public static void bSort_1(int[] arr)
{
boolean flag = false;
for (int i=0;i<arr.length-1 ;i++ )
{
flag = false;
for (int j=arr.length-1;j>i+1 ;j-- )
{
if (arr[j]<arr[j-1])
{
flag = true;
swap(arr,j,j-1);
}
if(!flag)
break;
}
}
}
public static void swap(int[] arr,int i,int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}


二.选择排序

/*
简单选择排序:
排序算法的思想:对N个元素的数组,进行n-1趟排序,每趟中默认当前趟的数是最小(大)的,
然后进行趟内的比较。比较的元素,应该是从趟数+1开始,找出最大的,将其放在数组中已经是拍好
序的元素之后(也就是当前趟数的位置,即,将最大值与当前趟数对应元素进行交换位置)。
*/

class SelectSort
{
public static void main(String[] args)
{
int[] arr = {1,4,6,3,7,4,9,8};
sSort(arr);
for (int a: arr)
{
System.out.print(a+".");
}
}

public static void sSort(int[] arr)
{
for (int i=0;i<arr.length/*这里可以优化(少排一次):arr.lenth-1*/ ;i++ )
{
int index = i;
for (int j=i+1;j<arr.length ;j++ )
{
if (arr[index]>=arr[j])
{
index = j;
}
}

if (index!=i)
{
swap(arr,index,i);
}
}
}

public static void swap(int[] arr,int index,int i)
{
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}

三.快速排序

class QuickSort
{
public static void main(String[] args)
{
int[] arr = {50,10,90,30,70,40,80,60,20};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
quick(arr);
System.out.println("--------排序完成--------");
System.out.println(java.util.Arrays.toString(arr));
}
/*
快排。思想:对于数组中从Start--end索引范围的子序列进行处理,使之满足所有小于分界值的放在左边,所有大于分界值的放在右边(从两头最边缘开始),由于观察到每次都是在左序列或者右序列中操作,那么可以采用递归的方法来做
*/
private static void quick(int[] arr){
subSort(arr,0,arr.length-1);
}
private static void subSort(int[] arr,int start,int end){
//区间存在,说明需要排序
if(start<end){
//以第一个元素作为分界值
int base = arr[start];
//i从左边搜索,搜索大于分界值的索引
int i = start;
//j从右边搜索,搜索小于分界值的索引
int j = end+1;
while(true){
//--i是先运算在使用(所以j要从end+1开始,而i需要从start开始,因为start是基数)
//找到大于分界值的元素的索引,或者i已经到了end处
while(i < end && arr[++i] <= base);
//找到小于分界值的元素的索引,或者j已经到了start处
while(j>start && arr[--j] >= base);
if(i<j){
//说明区间尚未遍历完,将本次找到的两个数交换
swap(arr,i,j);
}else{
//说明该端区间进行一次快排完成
System.out.println(java.util.Arrays.toString(arr));
break;
}
}
//将基数base和序列中间值交换
swap(arr , start , j);
//递归快排左子序列
subSort(arr,start,j-1);
//递归快排右序列
subSort(arr,j+1,end);
}
}
private static void swap(int[] arr,int i,int j){
/*不用第三方变量完成两个数交换
原	理: a = 5 ,b = 3
a = a + b; //a = 3 + 5;
b = a - b; //b = 8 - 3 = 5;
a = a - b; //a = 8 - 5 = 3
*/
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
}
}


四.插入排序

class  InsertSort
{
public static void main(String[] args)
{
int[] arr = {50,10,90,30,70,40,80,60,20};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
insertSort(arr);
System.out.println("--------排序完成--------");
System.out.println(java.util.Arrays.toString(arr));
}
public static void	insertSort(int[] arr){
//直接插入排序。思:想每次将当前i元素插入到前n-i已经排好序的数组中(需要进行n-1次插入,第一个元素不需要插入)
for (int i = 1;i<arr.length ;i++ )
{
//记录当前的值,保证数组在后移的过程中不会丢失
int temp = arr[i];
//因为前i-1个数已经是排好的数列了,i-1最大,如果i比i-1还大那么说明还是有序的不需要插入操作
if(arr[i]<arr[i-1]){
int j = i -1;
//整体整体往后移一格
while(j>=0 && arr[j]>temp )
{
//循环判断和后移,如果当前元素大于temp,说明该位置不是temp该插入的位置,后移。
//直到找到一个元素,小于temp,说明该位置的后一位正是插入的位置
arr[j+1] = arr[j];
j--;
}
//找到位置插入
arr[j+1] = temp;
}
System.out.println(java.util.Arrays.toString(arr));
}
}
}

五.折半插入

class BinarySearchInsertSort
{
public static void main(String[] args)
{
int[] arr = {50,10,90,30,30,70,40,80,60,20};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
binarySearchInsertSort(arr);
System.out.println("--------排序完成--------");
System.out.println(java.util.Arrays.toString(arr));
}
/*
折半插入排序。思想:其实是对直接插入排序的改进。将要把当前元素要插入有序数列位置寻找,使用二分查找法代替
*/
public static void binarySearchInsertSort(int[] arr){
for (int i = 1; i<arr.length ; i++ )
{
//同样首先记住当前位置的值。
int temp = arr[i];
//同样只要在当前值小于有序列中最大值时需要判断插入位置,否则不需要插入
if(arr[i]<arr[i-1]){
//开始二分查找
int low = 0;
int high = i - 1;
//想法:这里对于mid下标的值和temp比较,如果遇到相等的情况,那么可以直接将此时的low值置为 mid.然后跳出循环.这样可以优化比较次数
while(low <= high){
int mid = (low + high) / 2;
if(temp>arr[mid]){
//限制索引在大于mid那一半找
low = mid + 1;
}else if(temp<arr[mid]){
//限制索引在小于mid那一半找
high = mid -1;
}else {
low = mid;
break;
}
}

//确定了当前的值的位置(low的位置),后移数组
for (int j = i; j>low ; j-- )
{
arr[j] = arr[j-1];
}
//将当前值插入到low位置
arr[low] = temp;
}
System.out.println(java.util.Arrays.toString(arr));
}
}
}


六.堆排序

//堆排序:主要分为建堆和,排序调整堆

class  HeapSort
{
public static void main(String[] args)
{
int[] arr = {9,79,46,30,58,49,22,44,12,23,45};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
heap(arr);
}
public static void heap(int[] arr){
//循环建堆(每次建好之后,排序完之后,需要调整堆。)
for (int i = 0;i<arr.length ;i++ )
{
//建堆
buildMaxHeap(arr,arr.length-i-1);
//交换堆顶和最后一个元素
swap(arr,0,arr.length-1-i);
System.out.println(java.util.Arrays.toString(arr));
}
}
//建大根堆
private static void buildMaxHeap(int[] arr,int lastIndex){
//从lastIndex节点的父节点开始
for (int i=(lastIndex-1)/2;i>=0 ;i-- )
{
//K保存当前正在判断的节点
int k = i;
//如果当前K节点的子节点存在(循环判断两个子节点与当前节点值的大小)(这里为什么要使用while循环,是因为当前堆交换后下面的堆顺序可能会乱,需要再次向下)
while(k*2+1<=lastIndex){
//k节点的左子节点的索引
int biggerIndex = 2*k+1;
if((biggerIndex+1)<=lastIndex){
//k节点的右子节点存在
if(arr[biggerIndex]<arr[biggerIndex+1]){
//右子节点的值较大(使用biggerIndex记录较大的节点)
biggerIndex++;
}
}

//如果K节点的值小于其较大子节点的值
if(arr[k]<arr[biggerIndex]){
//交换他们
swap(arr,k,biggerIndex);
//将biggerIndex的值赋给K节点,然后开始下一次的while循环(判断该子节点下的子节点有比当前最大节点大的值)
//从新保证K节点的值大于其左右子节点的值
k = biggerIndex ;
} else{
break;
}
}
}
}

private static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

七.Shell排序

class ShellSort
{
public static void main(String[] args)
{
int[] arr = {50,10,90,30,70,40,80,60,20,110,800,200,100,12};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
shellSort(arr);
System.out.println("--------排序完成--------");
System.out.println(java.util.Arrays.toString(arr));
}
public static void shellSort(int[] arr){
/*
shell排序。思想:shell是直接插入排序的已近改进。只是将直接插入排序的每次插入增量1。改为h(shell值),然后通过一定规律递减h,进行插入排序,直到h<0时。为什么要这样做?因为插入排序有个特点,如果序列基本有序时(小元素在前面,大元素在后面,中等元素在中间),那么我们对于序列的只需要很少移动即可。一般关于h的取值: h = length , h = h / 3 + 1;
*/
//确定shell值
int h = 1 ;
while (h <= arr.length/3)
{
h = h*3 + 1;
}
//shell值>0,就继续进行插入排序
while(h>0){
//一趟以h为间距的插入排序完成。
System.out.println("当前h的值" + h);
for (int i=h;i<arr.length ; i++)
{
//以h为间距进行插入排序

//同样先记住当前要插入的值
int temp = arr[i];
//如果当前值比有序列中的最大值(i-1)都要大,说明位置正确,不需要进行插入,否则插入
if(temp<arr[i-h]){
int j = i - h;
//以h为间距后移,如果arr[j]大于temp说明插入位置没有找到,该位置后移
while (j>=0 && arr[j]>temp)
{
arr[j+h] = arr[j];
j = j - h;
}
arr[j+h] = temp;
}
System.out.println(java.util.Arrays.toString(arr));
}
h = (h - 1) / 3;
}
}

}

八.归并排序

class  MergeSort
{
public static void main(String[] args)
{
int[] arr = {50,10,90,30,70,40,80,60,20};
System.out.println(java.util.Arrays.toString(arr));
System.out.println("--------排序开始--------");
mergeSort(arr);
System.out.println("--------排序完成--------");
System.out.println(java.util.Arrays.toString(arr));
}

public static void mergeSort(int[] arr){
/*
归并排序.思路:算法有一种分治思路就是将大问题化为若干个小问题,然后每个小问题的解组合起来,就是大问题的解,归并排序就是采用了这样的思路。对一系列数据排序,不断对半分解,直到每组一个元素,那么直接排。然后将排好序的分组,归并起来。
所以分两步,分解,合并。关于分解我们可以采用递归取做。
*/
sort(arr,0,arr.length-1);
}
//将索引从left到right范围的数组元素进行归并排序.(第一步分解)
private static void sort(int[] arr,int left,int right){
//存在区间,可以进行排序
if(left<right){
//找到中间索引,以中间索引为界,分解
int center = (left+right) / 2;
//对左部分数组进行递归排序
sort(arr,left,center);
//对右部分数组进行递归排序
sort(arr,center+1,right);
//开始归并(当left=right时开始)
merge(arr,left,center,right);
}
}
//将left到center 和 center+1到right的数组进行合并(第二步合并)
//将两个数组进行归并,归并前两个数组已经有序,归并之后依然有序。
private static void merge(int[] arr,int left,int center,int right){
//需要创建一个同等大小的辅助数组(中间数组),用来保存归并之后的数组
int[] tempArr = new int[arr.length];
int mid = center + 1;
//third记录中间数组(辅助数组)的索引
int third = left;
//记录元数组的起始下标(用于最后复制回元数组用)
int temp = left;

//两个数组中都还有未遍历到的值
while(left<=center && mid<=right){
//从两个数组中取出小的放入到中间数组
if(arr[left]<arr[mid]){
tempArr[third++] = arr[left++];
}else{
tempArr[third++] = arr[mid++];
}
}
//上面两个数组可能有一个数组还有值没有遍历到。接下来将可能剩余的数组放入到中间数组
while (left<=center){
tempArr[third++] = arr[left++];
}
while(mid<=right){
tempArr[third++] = arr[mid++];
}
//以上已经将两个数组归并到同一个数组中。
//将中间数组的内容复制会原数组(原left--right范围的内容被复制回元数组),组成为一个更大有序集合
while (temp<=right)
{
arr[temp] = tempArr[temp++];
}
//此时一个小区间归并完毕.
System.out.println(java.util.Arrays.toString(arr));
}
private static void swap(int[] arr, int i ,int j){
/*
不借助第三方变量,交换两个变量的值
a = a ^ b;
b = a ^ b;// b = (a ^ b) ^ b = a;
a = a ^ b;// a = a ^ (a ^ b) = b;
*/
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}


九.对常用算法的时空复杂度,稳定性分析。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息