java 排序算法

一、概述:

  简单排序:冒泡排序、选择排序、插入排序

       高级排序:快速排序、归并排序、希尔排序

二、冒泡排序:

(1)原理:

  1、从第一个数据开始,与第二个数据相比较,如果第二个数据小于第一个数据,则交换两个数据的位置。

  2、指针由第一个数据移向第二个数据,第二个数据与第三个数据相比较,如果第三个数据小于第二个数据,则交换两个数据的位置。

  3、依此类推,完成第一轮排序。第一轮排序结束后,最大的元素被移到了最右面。

  4、依照上面的过程进行第二轮排序,将第二大的排在倒数第二的位置。

  5、重复上述过程,没排完一轮,比较次数就减少一次。

(2)例子:

待排序数据:7, 6, 9, 8, 5,1

第一轮排序过程:指针先指向7,7和6比较,6<7,交换6和7的位置,结果为:6,7,9,8,5,1

        指针指向第二个元素7,7和9比较,9>7,不用交换位置,结果仍为:6,7,9,8,5,1

        指针指向第三个元素9,比较9和8,8<9,交换8和9的位置,结果为:6,7,8,9,5,1

        指针指向第四个元素9,比较9和5,5<9,交换5和9,结果为:6,7,8,5,9,1

        指针指向第五个元素9,比较9和1,1<9,交换1和9的位置,结果为6,7,8,5,1,9

第一轮排序结束后,最大的数字9被移到了最右边。

进行第二轮排序,过程同上,只是由于最大的9已经放在最右边了,因此不用在比较9了,少了一次比较,第二轮结束的结果为:6,7,5,1,8,9

第三轮结果:6,5,1,7,8,9

第四轮比较结果:5,1,6,7,8,9

第五轮比较结果:1,5,6,7,8,9

最终排序结果为:1,5,6,7,8,9,由上可知N个数据排序,需要进行N-1轮排序;第i轮排序需要的比较次数为N-i次。

(3)编码思路:

  需要两层循环,第一层循环i表示排序的轮数,第二层循环j表示比较的次数。

(4)代码实现:

4.1

package com.test.insertsort;
/**
 * 冒泡排序
 * @author bjh
 *
 */
public class BobSort {
    
    private int[] array;
    private int length;
    
    /**
     * 构造函数
     * @param array
     */
    public BobSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    /**
     * 打印数组中数据
     */
    public void display(){
        for(int i : array){
            System.out.print(i+" ");
        }
        System.out.println();
    }
    
    /**
     * 冒泡排序
     */
    public void bobSort(){
        for(int i=0;i<length-1;i++){//排序轮数
            for(int j=0;j<length-1-i;j++){//比较次数
                if(array[j]>array[j+1]){
                    int temp = array[j+1];
                    array[j+1] = array[j];
                    array[j] = temp;
                }
            }
        }
    }
    
    /**
     * 测试方法
     * @param args
     */
    public static void main(String[] args){
        int[] array = {77,29,28,36,33,25,10};
        BobSort bobSort = new BobSort(array);
        System.out.println("排序前的数据为:");
        bobSort.display();
        bobSort.bobSort();
        System.out.println("排序后的数据为:");
        bobSort.display();
    }
}

4.2

/**
     * 冒泡排序
     * 比较相邻的元素。如果第一个比第二个大,就交换他们两个。  
     * 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。  
     * 针对所有的元素重复以上的步骤,除了最后一个。
     * 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 
     * @param numbers 需要排序的整型数组
     */
    public static void bubbleSort(int[] numbers){
        int temp = 0;
        int size = numbers.length;
        for(int i = 0 ; i < size-1; i ++){
            for(int j = 0 ;j < size-1-i ;j++){
                if(numbers[j] > numbers[j+1]){  //交换两数位置
                    temp = numbers[j];
                    numbers[j] = numbers[j+1];
                    numbers[j+1] = temp;
                }
            }
        }
    }

(5)冒泡排序算法总结:

  N个元素需要排序N-1轮;

  第i轮需要比较N-i次;

  N个元素排序,需要比较n(n-1)/2次;

  冒泡排序的算法复杂度较高,为O(n*n)

