归并排序和快速排序

一,归并排序

归并排序算法实现

算法思路:
如果要排序一个数组,我们先从数组中间把数组分成左数组和右数组两部分,分别对左右数组进行排序,然后将排序好的数组合并成结果数组,排序就完成了,最后只需将结果数组复制回原数组即可。

归并排序和快速排序_第1张图片

归并排序和快速排序_第2张图片

核心思想 ----- 分治思想

分治也即是分而治之,将一个大问题分解为小的子问题来解决。分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归是一种编程技巧。

归并排序分析

  1. 稳定排序算法
  2. 归并排序的执行效率与原始数据的有序程度无关,其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是O(NlogN)。
  3. 归并排序有一个缺点,那就是它不是原地排序算法。在进行子数组合并的时候,我们需要临时申请一个数组来暂时存放排好序的数据。因为这个临时空间是可以重复利用的,因此归并排序的空间复杂度为O(n),最多需要存放n个数据。

具体实现

public class MergeSort {
     

	/**
	 * 归并排序   O(NlogN)
	 * 
	 * 思想:分治
	 * @param oldArray
	 * @param tmpArray
	 * @param left
	 * @param right
	 */
	private static void mergeSort(int[] oldArray,int[] tmpArray,int left,int right) {
     
		if(left < right) {
     
			//中间元素下标
			int center = (left + right)/2;
			//分治左边数组[left , center]
			mergeSort(oldArray, tmpArray, left,center);
			//分治右边数组[center+1 , right]
			mergeSort(oldArray, tmpArray, center+1, right);
			//分开排序后合并成一个新数组在copy会原数组,此时原数组已经有序
			merge(oldArray, tmpArray, left, center+1,right);
			
		}
	}
	
	/**
	 * 排序,合并
	 * @param oldArray
	 * @param tmpArray
	 * @param leftStart
	 * @param rightStart
	 * @param rightEnd
	 */
	private static void merge(int[] oldArray, int[] tmpArray, int leftStart, int rightStart, int rightEnd) {
     
		
		//左数组开始下标
		int leftEnd = rightStart - 1;
		//辅助数组起始下标
		int tmpPos = leftStart;
		//数组元素数量
		int numElements = rightEnd - tmpPos + 1;
		
		//左右数组进行比较,按序放入辅助数组
		while(leftStart <= leftEnd && rightStart <= rightEnd) {
     
			if(oldArray[leftStart] <= oldArray[rightStart]) {
     
				tmpArray[tmpPos++] = oldArray[leftStart++];
			}else {
     
				tmpArray[tmpPos++] = oldArray[rightStart++];
			}
		}
		
		//判断子数组是否还有元素,有则拷贝到 tmp 后面
		while(leftStart <= leftEnd) {
     
			tmpArray[tmpPos++] = oldArray[leftStart++];
		}
		
		//判断子数组是否还有元素,有则拷贝到 tmp 后面
		while(rightStart <= rightEnd) {
     
			tmpArray[tmpPos++] = oldArray[rightStart++];
		}
		
		//复制辅助数组回原数组
		for(int i = 0;i<numElements;i++,rightEnd--) {
     
			oldArray[rightEnd] = tmpArray[rightEnd];
		}
	}


	public static void main(String[] args) {
     
		// TODO Auto-generated method stub
		int[] a = {
     1,8,2,6,4,2,3,8,4,6,10,12,45,21,31};
		int[] tmpArray = new int[a.length];
		mergeSort(a, tmpArray, 0, a.length-1);
		for(int i = 0;i<a.length;i++) {
     
			System.out.print(a[i]+"\t");
		}
		
	}

}

结果:
在这里插入图片描述

二,快速排序

算法思路:
随便选取一项(一般是中间值),然后形成三个组,小于被选项组,大于被选项组合等于被选项组,递归的对小于被选项组,大于被选项组进行排序,然后合并这三个组。
归并排序和快速排序_第3张图片
核心思想:------- 分治思想

算法分析:

  1. 快速排序是一个原地排序算法,是一个不稳定的排序算法,因为其在数据交换过程中可能会改变相等元素的原始位置。
  2. 平均时间复杂度O(NlogN) ,最坏时间复杂度O(N^2)
  3. 如果快速排序每次都将数据分成相等的两部分,则快排的时间复杂度和归并排序相同,也是O(nlogn),但这种情况是很难实现的。如果数据原来已经是有序的,则每次的分区都是不均等的,我们需要进行 n 次分区才能完成整个排序,此时快排的时间复杂度就退化成了O(n^2)
public class QuickSort {
     

	/**
	 * 快速排序   O(NlogN)
	 * @param list
	 */
	private static void quickSort(ArrayList<Integer> list) {
     
		
		if(list.size()>1) {
     
			
			ArrayList<Integer> smallerlist = new ArrayList<>();
			ArrayList<Integer> centerList = new ArrayList<>();
			ArrayList<Integer> largeList = new ArrayList<>();
			
			//中间元素
			Integer center = list.get(list.size()/2);
			
			//分类
			for(Integer i : list) {
     
				if(i < center) {
     
					//比中间元素小
					smallerlist.add(i);
				}else if (i > center) {
     
					//比中间元素大
					largeList.add(i);
				}else {
     
					//与中间元素相等
					centerList.add(i);
				}
			}
			
			//分组排序
			quickSort(smallerlist);
			quickSort(largeList);
			
			//清空
			list.clear();
			//合并
			list.addAll(smallerlist);
			list.addAll(centerList);
			list.addAll(largeList);
		}
	}
	
	
	public static void main(String[] args) {
     
		int[] a = {
     1,8,2,6,4,2,3,8,4,6,10,12,45,21,31};
		
		ArrayList<Integer> list = new ArrayList<>();
		
		for(int i = 0;i<a.length;i++) {
     
			list.add(a[i]);
		}
		
		quickSort(list);
		
		for(Integer integer : list) {
     
			System.out.println(integer+"\t");
		}
	}
}

结果:
在这里插入图片描述

你可能感兴趣的:(数据结构,java排序算法,快速排序,归并排序,排序,数据结构,算法)