一,归并排序

归并排序算法实现

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

归并排序和快速排序_排序

归并排序和快速排序_排序_02

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

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

归并排序分析

  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");
}

}

}

结果:

归并排序和快速排序_数据结构_03

二,快速排序

算法思路:

随便选取一项(一般是中间值),然后形成三个组,小于被选项组,大于被选项组合等于被选项组,递归的对小于被选项组,大于被选项组进行排序,然后合并这三个组。

归并排序和快速排序_归并排序_04


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

算法分析:

  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");
}
}
}

结果:

归并排序和快速排序_快速排序_05