三、选择排序

  选择排序是对冒泡排序的改进,它的比较次数与冒泡排序相同,但交换次数要小于冒泡排序。当数据量较大时,效率会有很大的提升,但时间复杂度仍为O(n*n)

(1)原理:

  1、从第一个元素开始,分别与后面的元素向比较,找到最小的元素与第一个元素交换位置;

  2、从第二个元素开始,分别与后面的元素相比较,找到剩余元素中最小的元素,与第二个元素交换;

  3、重复上述步骤,直到所有的元素都排成由小到大为止。

(2)例子:

  待比较数据:7, 6, 9, 8, 5,1

  第一轮:此时指针指向第一个元素7,找出所有数据中最小的元素,即1,交换7和1的位置,排序后的数据为:1,6,9,8,5,7

  第二轮:第一个元素已经为最小的元素,此时指针指向第二个元素6,找到6,9,8,5,7中最小的元素,即5,交换5和6的位置,排序后的结果为:1,5,9,8,6,7

  第三轮:前两个元素为排好序的元素,此时指针指向第三个元素9,找到9,8,6,7中最小的元素,即6,交换6和9的位置,排序后的结果为:1,5,6,8,9,7

  第四轮:前三个元素为排好序的元素,此时指针指向第四个元素8,找到8,9,7中最小的元素,即7,交换8和7的位置,排序后的结果为:1,5,6,7,9,8

  第五轮:前四个元素为排好序的元素,此时指针指向第五个元素9,找到9,8中最小的元素,即8,交换9和8的位置,排序后的结果为:1,5,6,7,8,9

  到此,全部排序完成。

  分析:从上面的原理可以看出,与冒泡排序不同,选择排序每排完一轮都把最小的元素移到了最左面,然后下一轮排序的比较次数比上一轮减少一次,即第i轮排序需要比较N-i次。因此,和冒泡排序一样,N个数据比较大小,需要排序N-1轮,第i轮排序需要比较N-i次。

(3)编码思路:

  需要两次循环,第一层循环i表示每轮指针指向的位置,将最小值min初始化为第i个元素,第二层循环从j=i+1开始,分别与min比较,如果小于min,则更新min的值,内层循环结束后;交换min元素和第i个元素的位置。以此类推进行下一轮循环,直到i=length时停止循环。当i=length时,说明小的元素已经全部移到了左面,因此无需进行内层循环了。

(4)编码实现:

4.1

package com.test.insertsort;
/**
 * 选择排序
 * @author Administrator
 *
 */
public class ChooseSort {
    private int[] array;
    private int length;
    
    public ChooseSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    /**
     * 打印数组中的所有元素
     */
    public void display(){
        for(int i: array){
            System.out.print(i+" ");
        }
        System.out.println(); 
    }
    
    /**
     * 选择排序算法
     */
    public void chooseSort(){
        for(int i=0; i<length-1; i++){
            int minIndex = i;
            for(int j=minIndex+1;j<length;j++){
                if(array[j]<array[minIndex]){
                    minIndex = j;
                }
            }
            int temp = array[i];
            array[i] = array[minIndex];
            array[minIndex] = temp; 
        }
    }
    
    public static void main(String[] args){
        int[] array={100,45,36,21,17,13,7};
        ChooseSort cs = new ChooseSort(array);
        System.out.println("排序前的数据为:");
        cs.display();
        cs.chooseSort();
        System.out.println("排序后的数据为:");
        cs.display();
    }
}

4.2

/**
     * 选择排序算法
     * 在未排序序列中找到最小元素,存放到排序序列的起始位置  
     * 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。 
     * 以此类推,直到所有元素均排序完毕。 
     * @param numbers
     */
    public static void selectSort(int[] numbers){
        int size = numbers.length; //数组长度
        int temp = 0 ; //中间变量
    
        for(int i = 0 ; i < size ; i++){
            int k = i;   //待确定的位置
            //选择出应该在第i个位置的数
            for(int j = size -1 ; j > i ; j--){
                if(numbers[j] < numbers[k]){
                    k = j;
                }
            }
            //交换两个数
            temp = numbers[i];
            numbers[i] = numbers[k];
            numbers[k] = temp;
        }
    }

