不管是学习哪一门编程语言,算法都是必须要掌握的知识点。算法有很多种,这里我只对排序算法做一个简单的总结。
排序大体上分为两种,一种是只使用内存的内部排序(内排序),另外一种是内,外存结合使用的外部排序(外排序)。
这里只介绍内排序。而内排序又分为,插入排序,选择排序,交换排序,归并排序,基数排序。
(一):插入排序
(1)直接插入排序
基本思想:将要排序的数组,分成两部分,一部分是已经排好序的,另外一部分是未排序的。循环取出未排序区域中的数据,按照大小顺序,在已排好序的数据区域中找到存放下标。直到所有未排序中的数据取完为止。
JAVA实现:
public void sort(List datas) {
int length = datas.size();
if(length<1)return;
for(intindex=1;index<length;index++) {
int startIndex = index-1;
Ttemp= (T)datas.get(index);
while(startIndex>=0 &&datas.get(startIndex).toString().compareTo(temp.toString())>0) {
datas.set(startIndex+1,datas.get(startIndex));
startIndex--;
}
datas.set(startIndex+1,temp);
}
}
(2)二分直接插入排序
基本思想:在直接插入排序的基础上,对已排序区域进行二分查找,提高查找速率。
JAVA实现:
public voidbinSort(List datas){
int length = datas.size();
if(length<1)return;
for(intindex = 1;index<length;index++) {
int startIndex = index-1;
Ttemp= (T)datas.get(index);
int binStartIndex = 0;
//对已经排序的区域进行二分查找
while(binStartIndex<=startIndex) {
intbinMidIndex = (startIndex+binStartIndex)/2;
if(datas.get(binMidIndex).toString().compareTo(temp.toString())<0) {
binStartIndex =binMidIndex+1;
}else {
startIndex = binMidIndex-1;
}
}
for(intbinIndex=index-1;binIndex>=startIndex+1;binIndex--) {
datas.set(binIndex+1,datas.get(binIndex));
}
datas.set(startIndex+1,temp);
}
}
(3)希尔排序
基本思想:
希尔排序是针对直接插入排序做的改良。对于直接插入排序算法,待排序数组长度越小有序区域越多,排序速率越高。所以希尔排序,将待排序数组,按照一定的间隔gap,将数组分成多组。每组内的数据排序采用直接插入排序算法。排序完成后,减小gap的值,再对每个分组进行直接插入排序,直到gap的值为1.
JAVA实现:
public voidsort(List datas){
int length = datas.size();
if(length<1)return;
int gap = length/2;
while(gap>0) {
for(intindex =gap;index<length;index++) {
intstartIndex =index-gap;
Ttemp= (T)datas.get(index);
while(startIndex>=0 &&datas.get(startIndex).toString().compareTo(temp.toString())>0) {
datas.set(startIndex+gap,datas.get(startIndex));
startIndex = startIndex-gap;
}
datas.set(startIndex+gap,temp);
}
gap=gap/2;
}
}
(二):选择排序
(1)简单选择排序
基本思想:对给定的排序数组(n),进行n次挑选数据的过程,每次都挑出最小的数据并与当前挑选次数下标值交换。
JAVA实现:
public voidsort(List datas){
int length = datas.size();
for(intcount=0;count<length;count++) {
Ttemp= (T)datas.get(count);
TswitchValue= temp;
int position = count;
for(intindex=count+1;index<length;index++) {
if(datas.get(index).toString().compareTo(switchValue.toString())<0) {
position = index;
switchValue = (T)datas.get(index);
}
}
datas.set(count,datas.get(position));
datas.set(position,temp);
}
}
(2)堆排序
基本思想:堆的定义如下:具有n个元素的序列(h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或
(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的
定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观
地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一
棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然
后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类
推,直到只有两个节点的堆,并对它们作交换,最后得到有 n个节点的有序序列。从算法
描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所
以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
JAVA实现:
public voidsort(List datas){
int length = datas.size();
if(length<1)return;
for(intcount=0;count<length-1;count++) {
buildMaxHeap(datas, length-1-count);
swap(datas,0,length-1-count);
}
}
private void swap(List datas,intindex1,intindex2) {
Ttemp= (T)datas.get(index1);
datas.set(index1,datas.get(index2));
datas.set(index2,temp);
}
private void buildMaxHeap(Listdatas,intlastIndex) {
for(intparentNode=(lastIndex-1)/2;parentNode>=0;parentNode--) {
int currentNode = parentNode;
while(2*currentNode+1<=lastIndex) {
intleftChildNode = 2*currentNode+1;
if(leftChildNode<lastIndex) {
//存在右节点
if(datas.get(leftChildNode).toString().compareTo(datas.get(leftChildNode+1).toString())<0){
leftChildNode++;
}
}
if(datas.get(currentNode).toString().compareTo(datas.get(leftChildNode).toString())<0){
swap(datas,currentNode,leftChildNode);
currentNode = leftChildNode;
}else {
break;
}
}
}
}
(三):交换排序
(1)冒泡排序
基本思想:通过无序区中相邻记录关键字间的比较和位置的交换,使关键字最小的记录如气泡一般逐渐往上‘漂浮’直至‘水面’。整个算法是从最下面的记录开始,对每两个相邻的关键字进行比较,使关键字小的交换到较大记录之上,经过一趟冒泡排序后,关键字最小的记录到达最上端。
JAVA实现:
private void sort(List datas) {
int length = datas.size();
if(length<=1) {
return;
}
for(int count=0;count<length-1;count++) {
for(int index=length-1;index>count;index--) {
if(datas.get(index).toString().compareTo(datas.get(index-1).toString())<0) {
T temp = (T)datas.get(index);
datas.set(index,datas.get(index-1));
datas.set(index-1,temp);
}}
}
}
(2)改进版冒泡排序
基本思想:在常规冒泡排序算法基础上,加一个判断标志用来判断是否该次冒泡排序是否有交换的记录,如果没有,说明已经排好序,直接跳出。
JAVA实现:
private void sort2(List datas) {
int length = datas.size();
if(length<=1) {
return ;
}
for(int count=0;count<length-1;count++) {
boolean change = true;
for(int index=length-1;index>count;index--) {
if(datas.get(index).toString().compareTo(datas.get(index-1).toString())<0) {
T temp = (T)datas.get(index);
datas.set(index,datas.get(index-1));
datas.set(index-1,temp);
change = false;
}
}
if(change)
break;
}
}
(3)快速排序
基本思想:选择一个基准值,扫描待排序区域,把基准值小的值,放在基准值的左边,将大于基准值的值,放在基准值的右边。然后递归分别排序左右两个数据区。
JAVA实现:
private void run(List datas,int start,int end) {
if(start<end) {
int left = start,right = end;
T temp = (T)datas.get(left);
while(left!=right) {
while(right>left &&datas.get(right).toString().compareTo(temp.toString())>0)
right--;
datas.set(left,datas.get(right));
while(left<right && datas.get(left).toString().compareTo(temp.toString())<0)
left++;
datas.set(right, datas.get(left));
}
datas.set(left, temp);
run(datas,start,left-1);
run(datas,left+1,end);
}
}
private void sort(List datas) {
int length = datas.size();
if(length<=1)
return;
int start = 0,end = length-1;
run(datas,start,end);
}
(四):归并排序
基本思想:将两个或两个以上的有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后把有序子序列合并为整体有序序列。
JAVA实现:
private void merge(List datas,int left,int center,int right) {
List temp = new ArrayList();
int startIndex = left;
int mid = center+1;
while(left<=center && mid<=right) {
if(datas.get(left).toString().compareTo(datas.get(mid).toString())<0) {
temp.add(datas.get(left));
left++;
}else {
temp.add(datas.get(mid));
mid++;
}
}
while(mid<=right) {
temp.add(datas.get(mid));
mid++;
}
while(left<=center) {
temp.add(datas.get(left));
left++;
}
int index=0;
for(;startIndex<=right;startIndex++) {
datas.set(startIndex,temp.get(index++));
}
}
private void mergeSort(List datas,int left,int right) {
if(left<right) {
int center = (left+right)/2;
mergeSort(datas,left,center);
mergeSort(datas,center+1,right);
merge(datas,left,center,right);
}
}
private void sort(List datas) {
int length = datas.size();
if(length<=1)
return;
mergeSort(datas,0,length-1);
}
(五):基数排序
基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面
补零。然后,从最低位开始,依次进行一次排序。基数排序是通过‘分配’和‘收集’过程来实现排序的,不需要进行关键字间的比较,是一种借助于多关键字排序的思想对单关键字排序的方法。
JAVA实现:
private void sort(int[] datas) {
int length = datas.length;
if(length<=1) return;
List mainArray = new ArrayList();
//建立10个队列
for(int count=0;count<10;count++) {
List array = new ArrayList();
mainArray.add(array);
}
int max = datas[0];
for(int index=1;index<length;index++) {
if(datas[index]>max) {
max = datas[index];
}
}
//最大数是几位
int time = 0;
while(max>0) {
max/=10;
time++;
}
for(int bit=0;bit<time;bit++) {
//分配
for(int data=0;data<datas.length;data++) {
//求出该趟位数
int radix = datas[data]%(int)Math.pow(10,bit+1)/(int)Math.pow(10,bit);
List radixArray = (List) mainArray.get(radix);
radixArray.add(datas[data]);
}
//收集数据
int record = 0;
for(int index=0;index<10;index++) {
while(null!=mainArray.get(index) && ((List)mainArray.get(index)).size()>0) {
List array = (List)mainArray.get(index);
datas[record]=Integer.parseInt((array.get(0).toString()));
array.remove(0);
record++;
}
}
}
}
各种排序算法性能对比:
排序方法 | 时间复杂度 | 稳定性 |
直接插入排序 | O(n^2) | 稳定 |
希尔排序 | O(n^1.3) | 不稳定 |
冒泡排序 | O(n^2) | 稳定 |
快速排序 | O(nlog2n) | 不稳定 |
直接选择排序 | O(n^2) | 不稳定 |
堆排序 | O(nlog2n) | 不稳定 |
归并排序 | O(nlog2n) | 稳定 |
基数排序 | O(d(n+r)) | 稳定 |