本文总结面试常见的排序算法,及基本的实现java
话不多说,先上干货。
稳定的排序算法是:冒泡排序,直接插入排序,归并排序,基数排序,二叉树排序,计数排序。
不稳定的排序算法:选择排序,快速排序,堆排序,希尔排序。
面试中常见的算法: 快速排序>归并排序>堆排序>冒泡>插入>选择。下面依次实现各排序算法。
public static void QuickSort(int[] a, int l, int r ) {
if(l < r) {
int p = partition(a, l, r);
QuickSort(a, l, p-1);
QuickSort(a, p+1, r);
}
}
private static int partition(int[] a, int l, int r) {
int tmp = a[r];
int p = l - 1;
for(int i=l; iif(a[i]< tmp) {
p++;
swap(a, i, p);
}
}
swap(a, r, p+1);
return p+1;
}
算法原理
归并排序具体工作原理如下(假设序列共有n个元素):
将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕
归并排序是稳定的排序算法,其时间复杂度为O(nlogn),如果是使用链表的实现的话,空间复杂度可以达到O(1),但如果是使用数组来存储数据的话,在归并的过程中,需要临时空间来存储归并好的数据,所以空间复杂度为O(n)
public void mergeSort(int[] A, int l, int r) {
if(l>=r) return;
int m = l + (r-l)/2;
mergeSort(A,l,m);
mergeSort(A,m+1,r);
merge(A,l,m,r);
}
public void merge(int[] A, int l, int mid, int r) {
int[] B = new int[A.length];
int s = l;
int m = mid+1;
int k = l;//数组标志位
while(s<=mid && m<=r) {
if(A[s]<=A[m]) {
B[k++] = A[s++];
} else{
B[k++] = A[m++];
}
}
while(s<=mid) {
B[k++] = A[s++];
}
while(m<=r) {
B[k++] = A[m++];
}
for(int i=l;i<=r;i++) {
A[i] = B[i];
}
}
首先讲一下二叉堆
二叉堆是完全二叉树或者近似完全二叉树,满足两个特性
1. 父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值
2. 每个结点的左子树和右子树都是一个二叉堆
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。一般二叉树简称为堆。
一般都是数组来存储堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。存储结构如图所示:
/**
* 将数组arr构建大根堆
* @param arr 待调整的数组
* @param i 待调整的数组元素的下标
* @param len 数组的长度
*/
public void heap_adjust(int arr[], int i, int len)
{
int child;
int temp;
for (; 2 * i + 1 < len; i = child)
{
child = 2 * i + 1; // 子结点的位置 = 2 * 父结点的位置 + 1
// 得到子结点中键值较大的结点
if (child < len - 1 && arr[child + 1] > arr[child])
child ++;
// 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
if (arr[i] < arr[child])
{
temp = arr[i];
arr[i] = arr[child];
arr[child] = temp;
}
else
break;
}
}
/**
* 堆排序算法
*/
public void heap_sort(int arr[], int len)
{
int i;
// 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
for (int i = len / 2 - 1; i >= 0; i--)
{
heap_adjust(arr, i, len);
}
for (i = len - 1; i > 0; i--)
{
// 将第1个元素与当前最后一个元素交换,保证当前的最后一个位置的元素都是现在的这个序列中最大的
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
heap_adjust(arr, 0, i);
}
}
算法原理
相邻的数据进行两两比较,小数放在前面,大数放在后面,这样一趟下来,最小的数就被排在了第一位,第二趟也是如此,如此类推,直到所有的数据排序完成
实现
public void bubbleSort(int[] A) {
int len = A.length;
for(int i=0; ifor(int j=len-1; j>i; j--) {
if(A[i]>A[j]){
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
}
}
public void insertSort(int[] A) {
int len = A.length;
int j;
for(int i=0;iint tmp = A[i];
for(j = i; j > 0 && tmp < A[j-1]; j--)
A[j] = A[j-1];
A[j] = tmp;
}
}
void select_sort(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
int index = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j] < arr[index])
index = j;
}
if (index != i)
{
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
}