(5)选择排序总结:

  N个元素需要排序N-1轮;

  第i轮需要比较N-i次;

  N个元素排序,需要比较n(n-1)/2次;

  选择排序的算法复杂度仍为O(n*n);

  相比于冒泡排序,选择排序的交换次数大大减少,因此速度要快于冒泡排序

四、插入排序

  插入排序是简单排序中最快的排序算法,虽然时间复杂度仍然为O(n*n),但是却比冒泡排序和选择排序快很多。

(1)原理:

  1、将指针指向某个元素,假设该元素左侧的元素全部有序,将该元素抽取出来,然后按照从右往左的顺序分别与其左边的元素比较,遇到比其大的元素便将元素右移,直到找到比该元素小的元素或者找到最左面发现其左侧的元素都比它大,停止;

       2、此时会出现一个空位,将该元素放入到空位中,此时该元素左侧的元素都比它小,右侧的元素都比它大;

  3、指针向后移动一位,重复上述过程。每操作一轮,左侧有序元素都增加一个,右侧无序元素都减少一个。

(2)例子:
  待比较数据:7, 6, 9, 8, 5,1

  第一轮:指针指向第二个元素6,假设6左面的元素为有序的,将6抽离出来,形成7,_,9,8,5,1,从7开始,6和7比较,发现7>6。将7右移,形成_,7,9,8,5,1,6插入到7前面的空位,结果:6,7,9,8,5,1

  第二轮:指针指向第三个元素9,此时其左面的元素6,7为有序的,将9抽离出来,形成6,7,_,8,5,1,从7开始,依次与9比较,发现9左侧的元素都比9小,于是无需移动,把9放到空位中,结果仍为:6,7,9,8,5,1

  第三轮:指针指向第四个元素8,此时其左面的元素6,7,9为有序的,将8抽离出来,形成6,7,9,_,5,1,从9开始,依次与8比较,发现8<9,将9向后移,形成6,7,_,9,5,1,8插入到空位中,结果为:6,7,8,9,5,1

  第四轮:指针指向第五个元素5,此时其左面的元素6,7,8,9为有序的,将5抽离出来,形成6,7,8,9,_,1,从9开始依次与5比较,发现5比其左侧所有元素都小,5左侧元素全部向右移动,形成_,6,7,8,9,1,将5放入空位,结果5,6,7,8,9,1。

  第五轮:同上,1被移到最左面,最后结果:1,5,6,7,8,9。

(3)编码分析:

  需要两层循环,第一层循环index表示上述例子中的指针,即遍历从坐标为1开始的每一个元素;第二层循环从leftindex=index-1开始,leftindex--向左遍历,将每一个元素与i处的元素比较,直到j处的元素小于i出的元素或者leftindex<0;遍历从i到j的每一个元素使其右移,最后将index处的元素放到leftindex处的空位处。

(4)代码实现:

4.1

package com.test.insertsort;
/**
 * 插入排序算法:
 * 1、以数组的某一位作为分隔位,比如index=1,假设左面的都是有序的.
 * 
 * 2、将index位的数据拿出来,放到临时变量里,这时index位置就空出来了.
 * 
 * 3、从leftindex=index-1开始将左面的数据与当前index位的数据(即temp)进行比较,如果array[leftindex]>temp,
 * 则将array[leftindex]后移一位,即array[leftindex+1]=array[leftindex],此时leftindex就空出来了.
 * 
 * 4、再用index-2(即leftindex=leftindex-1)位的数据和temp比,重复步骤3,
 * 直到找到<=temp的数据或者比到了最左面(说明temp最小),停止比较,将temp放在当前空的位置上.
 * 
 * 5、index向后挪1,即index=index+1,temp=array[index],重复步骤2-4,直到index=array.length,排序结束,
 * 此时数组中的数据即为从小到大的顺序.
 * 
 * @author bjh
 *
 */
public class InsertSort {
    private int[] array;
    private int length;
    
    public InsertSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    public void display(){        
        for(int a: array){
            System.out.print(a+" ");
        }
        System.out.println();
    }
    
