zoukankan      html  css  js  c++  java
  • 常用数据结构算法:高级排序方式(快速、归并、希尔)

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

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

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

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

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

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

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

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

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

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

    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]<pivot)left++;
                while(right>0&&input[right]>=pivot)right--;
                if(left<right)
                {
                    swap(left, right);
                }
                else break;
            }
            swap(left,end);
            return left;
        }
    }

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

    思路就一目了然,之后我们来考虑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<length;i++)
                {
                    for(int j=i;j<length-incrementNum;j=j+incrementNum)
                    {
                        if(input[j]>input[j+incrementNum])swap(j, j+incrementNum);
                    }
                }
                incrementNum = incrementNum/2;
                System.out.println(incrementNum);
            }
        }
    }

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

  • 相关阅读:
    python每日作业4/21
    socket实现并发之socketserver模块的使用
    python socket粘包问题的解决
    每日作业:4/20
    网络编程基础(socket)
    网络基础之网络协议
    异常处理
    python 作业4/15
    centos7简单安装配置mariadb
    Centos7下Firewalld防火墙配置命令
  • 原文地址:https://www.cnblogs.com/WellHold/p/6589014.html
Copyright © 2011-2022 走看看