排序升级版—带你了解不一样的快速排序以及归并排序

快速排序算是最优的排序方式了,这两种排序的思想都是分治思想,一个使用递归,一个没使用,各有利弊。有兴趣的小伙伴可以多来探讨,写的不对的或者可以优化的地方可以给我留言。

一,归并排序(英语:Merge sort,或mergesort)

1,排序思想:

是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序的核心思想是将两个有序的数列合并成一个大的有序的序列。通过递归,层层合并,即为归并。

排序升级版—带你了解不一样的快速排序以及归并排序

 

2,开始代码

1),传入一个数组以及数组的长度

/**
 * 归并排序 nlogn
 * @param arr
 * @param length
 */
public static void mergeSort(int[] arr,int length){
    __mergeSort(arr,0,length - 1);
}

2),对数组的 arr[l,r]范围进行排序,l为左边界,r为右边界,注意是左闭右闭的区间。

/**
 * 对arr[l,r]范围进行排序
 * @param arr
 * @param l
 * @param r
 */
public static void __mergeSort(int[] arr,int l,int r){
    if(l >= r){        // 跳出递归的条件
        return;
    }
    int mid = (l + r)/2;        //计算中点位置
    __mergeSort(arr,l,mid);        // 调用自身对左部分区间进行排序
    __mergeSort(arr,mid + 1,r);    // 调用自身对右部分区间进行排序
    if(arr[mid] > arr[mid + 1]){      //如果是近乎有序的数组,可以这样优化
        __merge(arr,l,mid,r);        // 开始归并并排序
    }
}

3),对arr[l,mid] 和 arr[mid +1 ,r] 两部分进行归并

/**    
     * 对arr[l,mid] 和  arr[mid +1 ,r] 两部分进行归并
     * @param arr
     * @param l
     * @param mid
     * @param r
     */
    public static void __merge(int[] arr,int l,int mid,int r){
    	int[] aux = new int[r - l + 1];
    	for(int i = l;i <= r;i++){
    		aux[i - l] = arr[i];
    	}
    	
    	int i = l;
    	int j = mid + 1;
    	for(int k = l;k <= r;k++){
    		if(i > mid){
    			arr[k] = aux[j - l];
    			j++;
    		}else if(j > r){
    			arr[k] = aux[i - l];
    			i++;
    		}else{
    			if(aux[i - l] < aux[j - l]){
    				arr[k] = aux[i -l];
    				i++;
    			}else{
    				arr[k] = aux[j -l];
    				j++;
    			}
    		}
    		
    	}
    }

以上就是归并排序,优化的话,可以在数组小于一定容量时,改为插入排序,增加效率。

if(l >= r){
     return;                             // 跳出递归的条件
 }
 // 我们可以改成以下
 if(r - l < 15){
     insertSort(arr,l,r);
     return;
 }

附上插入排序的代码:

/**    
     *    插入排序,对[l,r]区间的arr数组插入排序
     */
    public static void insertSort(int[] arr,int l,int r){
		for(int i = l + 1; i <= r; i++){
			int e = arr[i];
			int j;
			for(j = i;j > l && arr[j - 1] > e;j--){
				arr[j] = arr[j - 1];
			}
			arr[j] = e;
	    }
		return;
	}

二,快速排序(英语:Quicksort)

1,算法思想

基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(low指向起始位置,high指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换low和high位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换low和high位置的值,如此往复循环,直到low>=high,然后把基准点的值放到high这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。

2,开始代码

1),同样传入一个数组,和数组的长度。

/**
 * 快速排序
 * @param arr
 * @param length
 */
public static void quickSort(int[] arr,int length){
    __quickSort(arr,0,length - 1);
}

2),对arr[l,r]部分进行快速排序。

/**
 * 对arr[l,r]部分进行快速排序
 * @param arr
 * @param l
 * @param r
 */