    /**
     * 插入排序方法
     */
    public void doInsertSort(){
        for(int index = 1; index<length; index++){//外层向右的index,即作为比较对象的数据的index
            int temp = array[index];//用作比较的数据
            int leftindex = index-1;
            while(leftindex>=0 && array[leftindex]>temp){//当比到最左边或者遇到比temp小的数据时,结束循环
                array[leftindex+1] = array[leftindex];
                leftindex--;
            }
            array[leftindex+1] = temp;//把temp放到空位上
        }
    }
    
    public static void main(String[] args){
        int[] array = {38,65,97,76,13,27,49};
        InsertSort is = new InsertSort(array);
        System.out.println("排序前的数据为:");
        is.display();
        is.doInsertSort();
        System.out.println("排序后的数据为:");
        is.display();
    }
}

4.2

/**  
     * 插入排序
     * 
     * 从第一个元素开始,该元素可以认为已经被排序
     * 取出下一个元素,在已经排序的元素序列中从后向前扫描 
     * 如果该元素(已排序)大于新元素,将该元素移到下一位置  
     * 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置  
     * 将新元素插入到该位置中  
     * 重复步骤2  
     * @param numbers  待排序数组
     */  
    public static void insertSort(int[] numbers){
        int size = numbers.length;
        int temp = 0 ;
        int j =  0;
    
        for(int i = 0 ; i < size ; i++){
            temp = numbers[i];
            //假如temp比前面的值小,则将前面的值后移
            for(j = i ; j > 0 && temp < numbers[j-1] ; j --){
                numbers[j] = numbers[j-1];
            }
            numbers[j] = temp;
        }
    }

(5)插入排序分析:

       时间复杂度,由于仍然需要两层循环,插入排序的时间复杂度仍然为O(n*n)。
  比较次数:在第一轮排序中,插入排序最多比较一次;在第二轮排序中插入排序最多比较二次;以此类推,最后一轮排序时,最多比较N-1次,因此插入排序的最多比较次数为1+2+...+N-1=N*(N-1)/2。尽管如此,实际上插入排序很少会真的比较这么多次,因为一旦发现左侧有比目标元素小的元素,比较就停止了,因此,插入排序平均比较次数为N*(N-1)/4。

  移动次数:插入排序的移动次数与比较次数几乎一致,但移动的速度要比交换的速度快得多。

  综上,插入排序的速度约比冒泡排序快一倍(比较次数少一倍),比选择排序还要快一些,对于基本有序的数据,插入排序的速度会很快,是简单排序中效率最高的排序算法。

 

 

五、快速排序

高级算法所需要的基础知识:划分、递归,并顺带介绍二分查找算法。

二、划分:

  划分是快速排序的前提,即把数据分为两组,大于特定值的数据在一组,小于特定值的数据在另一组。快速排序即是由划分和递归操作来完成的。

(1)原理:

  定义一个阈值,分别从最左面和最右面向中间遍历元素,左面找到一个大于阈值的数据便停止,右边找到一个小于阈值的数据便停止,如果此时左右两边都还没有走到中间,则交换左面大于阈值的数据和右面小于阈值的数据;重复上述过程,直到左面指针和右面指针相遇,此时左面数据均小于阈值,右面数据均大于阈值,划分结束。划分结束后,数据仍然是无序的,但更接近于有序。

(2)例子:

  待划分数据:7, 6, 9, 8, 5,1,假设阈值为5

  第一轮:左指针指向7,右指针指向1,左指针向后移,右指针向左移,发现左面第一个大于5的元素7,右面第一个小于5的元素1,交换7和1的位置,结果:1,6,9,8,5,7;

  第二轮:从6开始找大于5的数字,找到6,右边从5起找小于5的数字,找到1,但此时由于6在1的右面,,即右指针<左指针,左右指针交叉,此时划分结束。原数列被划分为两部分,左侧子数列只有一个元素,即为1,其为小于阈值的子数列;右侧子数列包括5个元素,均为大于阈值5的元素。

(3)代码实现:

3.1

package com.test.insertsort;

/**
 * 划分、递归、快排
 * @author bjh
 *
 */
public class QuickSort {
    
    /**待排序、划分数组*/
    private int[] array;
    /**数组长度*/
    private int length;
    
