经典排序算法java实现

目录

1 插入排序

2 希尔排序

3 冒泡排序

4 快排

5 简单选择排序

6 堆排序

7 归并排序

8 基数排序

//9 计数排序

//10 桶排序


 

推荐:https://www.cnblogs.com/guoyaohua/p/8600214.html

 

【知识框架】:

稳定性:如果a原本在b前面,而a=b,排序之后a仍然在b的前面,则称这个算法是稳定的,否则成为不稳定。注意,算法是否具有稳定性并不能衡量一个算法的优劣,它主要是对于算法的性质进行描述。

 

1 插入排序

1.1 算法描述

每次讲一个待排序的记录,按其关键字的大小插入到前面已经排好序的子序列中,直到全部记录插入完成。

步骤:

有序序列L[1...i-1]L(i)无序序列L[i+1...n]

为了实现将元素L(i)插入到有序序列L[1...i-1]中,我们需要执行以下操作:

  • 第一个位置的元素被认为是已经排好序;
  • 取出下一个元素L(i)
  • i-1位置往前扫描,找到第一个比L(i)小的数,插入其后面的位置k
  • L[k...i-1]全部后移一个位置;
  • L(i)复制到L(k)

1.2 代码

	//一、插入排序
	static void InsetSort(int a[], int length) {
		int i,j;
		for(i=1; i<length ; i++) {
			j=i-1;
			int temp=a[i];
			while(j>=0 && a[j]>temp ){
				a[j+1]=a[j];
				j--;
			}
			a[j+1]=temp;			
		}
	}

1.3 复杂度分析 

最佳情况:表中元素已经有序,每插入一个元素都只需比较一次而不用移动元素,时间复杂度 O(n)  ,

最坏情况:逆序,总比较次数达到最大,为(2+3+4+...+n),时间复杂度 O(n2)   

平均情况:O(n2)

 

2 希尔排序

2.1 算法描述

直接插入排序适用于基本有序的排序表和数据量不大的排序表。基于这两点,1959年D.L.Shell提出了希尔排序,又称缩小增量排序。

先将待排序表分成若干个形如L[i, i+d, i+2d,..., i+kd] 的特殊子表,再分别进行直接插入排序,当整个表基本有序时,再对全体记录进行一次直接插入排序。

  • 先取一个小于n的步长d1,把表中全部记录分成d1组,所有距离为d1的倍数的记录放在同一组,对这些组分别进行直接插入排序;
  • 然后取下一个更小的步长d2,重复上述操作,知道最后的步长dt=1,即所有记录分成一组,进行直接插入排序,由于此时已经具有良好的局部有序性,故可以很快得到最终结果。
  • 步长:希尔提出的方法是d1=n/2,d2=(n/2)/2,...d(i+1)=di/2
  • 步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。

 

2.2 代码

	//二、希尔排序
	static void ShellSort(int a[], int length) {
		for (int i = length / 2; i >= 1; i /= 2) { //步长i
			for(int j=i; j<length; j++) {               //i组
				int temp=a[j];
				int m=j-i;
				while(m>=0&&temp<=a[m]) {
					a[m+i]=a[m];
					m=m-i;				
				}
				a[m+i]=temp;
			}

		}
	}

2.3 复杂度分析 

当n在某个特定范围时,希尔排序时间复杂度约为 O(n1.3),最坏情况:T(n) = O(n2)  

稳定性:不稳定。

 

3 冒泡排序

3.1 算法描述

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

-----------维基百科冒泡排序

3.2 代码

        //三、冒泡排序
	static void BubbleSort(int a[], int length) {
		int flag=1;
		for(int i=1; flag==1 && i<length; i++) {
			flag=0;
			int temp;
			
			for (int j = 0; j + 1 < length - i; j++) {
				if (a[j] > a[j + 1]) {
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
					flag = 1;

				}

			}
		}
	}

3.3 复杂度分析 

最佳情况:T(n) = O(n)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(n2)

 

4 快排

4.1 算法描述

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。

步骤为:

  1. 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),
  2. 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成,
  3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。

选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。

-----------维基百科快排

4.2 代码

//四、快排
	static void QuickSort(int a[], int low, int high) {
		//int pivot=a[0];
		//int low=0;
		//int high=length-1;
		int pivot=0;
		if(low<high) {
			pivot=partition(a,low,high);
		
			for(int i:a)
				System.out.print(i+" ");
			System.out.println();
			//分治
			QuickSort(a, low, pivot-1);
			QuickSort(a, pivot+1, high);

//			
		}
		
	}

	static int partition(int a[], int low,int high) {
		int pri=a[(low+high)/2];//选中点为基准点
		while (low < high) {
			while (low < high && a[high] >= pri) {//从右到左找到第一个小于pri的数
				--high;
			}
			a[low] = a[high];

			while (low < high && a[low] <= pri) {//从左到右找到第一个大于pri的数
				++low;
			}
			a[high] = a[low];
		}
		a[low]=pri;
		return low;
	}

4.3 复杂度分析 

最佳情况:T(n) = O(nlogn)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(nlogn) 

 

5 简单选择排序

