做为程序员,时时刻刻都要与算法打交道,其中排序算法算是比较常见的一种。而在面试程序员岗位中, 不出意外,排序算法也是比较常见的考量之一。所以咱们有必要了解和掌握各类常见排序算法。
这个篇文章记录了几种常见的排序算法,并各类排序算法极端状况的优劣,供学习和参考。java
对数据进行排序意味着以特定顺序排列数据,一般是在相似数组的数据结构中。您可使用各类排序标准,常见的排序标准是从最小到最大排序数字,反之亦然,或按字典顺序排序字符串。程序员
常见的几种排序算法面试
冒泡排序经过交换相邻元素(若是它们不是所需的顺序)来工做。此过程从数组的开头重复,直到全部元素都按顺序排列。算法
举例 对4 2 1 5 3进行排序:api
2 1 4 3 5 :这是一次迭代后获得的数组。 由于在第一次传递期间至少发生了一次交换(实际上有三次),咱们须要再次遍历整个数组并重复相同的过程。 经过重复这个过程,直到再也不进行交换,咱们将有一个排序的数组。数组
代码实现数据结构
public void bubbleSort(int[] array) {
boolean sorted = false;
int temp;
while(!sorted) {
sorted = true;
for (int i = 0; i < array.length - 1; i++) {
if (a[i] > a[i+1]) {
temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
sorted = false;
}
}
}
}
复制代码
选择排序是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,而后,再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾。以此类推,直到所有待排序的数据元素排完性能
举例 对4 2 1 5 3进行排序:学习
至此排序结束。测试
代码实现
public void selectionSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int min = array[i];
int minId = i;
for (int j = i+1; j < array.length; j++) {
if (array[j] < min) {
min = array[j];
minId = j;
}
}
// 交换
int temp = array[i];
array[i] = min;
array[minId] = temp;
}
}
复制代码
插入排序是将数组划分为已排序和未排序的子数组。 排序部分的开头长度为1,对应于数组中的第一个(最左侧)元素。咱们遍历数组,在每次迭代中,咱们将数组的排序部分扩展一个元素。 在扩展时,咱们将新元素放置在已排序子阵列中的适当位置。咱们经过将全部元素向右移动直到遇到咱们没必要移动的第一个元素来实现这一点。
举例 对3 5 7 8 4 2 1 9 6进行排序:
在这个过程以后,排序的部分被一个元素扩展,咱们如今有五个而不是四个元素。每次迭代都会这样作,最后咱们将对整个数组进行排序。
代码实现
public void insertionSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int current = array[i];
int j = i - 1;
while(j >= 0 && current < array[j]) {
array[j+1] = array[j];
j--;
}
array[j+1] = current;
}
}
复制代码
合并排序/归并算法使用递归来解决比先前提出的算法更有效的排序问题,特别是它使用分而治之的方法。 使用这两个概念,咱们将整个数组分解为两个子数组,而后: 一、对数组的左半部分进行排序(递归) 二、对数组的右半部分进行排序(递归) 三、合并解决方案
举例 对3 5 4 2 1 进行排序:
在咱们的例子中,咱们有数组3 5 3 2 1,因此咱们把它分红3 5 4和2 1。为了对它们进行排序,咱们进一步将它们分红它们的组 一旦咱们到达底部,咱们就开始合并并按照咱们的方式对它们进行排序
代码实现
public void mergeSort(int[] array, int left, int right) {
if (right <= left) return;
int mid = (left+right)/2;
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
merge(array, left, mid, right);
}
private void merge(int[] array, int left, int mid, int right) {
// 计算长度
int lengthLeft = mid - left + 1;
int lengthRight = right - mid;
// 建立临时数组
int leftArray[] = new int [lengthLeft];
int rightArray[] = new int [lengthRight];
// 将须要排序的数组分别传入左右两个临时数组里
for (int i = 0; i < lengthLeft; i++)
leftArray[i] = array[left+i];
for (int i = 0; i < lengthRight; i++)
rightArray[i] = array[mid+i+1];
// 左右临时数组当前索引
int leftIndex = 0;
int rightIndex = 0;
// 将左右临时数组从新按从小到大顺序写入待排序数组中
for (int i = left; i < right + 1; i++) {
// 若是R和L中仍然有未复制的元素,则复制这两个元素的最小值
if (leftIndex < lengthLeft && rightIndex < lengthRight) {
if (leftArray[leftIndex] < rightArray[rightIndex]) {
array[i] = leftArray[leftIndex];
leftIndex++;
}
else {
array[i] = rightArray[rightIndex];
rightIndex++;
}
}
// 若是全部元素都已从rightArray复制,则复制left Array的其他部分
else if (leftIndex < lengthLeft) {
array[i] = leftArray[leftIndex];
leftIndex++;
}
// 若是全部元素都已从leftArray复制,则复制right Array的其他部分
else if (rightIndex < lengthRight) {
array[i] = rightArray[rightIndex];
rightIndex++;
}
}
}
复制代码
经过一趟排序将要排序的数据分割成独立的两部分,其中一部分的全部数据都比另一部分的全部数据都要小,而后再按此方法对这两部分数据分别进行快速排序,整个排序过程能够递归进行,以此达到整个数据变成有序序列。
举例 对6 1 2 7 9 3 4 5 10 8 进行排序: 首先取基准数, 每一轮的排序要义是将数组以基准数为准,将数组分为左右两个部分, 左边部分都比基准数小, 右边部分都比基准数大。
至此, 第一轮搜索结束, 基准数6将数组分割成两部分, 接下来,将这两部分按照1-8步骤, 分别进行排序, 直至没法切分,则排序完成。
摘用网上图片:
代码实现
public void quickSort(int[] array, int begin, int end) {
if (end <= begin) return;
int pivot = partition(array, begin, end);
quickSort(array, begin, pivot-1);
quickSort(array, pivot+1, end);
}
private int partition(int[] array, int begin, int end) {
int baseValue = array[begin];
int leftIndex = begin + 1;
int rightIndex = end;
int temp;
while(leftIndex != rightIndex) {
//先从左边向右搜索
while(array[leftIndex] <= baseValue && leftIndex < rightIndex) {
leftIndex ++;
}
//再从右边向左搜索
while(rightIndex] >= baseValue && leftIndex < rightIndex) {
rightIndex --;
}
//交换两个数的位置
if (leftIndex < rightIndex) {
temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
}
}
//最终将基准数归位
array[begin] = array[leftIndex];
array[leftIndex] = baseValue;
//返回基准数位置
return leftIndex;
}
复制代码
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似彻底二叉树的结构,并同时知足堆积的性质:即子结点的键值或索引老是小于(或者大于)它的父节点
堆排序的基本思路:
代码实现
public void heapSort(int[] array) {
if (array.length == 0) return;
// 构建大顶堆
int length = array.length;
for (int i = length / 2-1; i >= 0; i--) {
heapify(array, length, i);
}
for (int i = length-1; i >= 0; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
//从新构建大顶堆
heapify(array, i, 0);
}
}
private void heapify(int[] array, int length, int i) {
int leftChild = 2*i+1;
int rightChild = 2*i+2;
int largest = i;
// 若是左节点大于父节点
if (leftChild < length && array[leftChild] > array[largest]) {
largest = leftChild;
}
// 若是右节点大于父节点
if (rightChild < length && array[rightChild] > array[largest]) {
largest = rightChild;
}
// 若是须要则交换
if (largest != i) {
int temp = array[i];
array[i] = array[largest];
array[largest] = temp;
heapify(array, length, largest);
}
}
复制代码
名称 | 最好状况 | 平均状况 | 最坏状况 | 额外空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | n | n^2 | n^2 | 1 | 稳定 |
选择排序 | n^2 | n^2 | n^2 | 1 | 不稳定 |
插入排序 | n | n^2 | n^2 | 1 | 稳定 |
归并排序 | nlog n | nlog n | nlog n | n | 稳定 |
快速排序 | nlog n | nlog n | n | nlog n | 不稳定 |
插入排序 | n | nlog n | nlog n | 1 | 不稳定 |
在10,000个整数的随机数组副本上,以上诉几种算法进行测试,结果以下:
时间(NS) | 冒泡排序 | 插入排序 | 选择排序 | 归并 | 堆排序 | 快速排序 |
---|---|---|---|---|---|---|
第一次运行 | 266089476 | 21973989 | 66603076 | 5511069 | 5283411 | 4156005 |
第二轮 | 323692591 | 29138068 | 80963267 | 8075023 | 6420768 | 7060203 |
第三次运行 | 303853052 | 21380896 | 91810620 | 7765258 | 8009711 | 7622817 |
第四次运行 | 410171593 | 30995411 | 96545412 | 6560722 | 5837317 | 2358377 |
第五次运行 | 315602328 | 26119110 | 95742699 | 5471260 | 14629836 | 3331834 |
第六次运行 | 286841514 | 26789954 | 90266152 | 9898465 | 4671969 | 4401080 |
第七次运行 | 384841823 | 18979289 | 72569462 | 5135060 | 10348805 | 4982666 |
八跑 | 393849249 | 34476528 | 107951645 | 8436103 | 10142295 | 13678772 |
第九跑 | 306140830 | 57831705 | 138244799 | 5154343 | 5654133 | 4663260 |
第十次运行 | 306686339 | 34594400 | 89442602 | 5601573 | 4675390 | 3148027 |
仅以此样本为例, 冒泡排序在性能方面是最差的, 堆排和快排性能最佳
对数据集进行排序是一种很是常见的操做,不管是进一步分析它们,仍是使用依赖于排序数据的更有效算法来加速搜索,过滤数据等。
欢迎长按下图关注公众号: 终身幼稚园