常用数据结构算法:高级排序方式(快速、归并、希尔)

  继三种简单的排序方式之后,我们继续介绍高级的排序方式,所谓的高级排序方式,就是时间复杂度比简单排序方式要小的多的方式,即高级排序方式的时间复杂度一般不超过O(N*logN),因为在进行数据量很大的排序操作的时候,简单的排序方式的时间复杂度往往难以满足要求,所以高级排序方式就显得尤为重要,接下来我们介绍经典的快速排序,在之后的很多数据结构的排序当中,快排可谓为基础中的基础。

常见的快速排序是一种基于递归的排序方式,它的核心思路是:

1.首先从要排序的数组当中,选取一个数字作为 “中间点”。

2.然后定义两个“指针”(java当中没有指针的概念,在此处所谓的指针指的是指向数组下表的两个标示),一个从左至右,另一个从右至左对数组进行扫描。

3.从左至右的指针 left 扫描数组当中的元素,如找到比“中间点”大的数字,则该指针锁定;

4.从右至左的指针 right 扫描数组当中的元素,如找到比“中间点”小的数字,则该指针锁定;

5.交换两个指针所指向的数组位置的元素的值

6.重复过程2-5,直到left>=right,跳出

当跳出的时候,left所指向的位置即为“中间点”所应该在的数组的位置,将中心点放入。之后就是外层的递归整个数组的过程了,即将中心点左边的数组和中心点右边的数组进行同样上述的1-6的操作,核心思路如图所示:

常用数据结构算法:高级排序方式(快速、归并、希尔)_第1张图片

 理解了快排的核心之后,我们开始实现快速排序,代码如下:

class QuickSort
{
    private int[]input;
    private int left;
    private int right;
    
    public QuickSort(int[]input) {
        this.input=input;
        left=0;
        right=input.length-1;
    }
    
    //封装快排入口
    public void Sort()
    {
        reQuickSort(left, right);
    }
    
    //外层递归
    private void reQuickSort(int left,int right)
    {
        //注意递归出口
        if(left>=right)return;
        
        int partition=partition(left, right);
        reQuickSort(left,partition-1);
        reQuickSort(partition+1,right);
    }
    
    private void swap(int a,int b)
    {
        int temp=input[a];
        input[a]=input[b];
        input[b]=temp;
    }
    
    //快排核心方法,注意两个while的边界理解
    private int partition(int start,int end)
    {
        int pivot=input[end];
        int left=start;
        int right=end-1;
        
        while(true)
        {
            while(input[left];
            while(right>0&&input[right]>=pivot)right--;
            if(left<right)
            {
                swap(left, right);
            }
            else break;
        }
        swap(left,end);
        return left;
    }
}

 

  快排之后,我们还要介绍一个很重要的排序方式,就是归并排序,这种排序方式也是基于递归的排序方式,但其与快排的思路不同的是,归并排序的核心思路是,将两个已经排序好的数组进行有序的合并,这样合并之后的数组也一定是有序的,然后再利用递归分割的思路,将数组分割成最小单位,即只有一个数字的情况下,对两个数字进行有序归并,之后层层递归出来,最后得到的结果就一定是有序的,也许看文字讲解有些绕,那么我们可以看下图:

常用数据结构算法:高级排序方式(快速、归并、希尔)_第2张图片

思路就一目了然,之后我们来考虑java怎么去实现归并排序,代码如下:

class MergeSort
{
    private int[] input;
    private int length;
    int first;
    int last;
    
    public MergeSort(int[] input) {
        this.input=input;
        length=input.length;
        first=0;
        last=length-1;
    }
    
    //入口封装
    public void Sort()
    {
        ReSplitTheArray(first,last);
    }
    
    //递归
    private void ReSplitTheArray(int first,int last)
    {
        if(first<last)
        {
            int middle=(first+last)/2;
            ReSplitTheArray(first,middle);
            ReSplitTheArray(middle+1,last);
            mergearray(first, middle, last);
            
        }
    }

    //核心方法,合并数组
    private void mergearray(int first, int middle, int last) {
        int[] temp=new int[last-first+1];
        int i=first;
        int j=middle+1;
        int k=0;
        while(i<=middle&&j<=last)
        {
            if(input[i]>input[j])//左边的数组比右边的大
            {
                temp[k++]=input[j++];
            }
            else
            {
                temp[k++]=input[i++];
            }
        }
        
        // 把左边边剩余的数移入数组
        while (i <= middle) {
            temp[k++] = input[i++];
        }
        
        // 把右边边剩余的数移入数组
        while (j <= last) {
            temp[k++] = input[j++];
        }
        
        // 把新数组中的数覆盖input数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            input[k2 + first] = temp[k2];
        }
        
    }
}

 

  最后介绍希尔排序,希尔排序实际上是插入排序的一种改进方案,插入排序当需要把小数据移动到左边的正确位置上时,所有的中间数据项都必须向右移动一位,接近于N次数量级的移动,总共是N方/2次复制所以效率是O(N2),改进:在希尔排序当中,当步进值很大的时候,数据项每一趟排序需要移动的元素个数很少,但数据项移动的距离很长。这非常的有效率。当步进值减小的时候,每一趟要排序移动的元素增多,但是此时数据项已经接近于它们排序的最终位置,这对于插入排序可以更有效率。其基本的实现思路如下:

1.根据所传入的数组,设定一个步进值,假定步进值iincrementNum设定为数组长度的一半。

2.在原数组当中,去选曲这个步进值所在的位置的数字,如:数组{82 ,31 ,29 ,71, 72, 42, 64, 5,110}   第一次取增量设置为array.length/2 = 4    先从82开始以4为增量遍历直到末尾,得到(82,42) 排序得到{42 ,31 ,29 ,71, 72, 82, 64, 5,110}。 然后从第二个数31开始重复上一个步骤,得到(31,64) 排序得到{42 ,31 ,29 ,71, 72, 82, 64, 5,110}.......   以4为增量的遍历完数组之后,得到的结果是{42 ,31,5,71,72,82,64,29,110}。

3.步进值需要递减,即incrementNum=incrementNum/2.

4.重复过程 2-3 ,注意这个步进值的变化,最后一定要为1,要么不能保证最后得出的结果是有序的。当步进值是1的时候,相当于是最普通的冒泡排序

接下来考虑如何实现希尔排序

class ShellSort
{
    private int[] input;
    private int length;
    public ShellSort(int[] input) {
        this.input=input;
        length=input.length;
    }
    
    private void swap(int a,int b)
    {
        int temp=input[a];
        input[a]=input[b];
        input[b]=temp;
    }
    
    public void Sort()
    {
        int incrementNum=length/2;
        while(incrementNum>=1)
        {
            for(int i=0;i)
            {
                for(int j=i;jincrementNum)
                {
                    if(input[j]>input[j+incrementNum])swap(j, j+incrementNum);
                }
            }
            incrementNum = incrementNum/2;
            System.out.println(incrementNum);
        }
    }
}

至此,三种高级的排序方式也讲解完毕。

 

转载于:https://www.cnblogs.com/WellHold/p/6589014.html

你可能感兴趣的:(数据结构与算法,java)