JAVA高级排序

高级排序-------希尔排序

原理:将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序......最后选择增量为1,即使用直接插入排序,使最终数组成为有序。

 

增量:希尔排序通过加大排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度地移动。当这些数据项排过一趟序后,希尔排序算法减小数据项的间隔再进行排序,依次进行下去。进行这些排序时数据项之间的间隔被称为增量。

 

比如:采用3*h+1增量,当有十个数时,最大增量跨度为4

4为增量排序0,4,8号数据项

当对0、4、8号数据项完成排序之后,算法向右移一步,对1、5、9号数据项进行排序。这个排序过程持续进行,直到所有的数据项都已经完成了4增量排序,也就是说所有的间隔为4的数据项之间都已经排列有序。

在完成以4为增量的希尔排序之后,数组可以看成是由4个子数组组成(0,4,8),(,1,5,9),(2,6,)和(3,7),这四个子数组内分别是完全有序的。

 

减小间隔:当进行过第一趟排序后,在制定间隔位置上的数已经有序了,这时应减小间隔继续排序,直到间隔变成1

根据上诉增量公式,再每次完成一次排序后,用公式h=(h-1)/3来减小间隔

 

实例:

先初始化跨度h = 1,通过while 循环来获取最大跨度

int h = 1;//初始化跨度
//设置跨度,将数组分成若干组
while (h <= arr.length/3){
   
h = 3*h +1;
}

 

while循环中的判断来控制循环的结束,当跨度小于1的时候表示排序完成,循环结束

while (h>0){

    for (outer = h;outer < arr.length;outer ++){//通过循环比较,将对应位置的数据进行大小交换
        temp = arr[outer];
        inner = outer;
        System.out.println("temp = "+temp+";outer = "+outer);
        while (inner > h-1 && arr[inner - h] >= temp){
            arr[inner] = arr[inner - h];
            inner -=h;
        }
        arr[inner] = temp;
    }
    //上述for循环跳出来之后,整个数组中,被分成的若干小数组已经有序了
    h = (h - 1)/3;//调整数组间隔,直到数组间隔小于1时,排序完成跳出while循环
}

 

高级排序------快速排序

快速排序分三个基本步骤

1、  把数组或者子数组划分成左边(较小的关键字)的一组和右边(较大的关键字)的一组

2、  调用自身对左边的一组进行排序

3、  再次调用自身对右边的一组进行排序

 

经过一次划分之后,所有在左边子数组的数据项都小于在右边子数组的数据项,只要对左边子数组和右边子数组分别进行排序,整个数据就是有序的了,如何对子数组进行排序,通过递归的调用排序算法自身就可以。

递归调用排序子数组

 

选择枢纽(三数据项取中)

         partitionIn()方法获取枢纽

选择具体的一个数据项的关键字的值作为枢纽:称这个数据项为pivot(枢纽)

可以选择任意一个数据项作为枢纽,一般可以选择带划分的子数组最右端的数据作为枢纽,但是会有一种情况(已经有序,或者最右端的数值最大),这样等于是进行一次排序后,数组还是一个,与其他排序相比体现不出来,因此,我们一般取数组或者子数组的三个值进行比较,选取中间值作为枢纽。

private int medianOf3(int left, int right) {

    int center = (left + right)/2;

    if (arr[center] > arr[right]){
        swap(center,right);
    }
    if (arr[left] > arr[right]){
        swap(left,right);
    }
    if (arr[left] > arr[center]){
        swap(left,center);
    }

    swap(center,right - 1); 

    return arr[right-1];
}

 

交换枢纽位置

获取第一次划分后,关键字应停放的位置

private int partitionIn(int left, int right, int pivot) {
    int leftPtr = left;
    int rightptr = right - 1;
    while (true){

        while (arr[++leftPtr] < pivot);
        while (rightptr > 0 && arr[--rightptr] > pivot);

        if (leftPtr >= rightptr){
            break;
        }else {
            swap(leftPtr,rightptr);
        }
    }
    swap(leftPtr,right - 1);

    return leftPtr;
}

 

由于中值划分要求至少含有四个数据项,所以不能应用于只有两个或者三个数据项的子数组,所以,当子数组中只有一个数据项时方法立即返回,有两个数据项时,如果需要则交换这两个数据项,有三个数据项时对三个数据项进行排序。

 

private void manulSort(int left, int right) {
    int size = right - left +1;
    if (size <= 1){
        return;
    }
    if (size == 2){
        if (arr[left] > arr[right]){
            swap(left,right);
            return;
        }
    }else {
        if (arr[left] > arr[right - 1]){
            swap(left,right-1);
        }
        if (arr[left] > arr[right]){
            swap(left,right);
        }
        if (arr[right - 1] > arr[right]){
            swap(right-1,right);
        }
    }
}

 

快速排序总的调用方式

private void  recQuickSort(int left,int right){
    int size = right - left + 1;
    if (size <= 3){
        manulSort(left,right);
    }else {
        int pivot = medianOf3(left,right);

        int partition = partitionIn(left,right,pivot);
        //递归排序的时候,由于中间位置partition已经有序,所以从它的左边一位,或者右边一位开始
        recQuickSort(left,partition-1);
        recQuickSort(partition+1,right);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值