归并排序、堆排序、快速排序
2017-04-04 20:17
417 查看
归并排序
递归法
迭代法
堆排序
快速排序
最坏情况:Ο(nlogn)
平均情况:Ο(nlogn)
辅助空间:Ο(n)
稳定性:稳定
归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。
归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。
计算左右边界和中间分割线
递归左右数组
将左右数组合并
3.1. 提取左右数组为左右临时数组
3.2. 在左右临时数组提取较大(小)的元素放入原数组
输出:
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
2 4 5 6 8 10 12 9 1 7 3 13 0
2 4 5 6 8 10 12 1 9 7 3 13 0
2 4 5 6 8 10 12 1 7 9 3 13 0
2 4 5 6 8 10 12 1 7 9 3 13 0
2 4 5 6 8 10 12 1 7 9 0 3 13
2 4 5 6 8 10 12 0 1 3 7 9 13
0 1 2 3 4 5 6 7 8 9 10 12 13
计算步长,步长每次*2
根据步长计算左右边界和中间分割线
将左右数组合并
重新计算左右边界
输出:
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 3 7 13 0
2 4 5 6 8 9 10 12 1 3 7 13 0
2 4 5 6 8 9 10 12 0 1 3 7 13
0 1 2 3 4 5 6 7 8 9 10 12 13
最坏情况:Ο(nlogn)
平均情况:Ο(nlogn)
辅助空间:Ο(1)
稳定性:不稳定
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构(通常堆是通过一维数组来实现的),并同时满足堆的性质:即子结点的键值总是小于(或者大于)它的父节点。
过程:
创建初始堆(最大堆或最小堆)
1.1. 调整每个非叶子节点及其子堆
根节点与最后元素交换
2.1. 逐渐缩小堆
2.2. 从堆顶进行堆调整
输出:
5 8 6 12 2 4 11 9 1 7 3 13 0 10
5 8 6 12 2 13 11 9 1 7 3 4 0 10
5 8 6 12 7 13 11 9 1 2 3 4 0 10
5 8 6 12 7 13 11 9 1 2 3 4 0 10
5 8 13 12 7 6 11 9 1 2 3 4 0 10
5 12 13 9 7 6 11 8 1 2 3 4 0 10
13 12 11 9 7 6 10 8 1 2 3 4 0 5
12 9 11 8 7 6 10 5 1 2 3 4 0 13
11 9 10 8 7 6 0 5 1 2 3 4 12 13
10 9 6 8 7 4 0 5 1 2 3 11 12 13
9 8 6 5 7 4 0 3 1 2 10 11 12 13
8 7 6 5 2 4 0 3 1 9 10 11 12 13
7 5 6 3 2 4 0 1 8 9 10 11 12 13
6 5 4 3 2 1 0 7 8 9 10 11 12 13
5 3 4 0 2 1 6 7 8 9 10 11 12 13
4 3 1 0 2 5 6 7 8 9 10 11 12 13
3 2 1 0 4 5 6 7 8 9 10 11 12 13
2 0 1 3 4 5 6 7 8 9 10 11 12 13
1 0 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。
最坏情况:Ο(n2)
平均情况:Ο(nlogn)
辅助空间:Ο(logn)~ Ο(n)
稳定性:不稳定
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。
过程:
按已坐好位置的基准将数组分为左右两个子数组
将比基准小的元素放到前面,比基准大的元素放到后面
将基准放置到准确位置
输出:
5 8 6 2 4 10 9 1 7 3 0 11 12 13
0 8 6 2 4 10 9 1 7 3 5 11 12 13
0 2 4 1 3 5 9 8 7 6 10 11 12 13
0 2 1 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 6 8 7 9 10 11 12 13
0 1 2 3 4 5 6 8 7 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。
来源:http://www.cnblogs.com/eniac12/p/5329396.html
递归法
迭代法
堆排序
快速排序
归并排序
最好情况:Ο(nlogn)最坏情况:Ο(nlogn)
平均情况:Ο(nlogn)
辅助空间:Ο(n)
稳定性:稳定
归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。
归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。
递归法
过程:计算左右边界和中间分割线
递归左右数组
将左右数组合并
3.1. 提取左右数组为左右临时数组
3.2. 在左右临时数组提取较大(小)的元素放入原数组
public static void main(String[] args) { int[] arr = new int[]{5, 8, 6, 12, 2, 4, 10, 9, 1, 7, 3, 13, 0}; int length1 = arr.length; leftArr = new int[length]; rightArr = new int[length]; mergeSortRecursion(arr, 0, length - 1); } static void mergeSortRecursion(int[] arr, int left, int right) { //递归实现的归并排序 int middle = (left + right) / 2; //取中间位置 if (left < right) { mergeSortRecursion(arr, left, middle); //递归左边数组 mergeSortRecursion(arr, middle + 1, right); //递归右边数组 merge(arr, left, middle, right); //将已经排序的左边数组和右边数组进行排序 print(arr); } } static void merge(int[] arr, int left, int middle, int right) { int[] leftArr = new int[right - left + 1]; int[] rightArr = new int[right - left + 1]; System.arraycopy(arr, left, leftArr, 0, middle - left + 1); //将左边数组拷贝到leftArr System.arraycopy(arr, middle + 1, rightArr, 0, right - middle); //将右边数组拷贝到rightArr leftArr[middle - left + 1] = Integer.MAX_VALUE; //在数组最后面设置正无穷,使下面循环内的判断不会越界 rightArr[right - middle] = Integer.MAX_VALUE; for (int k = left, i = 0, j = 0; k <= right; k++) { if (leftArr[i] <= rightArr[j]) { arr[k] = leftArr[i++]; } else { arr[k] = rightArr[j++]; } } }
输出:
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
5 6 8 12 2 4 10 9 1 7 3 13 0
2 4 5 6 8 10 12 9 1 7 3 13 0
2 4 5 6 8 10 12 1 9 7 3 13 0
2 4 5 6 8 10 12 1 7 9 3 13 0
2 4 5 6 8 10 12 1 7 9 3 13 0
2 4 5 6 8 10 12 1 7 9 0 3 13
2 4 5 6 8 10 12 0 1 3 7 9 13
0 1 2 3 4 5 6 7 8 9 10 12 13
迭代法
过程:计算步长,步长每次*2
根据步长计算左右边界和中间分割线
将左右数组合并
重新计算左右边界
static void mergeSortIteration(int[] arr, int left, int right) { //迭代实现的归并排序 for (int gap = 1; gap <= right - left; gap *= 2) { int leftPoint = left, rightPoint; //left和right都是数组的左右边界,leftPoint和rightPoint都是代表数组位置的游离指针; while (leftPoint + gap <= right) { rightPoint = leftPoint + gap * 2 - 1; int middle = (leftPoint + rightPoint) / 2; //middle一定要放在检查rightPoint的前面,这样才能准确分块 if (rightPoint > right) { rightPoint = right; } merge(arr, leftPoint, middle, rightPoint); leftPoint = rightPoint + 1; print(arr); } } }
输出:
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 10 9 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 8 6 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 7 3 13 0
5 6 8 12 2 4 9 10 1 3 7 13 0
2 4 5 6 8 9 10 12 1 3 7 13 0
2 4 5 6 8 9 10 12 0 1 3 7 13
0 1 2 3 4 5 6 7 8 9 10 12 13
堆排序
最好情况:Ο(nlogn)最坏情况:Ο(nlogn)
平均情况:Ο(nlogn)
辅助空间:Ο(1)
稳定性:不稳定
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构(通常堆是通过一维数组来实现的),并同时满足堆的性质:即子结点的键值总是小于(或者大于)它的父节点。
过程:
创建初始堆(最大堆或最小堆)
1.1. 调整每个非叶子节点及其子堆
根节点与最后元素交换
2.1. 逐渐缩小堆
2.2. 从堆顶进行堆调整
public static void main(String[] args) { int[] arr = new int[]{5, 8, 6, 12, 2, 4, 10, 9, 1, 7, 3, 13, 0, 11}; sortHeap(arr); } static void sortHeap(int[] arr) { buildHeap(arr); for (int i = arr.length - 1; i >= 1; i--) { //对最大堆进行排序 exchange(arr, 0, i); //将第根节点元素与最后节点(i)元素交换 heapAdjust(arr, 0, i); //最后节点元素被固定,即可交换的数组长度为i print(arr); } } static void buildHeap(int[] arr) { //建堆(最大堆) for (int i = (arr.length - 1) / 2; i >= 0; i--) { heapAdjust(arr, i, arr.length); //自下向上对每个非叶子节点进行调整 print(arr); } } static void heapAdjust(int[] arr, int i, int heapSize) { int leftChild = i * 2 + 1; //i节点(父节点)的左孩子和右孩子 int rightChild = leftChild + 1; int largest = i; //父节点、左孩子、右孩子进行大小比较 if (leftChild < heapSize && arr[leftChild] > arr[i]) { largest = leftChild; } if (rightChild < heapSize && arr[rightChild] > arr[leftChild]) { largest = rightChild; } if (largest != i) { //如果最大值不是父节点,那么不符合堆性质,调整最大值位置 exchange(arr, i, largest); heapAdjust(arr, largest, heapSize); //从被调整的孩子继续递归 } } static void exchange(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } static void print(int[] arr) { for (int a : arr) { //输出 System.out.print(a + "\t"); } System.out.println(); }
输出:
5 8 6 12 2 4 11 9 1 7 3 13 0 10
5 8 6 12 2 13 11 9 1 7 3 4 0 10
5 8 6 12 7 13 11 9 1 2 3 4 0 10
5 8 6 12 7 13 11 9 1 2 3 4 0 10
5 8 13 12 7 6 11 9 1 2 3 4 0 10
5 12 13 9 7 6 11 8 1 2 3 4 0 10
13 12 11 9 7 6 10 8 1 2 3 4 0 5
12 9 11 8 7 6 10 5 1 2 3 4 0 13
11 9 10 8 7 6 0 5 1 2 3 4 12 13
10 9 6 8 7 4 0 5 1 2 3 11 12 13
9 8 6 5 7 4 0 3 1 2 10 11 12 13
8 7 6 5 2 4 0 3 1 9 10 11 12 13
7 5 6 3 2 4 0 1 8 9 10 11 12 13
6 5 4 3 2 1 0 7 8 9 10 11 12 13
5 3 4 0 2 1 6 7 8 9 10 11 12 13
4 3 1 0 2 5 6 7 8 9 10 11 12 13
3 2 1 0 4 5 6 7 8 9 10 11 12 13
2 0 1 3 4 5 6 7 8 9 10 11 12 13
1 0 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
堆排序是不稳定的排序算法,不稳定发生在堆顶元素与A[i]交换的时刻。
快速排序
最好情况:Ο(nlogn)最坏情况:Ο(n2)
平均情况:Ο(nlogn)
辅助空间:Ο(logn)~ Ο(n)
稳定性:不稳定
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。
过程:
按已坐好位置的基准将数组分为左右两个子数组
将比基准小的元素放到前面,比基准大的元素放到后面
将基准放置到准确位置
public static void main(String[] args) { int[] arr = new int[]{5, 8, 6, 12, 2, 4, 10, 9, 1, 7, 3, 13, 0, 11}; quickSort(arr, 0, arr.length - 1); } static void quickSort(int[] arr, int left, int right) { //递归分配基准的左右数组 if (left < right) { int index = partition(arr, left, right); quickSort(arr, left, index - 1); quickSort(arr, index + 1, right); } } static int partition(int[] arr, int left, int right) { //比基准小的放到基准左边,比基准大的放到基准右边 int pivot = arr[right]; //基准值,不一定是最右侧的元素 int lastArrTail = left; //被排序数列最左侧的游离指针,指针所在位置及往前位置的元素,皆比基准小 for (int i = left; i < right; i++) { //将比基准小的元素与比基准大的元素进行交换 if (arr[i] <= pivot) { exchange(arr, i, lastArrTail++); } } exchange(arr, right, lastArrTail); //将基准放在游离指针的位置,该位置及往后的元素比基准大,该位置往前的元素比基准小 print(arr); return lastArrTail; //排布完成后,返回该基准的位置,该基准已经坐在正确的排列位置上了,但基准的前后序列可能还存在乱序 }
输出:
5 8 6 2 4 10 9 1 7 3 0 11 12 13
0 8 6 2 4 10 9 1 7 3 5 11 12 13
0 2 4 1 3 5 9 8 7 6 10 11 12 13
0 2 1 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 9 8 7 6 10 11 12 13
0 1 2 3 4 5 6 8 7 9 10 11 12 13
0 1 2 3 4 5 6 8 7 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1 2 3 4 5 6 7 8 9 10 11 12 13
快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。
来源:http://www.cnblogs.com/eniac12/p/5329396.html
相关文章推荐
- 排序算法: 冒泡排序, 快速排序,希尔排序,直接插入排序 ,直接选择排序,归并排序,堆排序
- 归并排序,快速排序,堆排序,冒泡排序 c语言源代码
- O(lgn)的三种排序,快速排序、归并排序、堆排序
- 各种排序算法实现——基数排序、归并排序、插入排序、冒泡排序、选择排序、快速排序、堆排序、希尔排序
- 快速排序、堆排序、归并排序
- 几种常见的排序算法,选择排序,冒泡排序,希尔排序,堆排序,快速排序,归并排序,基数排序的比较
- Java实现快速排序、归并排序、堆排序
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法, 冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 数据结构之排序算法二:堆排序,快速排序,归并排序
- 快速排序-堆排序-STL中的堆排序-归并排序 》时间比较
- 三种排序算法(归并排序、快速排序,堆排序)
- 比较排序总结——直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序
- 归并排序,快速排序,堆排序,冒泡排序 c语言源代码
- 牛客网Java刷题知识点之插入排序(直接插入排序和希尔排序)、选择排序(直接选择排序和堆排序)、冒泡排序、快速排序、归并排序和基数排序(博主推荐)
- 常用的排序算法(快速排序、插入排序、希尔排序、堆排序、冒泡排序、选择排序、归并排序)
- java五种内部排序(直接插入排序、希尔排序、快速排序、堆排序、归并排序)
- 七大内部排序算法总结(插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、归并排序、堆排序)
- 快速排序、归并排序、堆排序三种算法性能比较
- 常用的较优排序之快速排序,堆排序,归并排序
- 面试题:冒泡排序,选择排序,插入排序,快速排序,归并排序,堆排序