经典快速排序及其改进

快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。

切分过程:通常取数组第一个元素作为切分,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置

图片:图片来源:https://github.com/CyC2018/CS-Notes/blob/master/notes/%E7%AE%97%E6%B3%95%20-%20%E6%8E%92%E5%BA%8F.md#快速排序

快排的每一步,有下列的数组{6,1,2,7,9,3,4,5,10,8},盗张图(嘿嘿),地址:https://www.cnblogs.com/ysocean/p/8032632.html#_label1

性能分析:

快速排序是原地排序,不需要辅助数组,但是递归要用辅助栈,最好的情况下是将数组切分成二个一样的部分。复杂度为O(n*logn)

最坏情况下是原来数组就已经有序,这时复杂度就为O(n^2),因此为防止数组一开始就有序,应该随机打乱数组的顺序。

关于优缺点和相关的优化,这篇文章写的挺好的:https://blog.csdn.net/pcwl1206/article/details/83067542#4、快速排序

和这篇:https://www.cnblogs.com/ysocean/p/8032632.html#_label1

解决原数组有序的情况:找一个可能在文件的中间位置的元素作为pivot。则可以选取数组中最左边元素、最右边元素以及中间元素中中间大小的元素作为pivot,这样使得最坏情况几乎不可能再发生

public class ReviceDemo {
    private static int medianOf3(int[] array, int left, int right) {
        int center = (right - left) / 2 + left;
        //保证arr[right]最大
        if(array[left]>array[right]){
            swap(array, left,right);
        }
        if(array[center]>array[right]){
            swap(array, center,right);
        }
        //比较center和left,让最大的在left位置上
        if(array[center]>array[left]){
            swap(array, center,left);
        }

        return array[left]; //array[left]的值已经被换成三数中的中位数, 将其返回
    }

    private static int partitionIt(int[] array, int left, int right) {
        //为什么 j加一个1,而i没有加1,是因为下面的循环判断是从--j和++i开始的.
        //而基准元素选的array[left],即第一个元素,所以左游标从第二个元素开始比较
        int i = left;
        int j = right + 1;
        int pivot = array[left];// pivot 为选取的基准元素(头元素)

        int size = right - left + 1;
        if (size >= 3) {
            pivot = medianOf3(array, left, right); //数组范围大于3,基准元素选择中间值。
        }
        while (true) {
            while (i < right && array[++i] < pivot) {
            }

            while (j > 0 && array[--j] > pivot) {
            }

            if (i >= j) {// 左右游标相遇时候停止, 所以跳出外部while循环
                break;
            } else {
                swap(array, i, j);// 左右游标未相遇时停止, 交换各自所指元素,循环继续
            }
        }
        swap(array, left, j);//基准元素和游标相遇时所指元素交换,为最后一次交换
        return j;// 一趟排序完成, 返回基准元素位置(注意这里基准元素已经交换位置了)
    }
    public static void swap(int []arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
    public static void main(String[] args) {
        int arr[]={1,4,6,2,8};
        int i = medianOf3(arr, 0, arr.length - 1);
        System.out.print(i);
    }
}

解决数组长度较小的时候,用插入排序

解决数组中重复元素较多的时候,可用荷兰国旗问题解决实际上荷兰国旗问题可以解决数组中有大量重复元素,和数组原来有序的情况,关键代码在于swap(arr, l + (int) (Math.random() * (r - l + 1)), r);

public class QuickSort {

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
                        swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);
			quickSort(arr, p[1] + 1, r);
		}
	}

	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;
		int more = r;
                int cur=l;
		while (cur < more) {
			if (arr[cur] < arr[r]) {
				swap(arr, ++less, cur++);
			} else if (arr[cur] > arr[r]) {
				swap(arr, --more, cur);
			} else {
				cur++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值