    public QuickSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    /**
     * 打印元素
     */
    public void printArray(){
        for(int i=0; i<length; i++){
            System.out.print(array[i]+" ");
        }
        System.out.println();
    }
    
    
    /**
     * 划分
     * @return 划分的分界点
     */
    public int partition(int left, int right, int pivot){
        //左指针的起点,left-1是由于在后面的循环中,每循环一次左指针都要右移,
        //这样可以确保左指针从左边第一个元素开始,不然是从第二个开始
        int leftpoint = left-1;
        //右指针的起点,right+1是由于后面的循环中,每循环一次右指针都要左移,
        //这样可以确保右指针从最右边开始,不然是从倒数第二个开始
        int rightpoint = right+1;
        while(true){
            //找到左边大于pivot的数据,或者走到了最右边仍然没有找到比pivot大的数据
            while(leftpoint<right && array[++leftpoint]<pivot);
            //找到右边小于pivot的数据,或者走到了最左边仍然没有找到比pivot小的数据
            while(rightpoint>left && array[--rightpoint]>pivot);
            //左指针和右指针重叠或相交
            if(leftpoint >= rightpoint){
                break;
            }else{
                //交换左边大的和右边小的数据
                swap(leftpoint,rightpoint);
            }
        }
        //返回分界点,即右边子数组中最左边的点
        return leftpoint;
    }
    
    
    /**
     * 交换数据
     */
    public void swap(int leftpoint,int rightpoint){
        int temp = array[leftpoint];
        array[leftpoint] = array[rightpoint];
        array[rightpoint] = temp;
    }
    
    public static void main(String args[]){
        int[] array = {99,78,26,17,82,36,9,81,22,100,30,20,17,85};
        QuickSort qs = new QuickSort(array);
        System.out.println("划分前的数据为:");
        qs.printArray();
        int bound = qs.partition(0, array.length-1, 50);
        System.out.println("划分后的数据为:");
        qs.printArray();
        System.out.println("划分的分界点为:" + array[bound] + ",分界点的坐标为:" + bound);
    }

}

3.2

1.查找中轴(最低位作为中轴)所在位置

/**
     * 查找出中轴(默认是最低位low)的在numbers数组排序后所在位置
     * 
     * @param numbers 带查找数组
     * @param low   开始位置
     * @param high  结束位置
     * @return  中轴所在位置
     */
    public static int getMiddle(int[] numbers, int low,int high){
        int temp = numbers[low]; //数组的第一个作为中轴
        while(low < high){
            while(low < high && numbers[high] > temp){
                high--;
            }
            numbers[low] = numbers[high];//比中轴小的记录移到低端
            while(low < high && numbers[low] < temp){
                low++;
            }
            numbers[high] = numbers[low] ; //比中轴大的记录移到高端
        }
        numbers[low] = temp ; //中轴记录到尾
        return low ; // 返回中轴的位置
    }

2、 递归形式的分治排序算法:

/**
     * 
     * @param numbers 带排序数组
     * @param low  开始位置
     * @param high 结束位置
     */
    public static void quickSort(int[] numbers,int low,int high){
        if(low < high){
          int middle = getMiddle(numbers,low,high); //将numbers数组进行一分为二
          quickSort(numbers, low, middle-1);   //对低字段表进行递归排序
          quickSort(numbers, middle+1, high); //对高字段表进行递归排序
        }
    }

3、快速排序提供方法调用

/**
     * 快速排序
     * @param numbers 带排序数组
     */
    public static void quick(int[] numbers){
        if(numbers.length > 0){   //查看数组是否为空
            quickSort(numbers, 0, numbers.length-1);
        }
    }

分析:

  快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。快速排序是一个不稳定的排序方法。

六、归并排序

基本思想:

  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

归并排序示例:

合并方法:

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。

    1. j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标
    2. 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束
    3. //选取r[i]和r[j]较小的存入辅助数组rf
      如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵
      否则,rf[k]=r[j]; j++; k++; 转⑵
    4. //将尚未处理完的子表中元素存入rf
      如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空
      如果j<=n ,  将r[j…n] 存入rf[k…n] //后一子表非空
    5. 合并结束。

算法实现:

