排序分类: 1、插入排序:直接插入排序,二分法插入排序,希尔排序; 2、选择排序:简单选择排序,堆排序; 3、交换排序:冒泡排序,快速排序; 4、归并排序;
5、基数排序;
(1)直接插入排序:(稳定排序) 基本思想:将每个待排序的记录,按照其顺序码的大小插入到前面已经排序好的子序列的合适位置(从后向前找合适位置),直到全部插入排序完成为止。 时间复杂度:
平均时间复杂度为O(n^2)。
文件初态不同时,直接插入排序所耗费的时间有很大差异。若文件初态为正序,则每个待插入的记录只需要比较一次就能够找到合适的位置插入,故算法的时间复杂度为O(n),这时最好的情况。若初态为反序,则第i个待插入记录需要比较i+1次才能找到合适位置插入,故时间复杂度为O(n^2),这时最坏的情况。
/*
* 直接插入排序
*/
static void directInsertSort(int []array){
int i,j,temp;
for (i = 1; i < array.length; i++) {
//待插入元素
temp = array[i];
for (j = i-1; j>=0 && array[j]>temp; j--) {
//将大于temp的往后移动一位
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
(2)二分法插入排序(稳定排序) 基本思路:同直接插入排序,只是寻找插入位置的方法不同,使用二分法确定插入位置; 时间复杂度:
平均时间复杂度为O(n^2)。
/*
* 二分插入排序
*/
static void binaryInsertSort(int []array){
int left,right,mid,temp;
for(int i=0;i<array.length;i++)
{
temp=array[i];
left=0;
right=i-1;
mid=0;
while(left<=right){
mid=(left+right)/2;
if(temp<array[mid])
right=mid-1;
else {
left = mid+1;
}
}
for(int j=i-1;j>=left;j--){
array[j+1]=array[j];
}
array[left]=temp;
}
}
(3)希尔排序(不稳定排序)
基本思路:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
时间复杂度:平均时间复杂度为O(nlogn)
希尔排序的时间性能优于直接插入排序,原因如下:
(1)当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。 (2)当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。 (3)在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。 因此,希尔排序在效率上较直接插人排序有较大的改进。
/*
* 希尔排序
*/
static void xierInsertSort(int []array){
int interval=array.length;
while(true){
interval=interval/2;
for(int i=0;i<array.length;i++){
directInsertSort(array,i,interval);
}
if(interval==1)
break;
}
}
/*
* 直接插入排序
*/
static void directInsertSort(int []array,int start,int interval){
int i,j,temp;
for (i = start; i < array.length; i=i+interval) {
//待插入元素
temp = array[i];
for (j = i-interval; j>=0 && array[j]>temp; j=j-interval) {
//将大于temp的往后移动interval
array[j+interval] = array[j];
}
array[j+interval] = temp;
}
}
(4)直接选择排序(不稳定排序)
基本思路:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
时间复杂度:平均时间复杂度为O(n^2)。
/*
* 简单选择排序
*/
static void simpleSelectSort(int []array){
int i,j,temp,flag;
for (i = 0; i < array.length; i++) {
temp = array[i];
flag=i;
for (j = i+1; j<array.length; j++) {
if(array[j]<temp){
temp=array[j];
flag=j;
}
}
array[flag]=array[i];
array[i] = temp;
}
}
(5)堆排序(不稳定排序)
堆的定义下:具有n个元素的序列 (h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。
基本思路:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。
时间复杂度:平均时间复杂度为O(nlogn)
static void buildHeap(int [] array){
//循环建堆
for(int i=0;i<array.length-1;i++){
//建堆
buildMinHeap(array,array.length-1-i);
//交换堆顶和最后一个元素
swap(array,0,array.length-1-i);
System.out.println(Arrays.toString(array));
}
}
//对array数组从0到lastIndex建大根堆
static void buildMaxHeap(int[] array, int lastIndex){
//从lastIndex处节点(最后一个节点)的父节点开始
for(int i=(lastIndex-1)/2;i>=0;i--){
//k保存正在判断的节点
int k=i;
//如果当前k节点的子节点存在
while(k*2+1<=lastIndex){
//k节点的左子节点的索引
int biggerIndex=2*k+1;
//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
if(biggerIndex<lastIndex){
//若果右子节点的值较大
if(array[biggerIndex]<array[biggerIndex+1]){
//biggerIndex总是记录较大子节点的索引
biggerIndex++;
}
}
//如果k节点的值小于其较大的子节点的值
if(array[k]<array[biggerIndex]){
//交换他们
swap(array,k,biggerIndex);
//将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
k=biggerIndex;
}else{
break;
}
}
}
}
//对array数组从0到lastIndex建小根堆
static void buildMinHeap(int[] array, int lastIndex){
//从lastIndex处节点(最后一个节点)的父节点开始
for(int i=(lastIndex-1)/2;i>=0;i--){
//k保存正在判断的节点
int k=i;
//如果当前k节点的子节点存在
while(k*2+1<=lastIndex){
//k节点的左子节点的索引
int smallerIndex=2*k+1;
//如果smallerIndex小于lastIndex,即smallerIndex+1代表的k节点的右子节点存在
if(smallerIndex<lastIndex){
//若果右子节点的值较小
if(array[smallerIndex]>array[smallerIndex+1]){
//smallerIndex总是记录较小子节点的索引
smallerIndex++;
}
}
//如果k节点的值大于其较小的子节点的值
if(array[k]>array[smallerIndex]){
//交换他们
swap(array,k,smallerIndex);
//将smallerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值小于其左右子节点的值
k=smallerIndex;
}else{
break;
}
}
}
}
//交换
static void swap(int[] array, int i, int j) {
int temp=array[i];
array[i]=array[j];
array[j]=temp;
}
(6)冒泡排序(稳定排序)
基本思路:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
时间复杂度:平均时间复杂度为O(n^2)。
/*
* 冒泡排序
*/
static void bubSwapSort(int []array){
int i,j,temp;
Boolean flag=false;
for (i = 0; i < array.length; i++) {
flag=true;
for(j=0;j<array.length-i-1;j++){
if(array[j+1]<array[j])
{
temp=array[j+1];
array[j+1]=array[j];
array[j]=temp;
flag=false;
}
}
if(flag)
break;
}
}
(7)快速排序(不稳定排序)
基本思路:
选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
时间复杂度:平均时间复杂度为O(nlogn)
/*
* 快速排序
*/
static void quickSwapSort(int []array){
if(array.length>0)
quickSort(array,0,array.length-1);
}
static void quickSort(int []array,int start,int end){
if(start<end){
int mid=getMiddleIndex(array,start,end);
quickSort(array,0,mid-1);
quickSort(array,mid+1,end);
}
}
static int getMiddleIndex(int []array,int start,int end){
int temp=array[start];
while(start<end){
while(start<end&&array[end]>=temp)
end--;
array[start]=array[end];
while(start<end&&array[start]<=temp)
start++;
array[end]=array[start];
}
array[start]=temp;
return start;
}
(8)归并排序(稳定排序)
基本思路:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
平均时间复杂度为O(nlogn)
/*
* 归并排序
*/
static void mergeSort(int[] array, int left, int right) {
if(left<right){
int middle = (left+right)/2;
//对左边进行递归
mergeSort(array, left, middle);
//对右边进行递归
mergeSort(array, middle+1, right);
//合并
merge(array,left,middle,right);
}
}
static void merge(int[] array, int left, int middle, int right) {
int[] tempArray = new int[array.length];
int middleRight = middle+1; //右边的起始位置
int temp = left;
int third = left;
while(left<=middle && middleRight<=right){
//从两个数组中选取较小的数放入临时数组
if(array[left]<=array[middleRight]){
tempArray[third++] = array[left++];
}else{
tempArray[third++] = array[middleRight++];
}
}
//将剩余的部分放入临时数组
while(left<=middle){
tempArray[third++] = array[left++];
}
while(middleRight<=right){
tempArray[third++] = array[middleRight++];
}
//将临时数组的排序结果复制到原数组
while(temp<=right){
array[temp] = tempArray[temp++];
}
}
(9)基数排序(稳定排序)
基本思路:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。从最低位开始,依次进行排序,从最低位一直排序到最高位,数列就变成一个有序序列。
时间复杂度:O(d(n+r)),d为位数,r为基数
/*
* 基数排序
*/
static void radixSort(int[] array) {
//找到最大数,确定位数
int max = 0;
int times = 0;
for (int i = 0; i < array.length; i++) {
if(max<array[i]){
max = array[i];
}
}
while(max>0){
max = max/10;
times++;
}
//建立数字列表
List<ArrayList> list = new ArrayList<ArrayList>();
for (int i = 0; i < 10; i++) {
ArrayList<Integer> queue = new ArrayList<Integer>();
list.add(queue);
}
int bit_num=0;
int count = 0;
for (int i = 0; i < times; i++) {
for (int j = 0; j < array.length; j++) {
bit_num = array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
ArrayList<Integer> queue = list.get(bit_num);
queue.add(array[j]);
list.set(bit_num,queue);
}
//重新调整数组
count = 0;
for (int j = 0; j < 10; j++) {
while(list.get(j).size()>0){
ArrayList<Integer> queue = list.get(j);
array[count++] = queue.get(0);
queue.remove(0);
}
}
}
}
参考:
http://www.cnblogs.com/liuling/p/2013-7-24-01.html
http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html