public static void __quickSort(int[] arr,int l,int r){
    if(l >= r){
        return;
    }
    int p = __partition2(arr,l,r);
    __quickSort(arr,l,p-1);
    __quickSort(arr,p+1,r);
}

3),对arr[l,r]部分进行partition操作,返回p,使得arr[l,p-1] < arr[p], arr[p+1] > arr[p]

/**
 * 对arr[l,r]部分进行partition操作,返回p,使得arr[l,p-1] < arr[p], arr[p+1] > arr[p]
 * @param arr
 * @param l
 * @param r
 * @return
 */
public static int __partition(int[] arr,int l,int r){
    int temp1 = arr[l];
    arr[l] = arr[new Random().nextInt(r - l + 1) + l];
    arr[new Random().nextInt(r - l + 1) + l] = temp1;
 
    int v = arr[l];
    int j = l;
    for(int i = l + 1;i <= r; i++){
        if(arr[i] < v){
            int temp = arr[j + 1];
            arr[j + 1] = arr[i];
            arr[i] = temp;
            j++;
        }
    }
    int temp = arr[j];
    arr[j] = arr[l];
    arr[l] = temp;
    return j;
}

改进版本,双路同时进行。

/**
 * 改进版
 * @param arr
 * @param l
 * @param r
 * @return
 */
public static int __partition2(int[] arr,int l,int r){
    int temp1 = arr[l];
    int index = new Random().nextInt(r - l + 1) + l;
    arr[l] = arr[index];
    arr[index] = temp1;
 
    int v = arr[l];
    int i = l + 1;
    int j = r;
    while(true){
        while(i <= r && arr[i] < v){
            i ++;
        }
        while(j >= l + 1 && arr[j] > v){
            j --;
        }
        if(i > j){
            break;
        }
        int temp2 = arr[j];
        arr[j] = arr[i];
        arr[i] = temp2;
        i++;
        j--;
    }
    int temp3 = arr[j];
    arr[j] = arr[l];
    arr[l] = temp3;
    return j;
}

快速排序的终极版本,三路快速排序。

/**
 * 三路快速排序
 * @param arr
 * @return
 */
public static void threeWaysQuickSort(int[] arr,int length) {
    __threeWaysQuickSort(arr,0,length - 1);
}
public static void __threeWaysQuickSort(int[] arr,int l,int r) {
    if(r - l < 15){
        insertSort(arr, r,l);
    }
 
    int index = new Random().nextInt(r - l + 1) + l;
    int temp1 = arr[l];
    arr[l] = arr[index];
    arr[index] = temp1;
 
    int v = arr[l];
    int lt = l;
    int gt = r + 1;
    int i = l + 1;
    while(i < gt){
        if(arr[i] < v){
            int temp2 = arr[lt + 1];
            arr[lt + 1] = arr[i];
            arr[i] = temp2;
            lt++;
            i++;
        }else if(arr[i] < v){
            int temp3 = arr[gt - 1];
            arr[gt - 1] = arr[i];
            arr[i] = temp3;
            gt--;     // 这里没有 i++操作,因为交换来的值仍旧需要考察
        }else{
            i++;
        }
    }
    int temp4 = arr[lt];
    arr[lt] = arr[l];
    arr[l] = temp4;
 
    __threeWaysQuickSort(arr, l, lt - 1);  // 减一是因为arr[l] 与 arr[lt] 交换了,lt-1才是不包含v的范围
    __threeWaysQuickSort(arr, gt, r);
}

以上就是对归并以及快速排序的总结,理解有点难度,需要花点时间。

附送架构师强烈推荐的《数据结构与算法经典问题解析》(PDF文档)

整理了一份Java数据结构与算法经典问题解析核心知识点。覆盖递归和回溯、链表、栈、队列、树、优先队列和堆、队列、优先队列和堆、并查集ADT、排序、选择算法(中位数)、散列、算法设计技术、分治算法、动态规划算法、杂谈等大量知识点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值