最近面试没少遇到排序算法考察,在此总结一些常见的排序算法以及相关实现
1. 基本思想:
每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
2. 排序过程:
【示例】:
[初始关键字] [49] 38 65 97 76 13 27 49
J=2(38) [38 49] 65 97 76 13 27 49
J=3(65) [38 49 65] 97 76 13 27 49
J=4(97) [38 49 65 97] 76 13 27 49
J=5(76) [38 49 65 76 97] 13 27 49
J=6(13) [13 38 49 65 76 97] 27 49
J=7(27) [13 27 38 49 65 76 97] 49
J=8(49) [13 27 38 49 49 65 76 97]
java代码:
//待排序的数组a
public static void insertSort(int[] a) {
for ( i = 1; i < a.length; i++) {
if (a[i] < a[i - 1]) {
temp = a[i]; //
a[i]=a[i-1];
for ( j = i; temp < a[j-1]&&j>0; j--) {
a[j] = a[j-1];
}
a[j] = temp; // 插入到正确的位置.
}
}
}
二、选择排序
1. 基本思想:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最后排序结果 13 27 38 49 49 76 76 97
Java代码:
public static void selectSort(int[] a) {
int small;
int temp;
for(int i=0;i<a.length;i++){
small=i;
//遍历数组找到最小数字的索引值
for(int j=i+1;j<a.length;j++){
if(a[j]<a[small]){
small=j;
}
}
if(small!=i){
temp=a[i];
a[i]=a[small];
a[small]=temp;
}
}
}
三、堆排序(Heap Sort)
1. 基本思想:
堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
2. 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:
Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])
堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个 堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等 于其孩子的关键字,则称之为大根堆。
3. 排序过程:
堆排序正是利用小根堆(或大根堆)来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无 序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐 步向前扩大到整个记录区。
【示例】:对关键字序列42,13,91,23,24,16,05,88建堆
java代码:
public static void heapSort(int[] a) {
int temp;
initCreateHeap(a);//初始化堆
for (int i = a.length - 1; i > 0; i--) {
temp = a[0];
a[0] = a[i];
a[i] = temp;
createHeap(a, 0, i);创建堆
}
}
/**
* a为创建堆的数组,h为创建堆的起始结点,n为创建堆的结束节点
* @param a int[]
* @param n int
* @param h int
*/
private static void createHeap(int a[], int h, int n) {
int i, j, flag;
int temp;
i = h;//根节点下标
j = 2 * i + 1;//i节点左孩子
temp = a[i];
flag = 0;
while (j < n && flag != 1) {
//选取最大的孩子节点
if (j < n - 1 && a[j] < a[j + 1]) {
j++;
}
//判断根节点与孩子节点的大小
if (temp > a[j]) {
flag = 1;
}
//交换根节点与孩子节点
else {
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
}
a[i] = temp;
}
/**
* 从第一个非叶节点a[i](i=(n-1)/2)开始,到根节点a[0]为止,循环调用createHeap的过程
* @param a int[]
*/
private static void initCreateHeap(int[] a) {
for(int i=(a.length-1)/2; i>=0; i--) {
createHeap(a,i,a.length);
}
}
四、冒泡排序
基本思想:依次比较相邻的两个数,将小数放在前面,大数放在后面。即首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大 数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复以上过程,仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个 数不再大于第2个数),将小数放前,大数放后,一直比较到最小数前的一对相邻数,将小数放前,大数放后,第二趟结束,在倒数第二个数中得到一个新的最小 数。如此下去,直至最终完成排序。
Java 程序:
public void bubbleSort(int[] array) {
int temp;
for(int i=0;i<array.length-1;i++){
for(int j=i+1;j<array.length;j++){
if (array[i]>array[j]){
temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
}
五、快速排序(quick sort)
基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序是典型的使用分治策略的一种交换排序.
一次排序步骤:
得到中心点作为关键点(其他的得到关键点的方法要根据排序数组来决定, 由于关键点是用来比较的基准所以如果基准选择得当可以减少交换的次数, 从而提高算法效率 , 是比较有技巧的部分), 以该关键点元素作为基准, 从数组两头分别与之比较, 大的放右边,小的放左边,相等的无论哪边都成(干脆不动). 最后当遍历完数组一遍(即两头的标志指向同一个数组元素)时把关键点元素放到该位置(此时确定这里为分治点 ),完成一次排序.
对关键点左右两个子序列递归调用, 以子序列小于两个元素为退出递归条件. 完成整个数组的排序.
Java代码
private void qsort(int[] array, int begin, int end) {
if (end - begin < 1) {
return;
}
int pivot = partition(array, begin, end);//进行一次排序得到的分治点
qsort(array, begin, pivot);
qsort(array, pivot + 1, end);
}
// 一次排序
private int partition(int[] array, int begin, int end) {
int pivot = getPivot(begin, end);//得到两个位置的中间值(begin+end)>>1
int tmp = array[pivot];
array[pivot] = array[end];
while (begin != end) {
while (array[begin] < tmp && begin < end) {
begin++;
}
if (/* array[begin] > tmp && */begin < end) {
array[end] = array[begin];
end--;
}
while (array[end] > tmp && end > begin) {
end--;
}
if (/* array[end] < tmp && */end > begin) {
array[begin] = array[end];
begin++;
}
}
// 此时两个下标指向同一个元素.以这个位置左右分治(分治点)
array[begin] = tmp;
return begin;
}