对某些数据进行排序是程序员不得不面对的一些需求,排序算法也是面试过程中老生常谈的一些问题,下面来总结一下常用到的一些排序算法,常用到的排序算法包括:冒泡排序,选择排序,插入排序,希尔排序,快速排序。
冒泡排序是指在排序时从最开始或者从最末尾开始,依次比较相邻两个数据项,遵循更大(小)往前(后)移动的规矩,从而依次将最值放到了开始或者末尾处,循环下来,最终完成排序功能。
效率分析 比较次数:C*O(n²) (C为常数,n为数据项数量)
交换次数:C*O(n²)
代码示例:
int[] a = new int[10];
int length = a.length;
/**
* 冒泡排序,大的往后移动 比较n*n次,交换n*n次
*/
public void bubblesort() {
for (int out = length - 1; out > 1; out--) {
for (int in = 0; in < out; in++) {
if (a[in] > a[in + 1]) {
swap(in, in + 1);
}
}
}
}
private void swap(int in, int i) {
int temp = a[in];
a[in] = a[i];
a[i] = temp;
}
选择排序是从左边第一位开始,往右边移动,记录住最大值或者最小值,然后再和左边第一位的数据交换,完成第一项数据的交换移动,然后从左边第二位数据开始,依此完成数据的交换移动,就完成了排序功能。
效率分析 比较次数:C*O(n²) (C为常数,n为数据项数量)
交换次数:C*O(n)
示例代码:
int[] a = new int[10];
int length = a.length;
/**
* 选择排序,选出最小值然后再放到前面,比较n*n,交换n次
*/
public void selectSort() {
int min;
for (int out = 0; out < length - 1; out++) {
min = out;
for (int in = out + 1; in < length; in++) {
if (a[in] < a[min]) {
min = in;
}
}
if (min != out)
swap(min, out);
}
}
private void swap(int in, int i) {
int temp = a[in];
a[in] = a[i];
a[i] = temp;
}
插入排序是指从左往右或者是从右往左依此局部排序,在某一刻存在局部有序,它的效率比冒泡排序和选择排序要高一些,如果是在大部分已经顺序的数据中,插入排序的效率会更高。
效率分析 比较次数:C*O(n²) (C为常数,n为数据项数量)
交换次数:C*O(n)
示例代码:
int[] a = new int[10];
int length = a.length;
/**
* 插入排序
*/
public void insertSort() {
int in, out;
for (out = 1; out < length; out++) {
in = out;
int temp = a[out];
while (in > 0 && a[in - 1] >= temp) {
a[in] = a[in - 1];
in--;
}
a[in] = temp;
}
}
private void swap(int in, int i) {
int temp = a[in];
a[in] = a[i];
a[i] = temp;
}
在插入排序中,通过依次局部排序,如果数据项很多并且后面有一些极大或者极小值,将会存在很多的数据交换,从而降低效率,而希尔排序就是为了解决这些麻烦,希尔排序以更大的增量来进行插入排序,并依次减小增量,最后以1来进行插入排序,这样就可以避免数据量很大时,插入排序因为太多的数据交换所造成的效率低的问题。增量的常用最佳选择是3h+1,并且最后一定要以1进行插入排序才完成了排序效果。
示例代码:
int[] a = new int[10];
int length = a.length;
/**
* 希尔排序
*
* 以h为增量的插入排序,h=h*3+1,h递减,最后h为1时即为普通插入排序
*
* 效率比冒泡,选择,插入排序更高,更快
*/
public void shellSort() {
int in, out, temp;
int h = 1;
while (h <= length / 3) {
h = h * 3 + 1;
}
while (h > 0) {
for (out = h; out < length; out++) {
temp = a[out];
in = out;
while (in > h - 1 && a[in - h] >= temp) {
a[in] = a[in - h];
in -= h;
}
a[in] = temp;
}
h = (h - 1) / 3;
}
}
快速排序,顾名思义是一种速度很快的排序算法,它的效率比起冒泡排序,选择排序,插入排序和希尔排序都要高,速度都更快一些,快速排序指的是 在数组中选择一个枢纽(可以选择左中右位置的一个中间值),然后按照枢纽将数组分成左右两边,枢纽放在中间,再对左右两边的数组分成两份分别完成之前的操作,此中用到递归的思想,这样就可以很快的完成排序,在数据项小于3项时可以直接通过交换数据完成排序,不必在分。
示例代码:
int[] a = new int[10];
int length = a.length;
public void quickSort() {
int left = 0;
int right = length - 1;
quickSortAction(left, right);
}
private void quickSortAction(int left, int right) {
if (right - left <= 2) {
//3项或者以下的数据直接交换
changeData(left, right);
} else {
//找到枢纽
int median = medianOf3(left, right);
//根据选出的枢纽,把大于等于枢纽的数据放到枢纽的右边,小于枢纽的数据放在枢纽的
//左边,然后选出分割线的位置,进行下次的划分排序
int partition = partitonInt(left, right, median);
quickSortAction(left, partition - 1);
quickSortAction(partition + 1, right);
}
}
//找到左中右三个数据的中间项作为枢纽,并将枢纽放到右边的倒数第二位
private int medianOf3(int left, int right) {
int center = (right + left) / 2;
if (a[left] < a[right]) {
swap(left, right);
}
if (a[center] < a[right]) {
swap(left, right);
}
if (a[left] < a[center]) {
swap(left, right);
}
swap(center, right - 1);
return a[right - 1];
}
//从左边开始,遇到大于等于中间项的数据停下来,然后从右边开始,遇到小于中间项的停下来,交换
//两个数的位置
//...依次下去,左后leftptr以左的数据都是小于枢纽的,右边的数据都是大于或者等于枢纽的,
//把枢纽移到leftptr位//置,并把左边界返回,作为下次两边数据的界限
private int partitonInt(int left, int right, int median) {
int leftPtr = left;
int rightPtr = right - 1;
while (true) {
while (a[++leftPtr] < median) ;
while (a[--rightPtr] > median) ;
if (leftPtr >= rightPtr) {
break;
} else {
swap(leftPtr, rightPtr);
}
}
swap(leftPtr, right - 1);
return leftPtr;
}
private void changeData(int left, int right) {
int size = right - left;
if (size <= 0) {
return;
} else if (size == 1) {
if (a[left] < a[right]) {
swap(left, right);
}
} else if (size == 2) {
if (a[left] < a[right]) {
swap(left, right);
}
if (a[left + 1] < a[right]) {
swap(left, right);
}
if (a[left] < a[left + 1]) {
swap(left, right);
}
}
}
private void swap(int in, int i) {
int temp = a[in];
a[in] = a[i];
a[i] = temp;
}