排序算法(二)

4、希尔排序
原理:希尔排序是直接插入排序的增强,希尔排序是将整个序列分成若干个组,然后进行分组插入排序,分组的增量gap从大到小,最终必须为1,当增量为1时,也就是插入排序(gap一般是gap = gap/2不断缩小到1或者gap = (gap/3+1),逐步缩小至1)。
举例如下:
希尔排序
代码实现如下:

public class ShellSort {
    public void shellSort(int[] arr) {
        int gap = arr.length;//增量
        int i,j,temp;
        while (gap > 0) {
            gap = gap/2;//逐步缩小到1
            for (i = gap; i < arr.length; i++) {
                if (arr[i] < arr[i-gap]){
                    temp = arr[i];
                    for(j = i-gap;j >= 0;j -= gap){//将大于temp的元素放到后面
                        if(arr[j] > temp) {
                            arr[j + gap] = arr[j];
                        }else{
                            break;
                        }
                    }
                    arr[j+gap] = temp;
                }
            }
        }
    }
    public static void main(String[] args){
        ShellSort m = new ShellSort();
        int[] arr = new int[]{3,6,2,8,5,7,3,8,5,8,3,7,0};
        m.shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

结果如下:
在这里插入图片描述
算法分析:
(1)性能分析
在这里插入图片描述
(2)时间复杂度
希尔排序最重要的地方在于,以前在小步长进行排序后,用大步长进行排序仍然有序;否则,算法在迭代过程中,打乱之前的排序,则算法效率就没有那么好了。在希尔排序中,比较是最主要的操作。
(3)稳定性
希尔排序中,相等的元素可能会交换位置,也就是说,相等元素的前后顺序可能会改变,该算法不稳定。
5、快速排序
快速排序是排序算法中非常重要的一种,也是经常会考到的一种,同时它有多种改进算法,需要细细研究。
原理:快速排序是选定一个基准,将大于基准的放在右边,将小于基准的放在左边,依次递归,最终达到整个序列的有序,一般改进的算法主要是在选定基准时进行改进。
步骤:
(1) 选定基准值后,从后往前进行比较,找到第一个小于基准值的进行交换;
(2) 然后,再从前往后找,找到第一个大于基准的进行交换;
(3) 直到从前往后的比较数组下标大于从后往前的比较数组下标,完成一次循环;
(4) 接着按着上述循环分别比较左右两侧的序列,最终有序。
代码实现:

  public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            int base = arr[low];//基准
            while(low < high){
                while(low < high && arr[high] >= base){//右侧扫描,找到第一个小于base的元素
                    high--;
                }
                arr[low] = arr[high];
                while(low < high && arr[low] <= base){//左侧扫描,找到第一个大于base的元素
                    low++;
                }
                arr[high] = arr[low];
            }
            arr[low] = base;
            return low;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

快速排序算法的优化:
(1)随机选定基准
当序列几乎有序时,基准每次选定为第一个值时,快排的性能不好,因此,将基准选择改为随机的。(但当整个序列是乱序的,排序性能可能会下降,有些许的运气成分)
代码实现:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            //排序前将base与随机的数进行交换,此时,基准值具有随机性了
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];
            int j = low;
            for(int i = low+1;i <= high;i++){
                if(base > arr[i]){
                    j++;
                    swap(arr,i,j);
                }
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

(2)两路快排
快速排序时,将大于基准的排在基准右边,将小于等于基准的排在基准左边。
代码实现:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];//每次将基准抛出,相当于对[low+1,...,high]的排序
            int i = low+1;//i表示对[low+1,...,i)的比base小(等)的部分排序
            int j = high;//j表示对(j,...,high]的比base大(等)的部分排序
            while(true){
                while (i <= high && arr[i] < base){//左侧扫描,找到第一个比base大的元素
                    i++;
                }
                while(j >= low && arr[j] > base){//右侧扫描,找到第一个比base小的元素
                    j--;
                }
                if(i > j){
                    break;
                }
                swap(arr,i++,j--);
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

(3)配合插入排序使用
快排是采用分治思想,也就是递归将问题不断缩小规模进而解决,但当数据规模小,整个序列近乎有序时,采用“递归+不稳定”的方式,效率不一定好,此时利用插入排序,效果会更好一些。
代码如下:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public void insertSort(int[] arr,int m,int n){
            int i,j,temp = 0;
            for(i = m+1;i <= n;i++){
                    temp = arr[i];
                for(j = i-1;j >= 0 && arr[j] > temp;j--){//将大于基准的后移
                        arr[j+1] = arr[j];
                }
                arr[j+1] = temp;
            }
        }
        public int partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];
            int j = low;
            for(int i = low+1;i <= high;i++) {
                if (base > arr[i]) {
                    j++;
                    swap(arr, i, j);
                }
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high,int k){
            if(low >= high) return;
            if(high-low < k){
                insertSort(arr,low,high);
                return;
            }
            int p = partition(arr,low,high);
            quickSort(arr,low,p-1,k);
            quickSort(arr,p+1,high,k);
        }

(4)三路快排
当整个序列中有大量的重复数据时,将整个序列分成小于基准,等于基准,大于基准三部分进行排序,也就是所称的三路快排。用指针从前到后扫描,如果cur指向的数小于base,那么交换arr[cur]和arr[i]的值,然后i++,cur++;cur指向的数等于base, 那么cur++;cur指向的数大于base,那么交换arr[cur]和arr[j]的值,然后j–。当cur > j的时候说明三路都已经完成。
代码实现如下:

public static void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int[] partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int temp = arr[low];
            int i = low;
            int j = high;
            int cur = i;
            while(cur <= j){
                if(arr[cur] < temp){
                    swap(arr,cur++,i++);
                }else if(arr[cur] == temp){
                    cur++;
                }else{
                    swap(arr,cur,j--);
                }
            }
            return new int[]{i-1,j+1};//[i...j]都等于base,子问题就只需要解决i左边和j右边就行了
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int[] brr = partition(arr,low,high);
                quickSort(arr,low,brr[0]);
                quickSort(arr,brr[1],high);
            }
        }
    }

三路快排可以避免很多重复元素再次参与递归,对于大量重复的待排序列,效率有所提升。
算法分析:
(1)性能分析
在这里插入图片描述
(2)时间复杂度
当序列接近有序时,以第一个元素为基准时,左右两个序列相差悬殊,此时执行效率最差;
当序列随机分布时,以第一个元素为基准进行分割,左右两个序列个数基本相等,此时执行效率最好;
综上,序列越乱序,快排的性能越好,越正序,快排的性能越差。
(3)稳定性
快速排序中,相同元素可能会因为分区而改变前后顺序,所以,快速排序是不稳定的算法。
(4)空间复杂度
快速排序,每次分割需要一个空间存储临时值,但需要进行Nlog2N次分割处理,所以空间复杂度为O(Nlog2N)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值