/**
     * 归并排序
     * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
     * 时间复杂度为O(nlogn)
     * 稳定排序方式
     * @param nums 待排序数组
     * @return 输出有序数组
     */
    public static int[] sort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            sort(nums, low, mid);
            // 右边
            sort(nums, mid + 1, high);
            // 左右归并
            merge(nums, low, mid, high);
        }
        return nums;
    }

    /**
     * 将数组中low到high位置的数进行排序
     * @param nums 待排序数组
     * @param low 待排的开始位置
     * @param mid 待排中间位置
     * @param high 待排结束位置
     */
    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;

        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }

        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = nums[i++];
        }

        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = nums[j++];
        }

        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }

七、堆排序算法

1、基本思想:

  堆排序是一种树形选择排序,是对直接选择排序的有效改进。

  堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。

  思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

2、实例

初始序列:46,79,56,38,40,84

  建堆:

   交换,从堆中踢出最大数

依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。

3.算法实现:

public class HeapSort {
    public static void main(String[] args) {
        int[] a={49,38,65,97,76,13,27,49,78,34,12,64};
        int arrayLength=a.length;  
        //循环建堆  
        for(int i=0;i<arrayLength-1;i++){  
            //建堆  
            buildMaxHeap(a,arrayLength-1-i);  
            //交换堆顶和最后一个元素  
            swap(a,0,arrayLength-1-i);  
            System.out.println(Arrays.toString(a));  
        }  
    }
    //对data数组从0到lastIndex建大顶堆
    public static void buildMaxHeap(int[] data, int lastIndex){
         //从lastIndex处节点(最后一个节点)的父节点开始 
        for(int i=(lastIndex-1)/2;i>=0;i--){
            //k保存正在判断的节点 
            int k=i;
            //如果当前k节点的子节点存在  
            while(k*2+1<=lastIndex){
                //k节点的左子节点的索引 
                int biggerIndex=2*k+1;
                //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
                if(biggerIndex<lastIndex){  
                    //若果右子节点的值较大  
                    if(data[biggerIndex]<data[biggerIndex+1]){  
                        //biggerIndex总是记录较大子节点的索引  
                        biggerIndex++;  
                    }  
                }  
                //如果k节点的值小于其较大的子节点的值  
                if(data[k]<data[biggerIndex]){  
                    //交换他们  
                    swap(data,k,biggerIndex);  
                    //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值  
                    k=biggerIndex;  
                }else{  
                    break;  
                }  
            }
        }
    }
    //交换
    private static void swap(int[] data, int i, int j) {  
        int tmp=data[i];  
        data[i]=data[j];  
        data[j]=tmp;  
    } 
}

八:希尔算法

1、基本思想:

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

2、操作方法:

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列个数k,对序列进行k 趟排序;
  3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

希尔排序的示例:

3、算法实现:

/**希尔排序的原理:根据需求,如果你想要结果从大到小排列,它会首先将数组进行分组,然后将较大值移到前面,较小值
 * 移到后面,最后将整个数组进行插入排序,这样比起一开始就用插入排序减少了数据交换和移动的次数,可以说希尔排序是加强
 * 版的插入排序
 * 拿数组5, 2, 8, 9, 1, 3,4来说,数组长度为7,当increment为3时,数组分为两个序列
 * 5,2,8和9,1,3,4,第一次排序,9和5比较,1和2比较,3和8比较,4和比其下标值小increment的数组值相比较
 * 此例子是按照从大到小排列,所以大的会排在前面,第一次排序后数组为9, 2, 8, 5, 1, 3,4
 * 第一次后increment的值变为3/2=1,此时对数组进行插入排序,
 *实现数组从大到小排
 */

    public static void shellSort(int[] data) {
        int j = 0;
        int temp = 0;
        //每次将步长缩短为原来的一半
        for (int increment = data.length / 2; increment > 0; increment /= 2){
            for (int i = increment; i < data.length; i++) {
                temp = data[i];
                for (j = i; j >= increment; j -= increment) {
                    if(temp > data[j - increment]){   //如想从小到大排只需修改这里
                        data[j] = data[j - increment];
                    }else{
                        break;
                    }
                } 
                data[j] = temp;
            }
        }
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值