主要总结一下以下常见的排序以及其java的实现,包含有:
比较排序
冒泡排序
插入排序
希尔排序
快速排序
堆排序
归并排序
1.比较排序:从第0个开始分别与后面的比较,正序不交换,反序就交换。时间复杂度n+(n-1)+.....1=(1+n)n/2=O(n^2)。
/**比较排序
* 2015年10月29日上午9:53:38
* @param data
*/
public static void CompareSort(int[] data)
{
for(int i=0;i<data.length;i++)
{
for(int j=i+1;j<data.length;j++)
{
if(data[i]>data[j])
{
int change=data[i];
data[i]=data[j];
data[j]=change;
}
}
}
}
2.冒泡排序:最后一个开始 a[n]与a[n-1]比较,正序不交换,反序交换,接着比较a[n-1]与a[n-2] ....遍历一遍可以最小的排到最上面,如此遍历n遍就可以实现排序,从底到上,如冒泡一样。时间复杂度为O(n^2)
/**
* 冒泡排序 2015年10月29日上午9:53:38
*
* @param data
*/
public static void bubleSort(int[] data) {
for (int i = 1; i <= data.length; i++) {
for (int j1 = data.length - 1, j2 = j1 - 1; j2 >= 0; j2--, j1--) {
if (data[j1] < data[j2]) {
int change = data[j1];
data[j1] = data[j2];
data[j2] = change;
}
}
}
}
改进:去掉已经排序了的部分
/**
* 冒泡排序 2015年10月29日上午9:53:38
*
* @param data
*/
public static void bubleSort(int[] data) {
for (int i = 0; i <data.length; i++) {
for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {
if (data[j1] < data[j2]) {
int change = data[j1];
data[j1] = data[j2];
data[j2] = change;
}
}
}
}
进一步改进:假如已经没有可以比较的了,退出循环
/**
* 冒泡排序 2015年10月29日上午9:53:38
*
* @param data
*/
public static void bubleSort(int[] data) {
Boolean noFinishCompare=true;
for (int i = 0; i <data.length&&noFinishCompare; i++) {
noFinishCompare=false;
for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {
if (data[j1] < data[j2]) {
int change = data[j1];
data[j1] = data[j2];
data[j2] = change;
noFinishCompare=true;//只要有交换就没有完成比较
}
}
}
}
3.插入排序:将要排序的数组分成两个部分,一部分是已经排序好了,一部分是原来的,一开始默认已经排序好的只有第一个数组下标。接着从第二个数组开始遍历,判断要排序的元素在已经排序的部分的位置,然后插入。比如判断第二个数组比第一个数组小,插入在左边,比它大插入在右边,确定插入位置的依据是比上一个位置小,不大于下一个位置。时间复杂度为O(n^2);
/**插入排序
* 2015年10月29日上午11:09:33
* @param data
*/
public static void inSertSort(int[] data )
{
for(int i=1;i<data.length;i++)
{
if(data[i]<data[0])
{
int change=data[i];//拿出data[i]
for(int s=i;s>=1;s--)//从0位起将i前面的后移一位
{
data[s]=data[s-1];
}
data[0]=change;//将i放到第一位
}
if(data[i]>data[0])
{
int change=data[i];//拿出data[i]
for(int s=0;s<i;s++ )//遍历已经排序的部分
{
if(change>data[s]&&change<=data[s+1]){//通过大于【i】小于或等于【i+1】来确定要插入的位置
for(int e=i;e>=s+1;e--)
{
data[e]=data[e-1]; //右移动留出插入位置
}
data[s+1]=change; //插入
}
}
}
}
}
改进:由于前部分已经排序了的,那么判断a[i]的插入位置只需要在已经排序的数据中判断就行,就是0~i这个已排序的数据中判断插入位置即可:
/**插入排序
* 2015年10月29日上午11:09:33
* @param data
*/
public static void inSertSort(int[] data )
{
for(int i=1;i<data.length;i++)
{
int ai=data[i];
int j;
for(j=i-1;j>=0&&data[j]>ai;j--)
{
data[j+1]=data[j];
}
data[j+1]=ai;
}
}
4.希尔排序:
原理是什么:对于无序数组 a【1】a【2】a【3】a【4】a【5】a【6】
将其划分成子序列: gap=6/2=3;
分别是 : a【1】a【4】 a【2】a【5】 a【5】a【6】 正序不交换,反序交换
那么通过排序让子序列有序得到:b【1】b【2】b【3】b【4】b【5】b【6】
可知:b【1】<b【4】 b【2】<b【5】 b【3】<b【6】
接下来 gap=3/2=1;
分别是:b【1】b【2】 b【2】b【3 】 b【3】b【4】 b【4】b【5】 b【5】b【6】
排序后得到序列:c【1】c【2】c【3】c【4】c【5】c【6】
可知c【1】<c【2】 c【2】<c【3】 】 c【3】<c【4】 c【4】<c【5】 c【5】<c【6】
结合b,可得到有序序列。
算法核心:不断的分成几个有序子序列,对子序列进行排序,最后得到基本有序的序列,然后进行一次插入排序。
public static void ShellSort(int[] datas)
{
int gap=datas.length/2;
if(datas.length>0)
gap=datas.length%2==0?gap-1:gap;
while(gap>=1)
{
for(int i=0;i<=datas.length/2;i++)
{
if(datas[i]>datas[i+gap])
{
int exchange=datas[i+gap];
datas[i+gap]=datas[i];
datas[i]=exchange;
}
}
gap=gap/2;
}
inSertSort(datas);
}
}
希尔排序最终也是用到插入排序,目的就是尽可以有序化,减小插入排序的运算,定义一个变量用于确定它允许的次数:
/**插入排序
* 2015年10月29日上午11:09:33
* @param data
*/
public static void inSertSort(int[] data )
{
for(int i=1;i<data.length;i++)
{
int ai=data[i];
int j;
for(j=i-1;j>=0&&data[j]>ai;j--)
{ System.out.println("-----num"+num++);
data[j+1]=data[j];
}
data[j+1]=ai;
}
}
public static void ShellSort(int[] datas)
{
int gap=datas.length/3;
if(datas.length>0)
gap=datas.length%2==0?gap-1:gap;
while(gap>=1)
{
for(int i=0;i<=datas.length/2;i++)
{
if(datas[i]>datas[i+gap])
{System.out.println("-----num"+num++);
int exchange=datas[i+gap];
datas[i+gap]=datas[i];
datas[i]=exchange;
}
}
gap=gap/3;
}
inSertSort(datas);
}
测试数据如下:
int[] datas = new int[] {8,15,4,55,98,14,77,35,88,21,546,875,1,65,756,43,4,87,54,11,25,66,78,95,555,423,657,442};
但是调用插入排序,打印出来的num:
-----num125
使用希尔排序的话:
-----num65
而且希尔排序的步长不一样的话运算树也不一样:
public static void ShellSort(int[] datas)
{
int gap=datas.length/3;
if(datas.length>0)
gap=datas.length%2==0?gap-1:gap;
while(gap>=1)
{
for(int i=0;i<=datas.length/2;i++)
{
if(datas[i]>datas[i+gap])
{System.out.println("-----num"+num++);
int exchange=datas[i+gap];
datas[i+gap]=datas[i];
datas[i]=exchange;
}
}
gap=gap/2;
}
inSertSort(datas);
}
打印出来的是:
-----num61
5.快速排序:
快速排序也叫分治法,以一个基准数为标准将其分为两个部分,一个是比这个数小的部分,一个是比这个数大的部分,然后让这两个部分按照这样的法则递归下去。
分成两个有序的部分思想是:
【1】【2】【3】【4】....【k】...【n-1】【n】
先以key=【1】为基准数,从n下标往左遍历,获取到第一个比key小的【k】,交换位置:【k】【2】【3】..【m】.....【1】.。。【n-1】【n】
然后从2开始往左遍历,找到第一个比key大的【m】,交换k下标以2下标:【k】【2】【3】..【1】..【m】...【n-1】【n】
可以知道:【1】前面的数都比【1】小,【m】后面的数都比【m】大
然后继续按照这个法则,从【1】下标到【m】的下标进行排序,一直到左边的下标等于右边的下标,最终分成两个部分....【1】....
前面的比【1】小,后面的比【1】大。。
然后分成两个部分递归,从0下标到【1】前一个下标,从【1】的后一个下标到【n】下标。以此下去最终通过实现排序。
/**快速排序
* 2015年10月30日下午1:50:53
*
* @param data
*/
public static void FastSortToHalf(int[] data, int leftFirstPosition,int rightLastPosition) {
int size = rightLastPosition;
if (leftFirstPosition < rightLastPosition) {
while (leftFirstPosition < rightLastPosition) {//通过循环分成两个部分
int key = data[leftFirstPosition];
while (rightLastPosition > leftFirstPosition&& data[rightLastPosition] >= key) {
rightLastPosition--;
}
if (leftFirstPosition < rightLastPosition) {
data[leftFirstPosition] = data[rightLastPosition];
data[rightLastPosition] = key;
}
while (leftFirstPosition < rightLastPosition&& data[leftFirstPosition] <= key) {
leftFirstPosition++;
}
if (leftFirstPosition < rightLastPosition) {
int changge = data[leftFirstPosition];
data[leftFirstPosition] = data[rightLastPosition];
data[rightLastPosition] = changge;
}
}
FastSortToHalf(data, 0, leftFirstPosition - 1);//对这两个部分进行递归
FastSortToHalf(data, leftFirstPosition + 1, size);
}
}
6.堆排序:
堆排序最好最坏以及平均的情况下时间复杂度都是O(nlongn),性能上比比较排序、冒泡排序、插入排序好。
思想:需要构建与保持一个最大堆,然后利用最大堆的性质实现排序。
原理:将要排序的数组看成一个完全二叉树,对于第i个位置,如果他有parent,那么parent位置为i/2,如果他有左子树,那么左子树位置为2*i,如果他有右子树,那么右子树位置为2*i+1,构建最大堆的原理是:如果父节点比它的子节点小,那么将父节点与它子节点的最大节点交换,子节点以此类推,达到父节点永远大于子节点,如此从下往上递归,最终根节点是最大的数。创建了大项堆后,将更节点的值与最后的值交换,将0-n-1下标的项重新构造大项堆,如此递归下去,最终排序。
所以步骤有:1.构造大项堆 2.将根节点与最后项交换 3.递归。
public static void HeapSort(int[] data) {
BuildBiggestHeap(data, data.length);//创建大项堆
for(int i=data.length;i>2;i--)//最后一个长度应该是3个节点
{
swap(data, 0,i-1);//交换根节点与最后项
BuildBiggestHeap(data, i);//重新创建大项堆
}
}
/**创建大项堆
* 2015年11月2日下午3:21:02
* @param data
* @param length
*/
public static void BuildBiggestHeap(int[] data,int length) {
if(length>data.length)
return;
for(int i=length/2-1;i>=0;i--){
int parent_position=i;
int left_position=i*2+1;
int right_position=i*2+2;
if(right_position<length)
{
if(right_position==length-1){//这时只有一个左子节点
if(data[parent_position]<data[left_position]);
swap(data, left_position, parent_position);
}else{//有两个子节点
int lagerposition=0;
lagerposition=data[left_position]>data[right_position]?left_position:right_position;
lagerposition=data[lagerposition]>data[parent_position]?lagerposition:parent_position;
if(lagerposition!=parent_position)
swap(data, parent_position, lagerposition);
}
}
}
}
//交换
private static void swap(int[] data, int i, int j) {
int tmp=data[i];
data[i]=data[j];
data[j]=tmp;
}
7.归并排序:
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
排序实现思路是:将数组分成左右两个部分,将两个部分分别进行排序,然后将排序了的两个部分合并。
拆分的思路是通过递归进行不断的拆分,最终拆分成长度为2或者3的序列。
归并的思路是:构造一个缓冲数组,数组长度为要归并的两部分的长度,左部分下标为i,右部分下标为j,数组下标为k,如果a【i】<a【j】,那么将a【i】赋予s【k】,并i++,k++,反之j++,k++。当i或者j长度分配完后,应该讲i或者j剩下的部分按顺序富裕k。
所以代码实现的步骤为:1.拆分. 2.合并 并且对拆分进行递归。
<strong> </strong>private static void MergeSort(int[] data,int firstposition,int mergeiength)
{
if(mergeiength==2)
{
sortlength2(data,firstposition);//长度为2时排序
return;
}
if(mergeiength==3){
sortlength3(data,firstposition);//长度为3时排序
return;
}
int mergeleftlength=mergeiength/2;
int mergerightlength=mergeiength-mergeleftlength;
MergeSort(data, firstposition, mergeleftlength);//拆分左边进行递归
MergeSort(data, firstposition+mergeleftlength, mergerightlength);//拆分右边进行递归
//拆分排序后将左右两部分合并
int[] temp=new int[mergeiength];//缓存数组,将左右部分合并到缓存数组中
int tempposition=0;
int i=firstposition,j=i+mergeleftlength;
int leftmaxposition=firstposition+mergeleftlength;
int rightmaxposition=firstposition+mergeiength;
while(i<leftmaxposition&&j<rightmaxposition){
if(data[i]>data[j])
{
temp[tempposition++]=data[j++];
}else {
temp[tempposition++]=data[i++];
}
}
while(tempposition<mergeiength)//合并剩余的部分
{
if(i<leftmaxposition)
{
temp[tempposition++]=data[i++];
}
if(j<rightmaxposition){
temp[tempposition++]=data[j++];
}
}
System.arraycopy(temp, 0, data, firstposition, mergeiength);//拷贝到缓存数组的对应位置中
}
private static void sortlength3(int[] data, int firstposition) {
sortlength2(data, firstposition);//先排好前两个
if(data[firstposition+2]<data[firstposition]){
int s=data[firstposition+2];
data[firstposition+2]=data[firstposition+1];
data[firstposition+1]=data[firstposition];
data[firstposition]=s;
}
if(data[firstposition+2]>data[firstposition]&&data[firstposition+2]<data[firstposition+1]){
int s=data[firstposition+2];
data[firstposition+2]=data[firstposition+1];
data[firstposition+1]=s;
}
}
private static void sortlength2(int[] data, int firstposition) {
if(data[firstposition]>data[firstposition+1])
swap(data, firstposition, firstposition+1);
}