做过几次各大IT公司的实习生招聘笔试题,发现数据结构还是很重要的,其中排序问题总是会出现在试题上,所以对几种常见的排序方法总结下。
其实排序可分为两大类:内部排序和外部排序
内部排序:指需排序的记录存放在计算机随机存储器中进行的排序过程,也就是小数量的数据排序,像冒泡排序,选择排序等就是内部排序;
外部排序:指需排序的记录数量很大,以致内存一次不能容纳全部记录,在排序过程中需对外存进行访问的排序过程;这种我暂时没怎么接触过,不多说了;
下面是几种常见的部内排序方法:
1. 直接插入排序
每次从无序表中抽出一个数插入到有序表中,使得表仍然有序,第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从后向前扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程,整个过程如下:
假设序列为49 38 65 97 76 13 27
【初始关键字】 ( 49 ) 38 65 97 13 27
i=2 (38) ( 38 49 ) 65 97 13 27
i=3 (65) ( 38 49 65 ) 97 13 27
i=4 (97) ( 38 49 65 97 ) 13 27
i=5 (13) ( 13 38 49 65 97 ) 27
i=5 (27) ( 13 27 38 49 65 97 )
监视哨(保存当前待比较的数值)temp
Java代码实现:
int[] text(int[] a ){
for (int i = 1; i < a.length; i++) {
int temp = a[i];
for (int j = i; j > 0 && temp < a[j-1]; j--) {
a[j] = a[j - 1];
}
a[j] = temp;
}
return a;
}
2.希尔排序
又称缩小增量排序,其基本思想是:先将整个待排序的记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序;
就是先取一个小于n(整个记录的长度)的整数t1作为第一个增量,把文件的全部记录分成t1个组。所有距离为t1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量t2<t1重复上述的分组和排序,直至所取的增量ti=1(ti<ti-l<…<t2<t1),即所有记录放在同一组中进行直接插入排序为止;
如果记录的量大且一组内的记录相隔距离远,那么就可以省去多次数据交换,这就是对直接插入排序的改进之处。示例如下:
假设序列为:49 38 65 97 76 13 27 49 55 04
增量可取: 5 , 3 , 1
【初始关键字】 49 38 65 97 76 13 27 49 55 04
先分成5组:(49 13) (38 27) (65 49) (97 55) (76 04)
每一组进行直接插入排序后:13 27 49 55 04 49 38 65 97 76(第一趟排序结果)
然后分成3组:(13 55 38 76) (27 04 65) (49 49 97)
每一组进行直接插入排序后:13 04 49 38 27 49 55 65 97 76(第二趟排序结果)
再分成1组(对整个序列排序) (13 04 49 38 27 49 55 65 97 76)
进行直接插入排序后:04 13 27 38 49 49 55 65 76 97(第三趟排序结果)
Java代码实现:
int [] ShellSort(int[] a) {
int Temp; // 暂存变量
int DataLength; // 分割集合的间隔长度
int Pointer; // 进行处理的位置
DataLength = a.length / 2; // 初始集合间隔长度
while (DataLength != 0) // 数列仍可进行分割
{ // 对各个集合进行处理
for (int j = DataLength; j < a.length; j++) {
Temp = a[j]; // 暂存Data[j]的值,待交换值时用
Pointer = j - DataLength; // 计算进行处理的位置
// 进行集合内数值的比较与交换值
while (Temp < a[Pointer] && Pointer >= 0 && Pointer < a.length) {
a[Pointer + DataLength] = a[Pointer];
// 计算下一个欲进行处理的位置
Pointer = Pointer - DataLength;
if (Pointer < 0 || Pointer >= Index)
break;
}
// 与最后的数值交换
a[Pointer + DataLength] = Temp;
DataLength = DataLength / 2; // 计算下次分割的间隔长度
}
return a;
}
3.冒泡排序
冒泡排序的过程很简单,首先将第一个数和第二个数进行比较,如果是逆序的,就将两个数交换,然后比较第二个数和第三个数,依次类推,直到第n-1个数和第n个数据进行比较完为止,然后进行延续二趟排序,对前n-1个数进行同类操作,直到没有数可比;
假设序列为: 49 38 65 97 76 13 27 49
第一趟排序:38 49 65 76 13 27 49 97
第二趟排序:38 49 65 13 27 49 76
第三趟排序:38 49 13 27 49 65
第四趟排序:38 13 27 49 49
第五趟排序:13 27 38 49
第六趟排序:13 27 38
从上面的排序过程可以看到:大的数一个个往下沉,小的数的就向上浮,跟这个排序方法的名字很贴切。
Java代码实现:
int sort (int[] a){
for(int j=1;j<=a.length;j++){
for(int i=0;i<a.length-j;i++){
if(a[i]>a[i+1]){
int temp;
temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
return a;
}
为了提高效率,可以设置一个是否继续比较下去的标志,因为如果数列的排列顺序就是所要求的顺序,那就没必要一个个往下比了,比如:当一趟排序中没有发生数据交换,就可以提前终止。
4.快速排序
快速排序是对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列;
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量I、J,排序开始的时候:I=0,J=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];
3)从J开始向前搜索,即由后开始向前搜索(J=J-1),找到第一个小于key的值A[J],并与A[I]交换;
4)从I开始向后搜索,即由前开始向后搜索(I=I+1),找到第一个大于key的A[I],与A[J]交换;
5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后另循环结束)
过程可如下图表示:
上面是完成一趟排序的过程,整个排序过程可描述如下:
初始状态 (49 38 65 97 76 13 27 49)
一次划分之后 ( 27 38 13 ) 49 (76 97 65 49)
分别进行快速排序 (13)27 (38)
结束 结束 ( 49 65 ) 76 ( 97 )
49 ( 65 ) 76 结束
结束
有序序列 ( 13 27 38 49 49 65 76 97 )
Java代码实现:
int[] sort(int[] data, int low, int high) {
// 枢纽元,一般以第一个元素为基准进行划分
int i = low;
int j = high;
if (low < high) {
// 从数组两端交替地向中间扫描
int pivotKey = data[low];
// 进行扫描的指针i,j;i从左边开始,j从右边开始
while (i < j) {
while (i < j && a[j]>=pivotkey) {
j--;
}// end while
if (i < j) {
// 比枢纽元素小的移动到左边
data[i] = data[j];
i++;
}// end if
while (i < j && a[i]<=pivotkey) {
i++;
}// end while
if (i < j) {
// 比枢纽元素大的移动到右边
data[j] = data[i];
j--;
}// end if
}// end while
// 枢纽元素移动到正确位置
data[i] = pivotKey;
// 前半个子表递归排序
sort(data, low, i - 1);
// 后半个子表递归排序
sort(data, i + 1, high);
}// end if
return a;
}// end sort
5.简单选择排序
其基本思想是:在每一趟排序中将n-i+1(i=1,2,…,n-1)个记录中选取最小的记录和有序序列中第i个记录交换,个人觉得这种排序方法跟冒泡排序很像,比如从小到大排序,冒泡是将大的数一个一个往下沉,而简单选择排序是将小的数一个个往上冒;
Java代码实现:
int[] sort(int[] data) {
int len = data.length;
for (int i = 0; i < len; i++) {
int position = i;
for (int j = i + 1; j < len; j++) {
if (data[position]>data[j])) {
position = j;
}
}
Int temp = data[i];
data[i] = data[position];
data[position] = temp;
}
}