5.1 算法描述(维基百科选择排序

  • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
  • 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的后面。
  • 以此类推,直到所有元素均排序完毕。

5.2 代码

	//五、简单选择排序
	static void SelectSort(int a[], int n) {
		
		for(int i=0;i<n-1;i++) {
			int min=i;
			for(int j=i+1;j<n-1;j++) {
				if(a[j]<a[min])
					min=j;
				
			}
			//System.out.println("a["+min+"]="+a[min]);
			if(min!=i) {
				int temp=a[i];
				a[i]=a[min];
				a[min]=temp;
				
			}
		}
	}

5.3 复杂度分析 

最佳情况:T(n) = O(n2)  最差情况:T(n) = O(n2)  平均情况:T(n) = O(n2)

 

6 堆排序

6.1 算法描述(维基百科)

概述:

若以升序排序说明,把数组转换成大根堆(Max-Heap Heap),这是一种满足最大堆积性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。

重复从最大堆积取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆积维持最大堆积性质。

堆节点的访问:

通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:(i=1,2,...n)

  • 父节点 i 的左子节点在位置(2i)
  • 父节点 i 的右子节点在位置(2i+1)
  • 子节点 i 的父节点在位置floor(i/2)

堆的操作:

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

  • 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
  • 创建最大堆(Build Max Heap):将堆中的所有数据重新排序
  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

6.2 代码

        //堆排序:建堆
	static void  BuildHeap(int a[], int len) {
		for(int i=len/2;i>0;i--) {
			AdjustDown(a,i,len);
		}
		
	}
	
	static void AdjustDown(int a[], int k, int len) {
		a[0]=a[k];
		for(int i=2*k;i<=len;i=i*2) {
			if(i<len && a[i]<a[i+1]) {
				i++;		
			}
			if(a[0]>=a[i])
				break;
			else{
				a[k]=a[i];
				k=i;
			}						
		}
		a[k]=a[0];
	}
	
	static void AdjustUp(int a[], int k, int len) {
		a[0]=a[k];
		for(int i=k/2; i>0 && a[i]<a[0]; i=i/2) {
			a[k]=a[i];
			k=i;
		}
		a[k]=a[0];
		
	}
	//六、堆排序
	static void HeapSort(int a[], int len) {
		BuildHeap(a,len);
		for(int i=len; i>1; i--) {
			a[0]=a[1];
			a[1]=a[i];
			a[i]=a[0];
			AdjustDown(a,1,i-1);
		}
	}

6.3 复杂度分析 

最佳时间:O(nlogn)  最坏时间:O(nlogn)  平均时间:O(nlogn)

空间复杂度:O(1)

 

7 归并排序

7.1 算法描述

采用分治法:

  • 分割:递归地把当前序列平均分割成两半。两半分别执行归并排序
  • 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。

7.2 代码

	//七、归并排序
	static void MergeSort(int a[], int low, int high) {
		if(low<high) {
			int mid=(low+high)/2;
			MergeSort(a,low,mid);
			MergeSort(a,mid+1,high);
			Merge(a,low,mid,high);
		}
	}
	static void Merge(int a[], int low, int mid, int high) {
		int i,j,k;   
		int[] b = new int[high+1]; 
		for(i=0; i<=high; i++) {
			b[i]= a[i];
		}
		
//		for(int m:b) {
//			System.out.print(m+" ");
//		}
//		System.out.println();
//		
		for(i=low,j=mid+1,k=i; i<=mid && j<=high; k++) {
			if(b[i]<=b[j]) {
				a[k]=b[i++];
			}else {
				a[k]=b[j++];
			}
		}
		while(i<=mid) {
			a[k++]=b[i++];
		}
		while(j<=mid) {
			a[k++]=b[j++];
		}
	}

7.3 复杂度分析

最佳情况:T(n) = O(n)  最差情况:T(n) = O(nlogn)  平均情况:T(n) = O(nlogn)

空间复杂度:O(n)

 

8 基数排序

8.1 算法描述(维基百科基数排序

将所有待比较数值(正整数)统一为同样的数字长度,数字较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

8.2 代码

	//八、基数排序
	static void RadixSort(int a[], int len) {
		if(a!=null && len<2) {
			return;
		}
		
		//算出最大数的位数
		int max=a[0];
		for(int i=1; i<len; i++) {
			if(a[i]>max) {
				max=a[i];
			}
		}
		int maxDigit=0;
		while(max!=0) {
			max/=10;
			maxDigit++;
		}
		
		int mod=10, div=1;
		ArrayList<ArrayList<Integer>> list=new ArrayList<ArrayList<Integer>>();
		for(int i=0; i<10; i++) {
			list.add(new ArrayList<Integer>());
		}
		System.out.println("list.size="+list.size());
		for(int i=0; i<maxDigit; i++,mod*=10,div*=10) {
			for(int j=0; j<len; j++) {
				int num=a[j]%mod/div;     //算出第i位上的数num
				list.get(num).add(a[j]);  //将第i位上为num的数a[j]加入num所在的list
			}
			int n=0;
			for(int j=0; j<10; j++) {
				for(int m=0; m<list.get(j).size(); m++) {
					a[n++]=list.get(j).get(m);
				}
				list.get(j).clear();      //清空第j个list
			}
			
		}
		
	}

8.3 复杂度分析

时间复杂度:O(kn)

空间复杂度:O(k+n)

 

//9 计数排序

 

//10 桶排序

 

函数调用

package demo01;

import java.util.ArrayList;
import java.util.Queue;



public class Test01 {
	public static void main(String[] args) {
		int a[] = {0, 50, 32, 40, 90, 80, 5, 21, 69, 55, 70, 1, 10, 1, 100};
		//int a[]= {27,3};
		int length = a.length;

		//InsetSort(a, length);
		//QuickSort(a, 0, length-1);
		//SelectSort(a,length);
		//HeapSort(a,length-1);
		MergeSort(a, 0, length-1);
		RadixSort(a,length);

		for(int i:a)
			System.out.print(i+" ");
	}
	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值