排序算法(十):八大排序算法总结

一、八大排序算法总结

八大排序算法的稳定性及复杂度总结如下:



二、选择排序算法准则

每种排序算法都各有优缺点。因此,在实用时需根据不同情况适当选用,甚至可以将多种方法结合起来使用。

影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点:

1.待排序的记录数目n的大小;

2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小;

3.关键字的结构及其分布情况;

4.对排序稳定性的要求。

 

设待排序元素的个数为n。

1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序

快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

堆排序:如果内存空间允许且要求稳定性的,

归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。

 

2)当n较大,内存空间允许,且要求稳定性,推荐归并排序

 

3)当n较小,可采用直接插入或直接选择排序。

直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。

直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序

 

4)一般不使用或不直接使用传统的冒泡排序。

 

5)基数排序

它是一种稳定的排序算法,但有一定的局限性:

  1、关键字可分解。

  2、记录的关键字位数较少,如果密集更好

  3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。

三、java实现

1、简单排序:冒泡排序  、选择排序  、插入排序  
package com.xin.sort;

public class Sort {
	private int [] array;  
	   
	public Sort(int [] array){  
		this.array = array;  
	}  
	//按顺序打印数组中的元素  
	public void display(){  
		for(int i=0;i<array.length;i++){  
			System.out.print(array[i]+"\t");  
		}  
		System.out.println();  
	}
	
	//冒泡排序  
	public void bubbleSort(){  
		int temp;
		int len = array.length;
		for(int i=0;i<len-1;i++) {	 //外层循环:每循环一次就确定了一个相对最大元素 
			for(int j=1;j<len-i;j++) {	 //内层循环:有i个元素已经排好,根据i确定本次的比较次数  
				if(array[j-1] >array[j]) {	//如果前一位大于后一位,交换位置 
					temp = array[j-1];
					array[j-1] = array[j];
					array[j] = temp;
 				}
			}
			System.out.print("第"+(i+1)+"轮排序结果:");  
	        display();  
		}
	}
	
	//冒泡排序改进1  
	public void bubbleSort_improvement_1(){  
		int temp;  
		int len = array.length;  
	  
		for(int i=0;i<len-1;i++){   
			boolean exchange = false;  //设置交换变量  
			for(int j=1;j<len-i;j++){   
				if(array[j-1]>array[j]){  //如果前一位大于后一位,交换位置  
					temp = array[j-1];  
					array[j-1] = array[j];  
					array[j] = temp;  
					if(!exchange) exchange =true;  //发生了交换操作  
				}  
			}  
			System.out.print("第"+(i+1)+"轮排序结果:");  
			display();  
			if(!exchange) break;  //如果上一轮没有发生交换数据,证明已经是有序的了,结束排序  
	     }  
	}
	
	//冒泡排序改进2  
	public void bubbleSort_improvement_2(){  
		int temp;  
		int counter = 1;  
		int endPoint = array.length-1;  //endPoint代表最后一个需要比较的元素下标  
		while(endPoint>0){   
			int pos = 1;  
			for(int j=1;j<=endPoint;j++){    
				if(array[j-1]>array[j]){  //如果前一位大于后一位,交换位置  
					temp= array[j-1];  
					array[j-1]= array[j];  
					array[j]= temp;  
                      
					pos= j;  //下标为j的元素与下标为j-1的元素发生了数据交换  
				}  
			}  
			endPoint= pos-1;  //下一轮排序时只对下标小于pos的元素排序,下标大于等于pos的元素已经排好  
			System.out.print("第"+counter+"轮排序结果:"); 
			counter++;
			display();  
       }  
	} 
	
	//冒泡排序改进3  
	public void bubbleSort_improvement_3(){  
		int temp;  
		int low = 0;  
		int high = array.length-1;  
		int counter = 1;  
		while(low<high){   
			for(int i=low;i<high;++i){   //正向冒泡,确定最大值  
				if(array[i]>array[i+1]){  //如果前一位大于后一位,交换位置  
					temp= array[i];  
					array[i]= array[i+1];  
					array[i+1]= temp;  
				}  
			}  
			--high;  
           
			for(int j=high;j>low;--j){   //反向冒泡,确定最小值  
				if(array[j]<array[j-1]){  //如果前一位大于后一位,交换位置  
					temp= array[j];  
					array[j]= array[j-1];  
					array[j-1]= temp;  
				}  
			}  
         	++low;  
           
         	System.out.print("第"+counter+"轮排序结果:");  
         	display();  
         	counter++;  
		}  
	}  
	
	//选择排序  
   public void selectionSort(){  
       int minPoint;  //存储最小元素的小标  
       int len = array.length;  
       int temp;  
       int counter = 1;  
        
       for(int i=0;i<len-1;i++){  
           
          minPoint= i;   
          for(int j=i+1;j<=len-1;j++){  //每完成一轮排序,就确定了一个相对最小元素,下一轮排序只对后面的元素排序  
              if(array[j]<array[minPoint]){  //如果待排数组中的某个元素比当前元素小,minPoint指向该元素的下标  
                 minPoint= j;  
              }  
          }  
           
          if(minPoint!=i){  //如果发现了更小的元素,交换位置  
              temp= array[i];  
              array[i]= array[minPoint];  
              array[minPoint]= temp;  
          }  
           
          System.out.print("第"+counter+"轮排序结果:");  
          display();  
          counter++;  
       }  
   } 
   //选择排序改进版  
   public void selectionSort_improvement(){  
       int minPoint;  //存储最小元素的小标  
       int maxPoint;  //存储最大元素的小标  
       int len = array.length;  
       int temp;  
       int counter = 1;  
        
       for(int i=0;i<len/2;i++){  
           
          minPoint= i;  
          maxPoint= i;  
          for(int j=i+1;j<=len-1-i;j++){  //每完成一轮排序,就确定了两个最值,下一轮排序时比较范围减少两个元素  
              if(array[j]<array[minPoint]){  //如果待排数组中的某个元素比当前元素小,minPoint指向该元素的下标  
                 minPoint= j;  
                 continue;  
              }else if(array[j]>array[maxPoint]){  //如果待排数组中的某个元素比当前元素大,maxPoint指向该元素的下标  
                 maxPoint= j;  
              }  
          }  
           
          if(minPoint!=i){  //如果发现了更小的元素,与第一个元素交换位置  
              temp= array[i];  
              array[i]= array[minPoint];  
              array[minPoint]= temp;  
               
              //原来的第一个元素已经与下标为minPoint的元素交换了位置  
              //如果之前maxPoint指向的是第一个元素,那么需要将maxPoint重新指向array[minPoint]  
              //因为现在array[minPoint]存放的才是之前第一个元素中的数据  
              if(maxPoint== i){  
                 maxPoint= minPoint;  
              }  
               
          }  
   
          if(maxPoint!=len-1-i){  //如果发现了更大的元素,与最后一个元素交换位置  
              temp= array[len-1-i];  
              array[len-1-i]= array[maxPoint];  
              array[maxPoint]= temp;  
          }  
           
          System.out.print("第"+counter+"轮排序结果:");  
          display();  
          counter++;  
       }  
   } 
   //插入排序  
   public void insertionSort(){  
           
          int len = array.length;  
          int counter = 1;  
           
          for(int i=1;i<len;i++){  
              
             int temp = array[i];  //存储待排序的元素值  
             int insertPoint = i-1;  //与待排序元素值作比较的元素的下标  
              
             while(insertPoint>=0 && array[insertPoint]>temp){ //当前元素比待排序元素大  
                 array[insertPoint+1]= array[insertPoint];  //当前元素后移一位  
                 insertPoint--;  
             }  
             array[insertPoint+1]= temp;  //找到了插入位置,插入待排序元素  
              
             System.out.print("第"+counter+"轮排序结果:");  
             display();  
             counter++;  
          }  
      }  
   //二分插入排序  
   public void BinaryInsertionSort(){  
        
       int len = array.length;  
       int counter = 1;  
        
       for(int i=1;i<len;i++){  
           
          int temp = array[i];  //存储待排序的元素值  
           
          if(array[i-1]>temp){  //比有序数组的最后一个元素要小  
               
              int insertIndex = binarySearch(0, i-1, temp); //获取应插入位置的下标  
              for(int j=i;j>insertIndex;j--){  //将有序数组中,插入点之后的元素后移一位  
                 array[j]= array[j-1];  
              }  
               
              array[insertIndex]= temp;  //插入待排序元素到正确的位置  
          }  
           
          System.out.print("第"+counter+"轮排序结果:");  
          display();  
          counter++;  
       }  
   }  
    
   /** 
    * 二分查找法 
    * @param lowerBound 查找段的最小下标 
    * @param upperBound 查找段的最大下标 
    * @param target 目标元素 
    * @return 目标元素应该插入位置的下标 
    */  
   public int binarySearch(int lowerBound,int upperBound,int target){  
       int curIndex;   
       while(lowerBound<upperBound){  
          curIndex= (lowerBound+upperBound)/2;  
          if(array[curIndex]>target){  
              upperBound= curIndex - 1;  
          }else{  
              lowerBound= curIndex + 1;  
          }  
       }  
       return lowerBound;  
   }  
   //2-路插入排序  
   public void two_wayInsertionSort(){  
        
       int len = array.length;  
       int [] newArray = new int [len];   
       newArray[0]= array[0];  //将原数组的第一个元素作为枢纽元素  
       int first = 0;  //指向最小元素的指针  
       int last = 0;   //指向最大元素的指针  
        
       for(int j=0;j<newArray.length;j++){  //打印初始化数组  
          System.out.print(newArray[j]+"\t");  
       }  
       System.out.println();  
        
       for(int i=1;i<len;i++){  
           
          if(array[i]>= newArray[last]){  //大于等于最大元素,直接插入到last后面,不用移动元素  
              last++;  
              newArray[last]= array[i];  
          }else if(array[i] < newArray[first]){  //小于最小元素,直接插到first前面,不用移动元素  
              first= (first-1+len) % len;  
              newArray[first]= array[i];  
          }else if(array[i] >= newArray[0]){  //在最大值与最小值之间,且大于等于枢纽元素,插入到last之前,需要移动元素  
              int curIndex = last;  
              last++;  
              do{  //比array[i]大的元素后移一位  
                 newArray[curIndex+1]= newArray[curIndex];  
                 curIndex--;  
              }while(newArray[curIndex]>array[i]);  
               
              newArray[curIndex+1]= array[i];  //插入到正确的位置  
          }else{  //在最大值与最小值之间,且小于枢纽元素,插入到first之后,需要移动元素  
              int curIndex = first;  
              first= (first-1+len) % len;  
              do{  //比array[i]小的元素前移一位  
                 newArray[curIndex-1]= newArray[curIndex];  
                 curIndex= (curIndex+1+len)%len;  
              }while(newArray[curIndex]<=array[i]);  
               
              newArray[(curIndex-1+len)%len]= array[i];  //插入到正确的位置  
          }  
           
          for(int j=0;j<newArray.length;j++){  //打印新数组中的元素  
              System.out.print(newArray[j]+"\t");  
          }  
          System.out.println();  
           
       }  
   }  
}

2、 高级排序:归并排序  、快速排序  、希尔排序  、堆排序  、基数排序
package com.xin.sort;

import java.util.ArrayList;
import java.util.List;

/**
 * 高级排序
 * @author Administrator
 *
 */
public class HighSort {
	private int [] array;  //待排序的数组  
    
	public HighSort(int [] array){  
       this.array= array;  
	}  
   //按顺序打印数组中的元素  
   public void display(){  
       for(int i=0;i<array.length;i++){  
          System.out.print(array[i]+"\t");  
       }  
       System.out.println();  
   } 
   
   //归并排序  
   public void mergeSort(){  
        
       int[] workSpace = new int [array.length]; //用于辅助排序的数组  
       recursiveMergeSort(workSpace,0,workSpace.length-1);  
   } 
   
   /** 
    * 递归的归并排序 
    * @param workSpace  辅助排序的数组 
    * @param lowerBound 欲归并数组段的最小下标 
    * @param upperBound 欲归并数组段的最大下标 
    */  
   private void recursiveMergeSort(int [] workSpace,int lowerBound,int upperBound){  
        
       if(lowerBound== upperBound){  //该段只有一个元素,不用排序  
          return;  
       }else{  
          int mid = (lowerBound+upperBound)/2;  
          recursiveMergeSort(workSpace,lowerBound,mid);    //对低位段归并排序  
          recursiveMergeSort(workSpace,mid+1,upperBound);  //对高位段归并排序  
          merge(workSpace,lowerBound,mid,upperBound);  
          display();   
       }  
   }  
   
   /** 
    * 对数组array中的两段进行合并,lowerBound~mid为低位段,mid+1~upperBound为高位段 
    * @param workSpace 辅助归并的数组,容纳归并后的元素 
    * @param lowerBound 合并段的起始下标 
    * @param mid 合并段的中点下标 
    * @param upperBound 合并段的结束下标 
    */  
   private void merge(int [] workSpace,int lowerBound,int mid,int upperBound){  
        
       int lowBegin = lowerBound;  //低位段的起始下标  
       int lowEnd = mid;           //低位段的结束下标  
       int highBegin = mid+1;  //高位段的起始下标  
       int highEnd = upperBound;  //高位段的结束下标  
       int j = 0; //workSpace的下标指针  
       int n = upperBound-lowerBound+1;  //归并的元素总数  
        
       while(lowBegin<=lowEnd && highBegin<=highEnd){   
          if(array[lowBegin]<array[highBegin]){//将两者较小的那个放到workSpace中  
              workSpace[j++]= array[lowBegin++];   
          }else{  
              workSpace[j++]= array[highBegin++];   
          }  
       }   
        
       while(lowBegin<=lowEnd){   
          workSpace[j++]= array[lowBegin++];   
       }  
        
       while(highBegin<=highEnd){   
          workSpace[j++]= array[highBegin++];   
       }  
        
       for(j=0;j<n;j++){  //将归并好的元素复制到array中  
          array[lowerBound++]= workSpace[j];  
       }  
        
   }
   //快速排序  
   public void quikSort(){  
          recursiveQuikSort(0,array.length-1);  
   }  
    
   /** 
    * 递归的快速排序 
    *@param low  数组的最小下标 
    *@param high  数组的最大下标 
    */  
   private void recursiveQuikSort(int low,int high){  
          if(low>=high){  
                 return;  
          }else{  
                 int pivot = array[low];  //以第一个元素为基准  
                 int partition =partition(low,high,pivot);  //对数组进行划分,比pivot小的元素在低位段,比pivot大的元素在高位段  
                  
                 display();   
                  
                 recursiveQuikSort(low,partition-1);  //对划分后的低位段进行快速排序  
                 recursiveQuikSort(partition+1,high);  //对划分后的高位段进行快速排序  
          }  
   }  
    
   /** 
    * 以pivot为基准对下标low到high的数组进行划分 
    *@param low 数组段的最小下标 
    *@param high 数组段的最大下标 
    *@param pivot 划分的基准元素 
    *@return 划分完成后基准元素所在位置的下标 
    */  
   private int partition(int low,int high,int pivot){  
           
          while(low<high){  
                  
                 while(low<high &&array[high]>=pivot){  //从右端开始扫描,定位到第一个比pivot小的元素  
                        high--;  
                 }  
                 swap(low,high);  
                  
                 while(low<high &&array[low]<=pivot){  //从左端开始扫描,定位到第一个比pivot大的元素  
                        low++;  
                 }  
                 swap(low,high);  
                  
          }  
          return low;  
           
   }  
   /** 
    * 交换数组中两个元素的数据 
    *@param low 欲交换元素的低位下标 
    *@param high 欲交换元素的高位下标 
    */  
   private void swap(int low,int high){  
          int temp = array[high];  
          array[high] = array[low];  
          array[low] = temp;  
   }  
   
   //希尔排序  
   public void shellSort(){  
           
          int len = array.length;  
          int counter = 1;  
           
          int h = 1;  
          while(3*h+1<len){  //确定第一轮排序时的间隔  
                 h = 3*h+1;  
          }  
           
          while(h>0){  
                 for(int i=0;i<h;i++){   
                        shellInsertSort(i,h);  //对间隔为h的元素进行插入排序  
                 }  
                  
                 h = (h-1)/3;  //下一轮排序的间隔  
                  
                 System.out.print("第"+counter+"轮排序结果:");  
                 display();  
                 counter++;  
          }  
           
   }  
    
   /** 
    * 希尔排序内部使用的插入排序: 
    * 需要进行插入排序的元素为array[beginIndex]、array[beginIndex+increment]、array[beginIndex+2*increment]... 
    *@param beginIndex 起始下标 
    *@param increment 增量 
    */  
   private void shellInsertSort(int beginIndex,int increment){  
           
          int targetIndex =beginIndex+increment;  //欲插入元素的下标  
           
          while(targetIndex<array.length){  
                 int temp =array[targetIndex];  
                  
                 int previousIndex =targetIndex - increment;  //前一个元素下标,间隔为increment  
                 while(previousIndex>=0 && array[previousIndex]>temp){  
                        array[previousIndex+increment]= array[previousIndex];  //比欲插入数据项大的元素后移一位  
                        previousIndex =previousIndex - increment;  
                 }  
                 array[previousIndex+increment]= temp;  //插入到合适的位置  
                                       
                 targetIndex =targetIndex+increment;  //插入下一个元素  
          }  
           
   }  
 //堆排序  
   public void heapSort(){  
          buildHeap();  
          System.out.println("建堆:");  
          printTree(array.length);  
           
          int lastIndex = array.length-1;  
          while(lastIndex>0){  
                 swap(0,lastIndex);  //取出堆顶元素,将堆底放入堆顶。其实就是交换下标为0与lastIndex的数据  
                 if(--lastIndex == 0) break;  //只有一个元素时就不用调整堆了,排序结束  
                 adjustHeap(0,lastIndex);  //调整堆  
                  
                 System.out.println("调整堆:");  
                 printTree(lastIndex+1);  
          }  
           
   }  
    
   /** 
    * 用数组中的元素建堆 
    */  
   private void buildHeap(){  
          int lastIndex = array.length-1;  
          for(int i= (lastIndex-1)/2;i>=0;i--){ //(lastIndex-1)/2就是最后一个元素的根节点的下标,依次调整每棵子树  
                 adjustHeap(i,lastIndex);  //调整以下标i的元素为根的子树                    
          }  
   }  
    
   /** 
    * 调整以下标是rootIndex的元素为根的子树 
    *@param rootIndex 根的下标 
    *@param lastIndex 堆中最后一个元素的下标 
    */  
   private void adjustHeap(int rootIndex,int lastIndex){  
           
          int biggerIndex = rootIndex;   
          int leftChildIndex = 2*rootIndex+1;  
          int rightChildIndex = 2*rootIndex+2;  
           
          if(rightChildIndex<=lastIndex){  //存在右子节点,则必存在左子节点  
                  
                 if(array[rootIndex]<array[leftChildIndex] || array[rootIndex]<array[rightChildIndex]){ //子节点中存在比根更大的元素  
                  biggerIndex = array[leftChildIndex]<array[rightChildIndex] ? rightChildIndex :leftChildIndex;   
                 }  
                  
          }else if(leftChildIndex<=lastIndex){  //只存在左子节点  
                  
                 if(array[leftChildIndex]>array[rootIndex]){  //左子节点更大  
                        biggerIndex = leftChildIndex;  
                 }  
          }  
           
          if(biggerIndex != rootIndex){  //找到了比根更大的子节点  
                  
                 swap(rootIndex,biggerIndex);  
                  
                 //交换位置后可能会破坏子树,将焦点转向交换了位置的子节点,调整以它为根的子树  
                 adjustHeap(biggerIndex,lastIndex);  
          }  
   }  
    
   /** 
    * 将数组按照完全二叉树的形式打印出来 
    */  
   private void printTree(int len){  

          int layers = (int)Math.floor(Math.log((double)len)/Math.log((double)2))+1;  //树的层数  
          int maxWidth = (int)Math.pow(2,layers)-1;  //树的最大宽度  
          int endSpacing = maxWidth;  
          int spacing;  
          int numberOfThisLayer;  
          for(int i=1;i<=layers;i++){  //从第一层开始,逐层打印  
                 endSpacing = endSpacing/2;  //每层打印之前需要打印的空格数  
                 spacing = 2*endSpacing+1;  //元素之间应该打印的空格数  
                 numberOfThisLayer = (int)Math.pow(2, i-1);  //该层要打印的元素总数  
                  
                 int j;  
                 for(j=0;j<endSpacing;j++){  
                        System.out.print("  ");  
                 }  
                  
                 int beginIndex = (int)Math.pow(2,i-1)-1;  //该层第一个元素对应的数组下标  
                 for(j=1;j<=numberOfThisLayer;j++){  
                        System.out.print(array[beginIndex++]+"");  
                        for(int k=0;k<spacing;k++){  //打印元素之间的空格  
                               System.out.print("  ");  
                        }  
                        if(beginIndex == len){  //已打印到最后一个元素  
                               break;  
                        }  
                 }  
                  
                 System.out.println();  
          }  
          System.out.println();   
   }
   
   //基数排序
   public void radixSort(){  
                 
        int max = array[0];  
        for(int i=0;i<array.length;i++){  //找到数组中的最大值  
               if(array[i]>max){  
                      max = array[i];  
               }  
        }  
         
        int keysNum = 0;  //关键字的个数,我们使用个位、十位、百位...当做关键字,所以关键字的个数就是最大值的位数  
        while(max>0){  
               max /= 10;  
               keysNum++;  
        }  
                       
        List<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();  
        for(int i=0;i<10;i++){  //每位可能的数字为0~9,所以设置10个桶  
               buckets.add(new ArrayList<Integer>());  //桶由ArrayList<Integer>构成  
        }  
         
        for(int i=0;i<keysNum;i++){  //由最次关键字开始,依次按照关键字进行分配  
                
               for(int j=0;j<array.length;j++){  //扫描所有数组元素,将元素分配到对应的桶中  
                      //取出该元素对应第i+1位上的数字,比如258,现在要取出十位上的数字,258%100=58,58/10=5  
                      int key =array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);  
                      buckets.get(key).add(array[j]);  //将该元素放入关键字为key的桶中  
               }  
                
               //分配完之后,将桶中的元素依次复制回数组  
               int counter = 0;  //元素计数器  
               for(int j=0;j<10;j++){  
                       ArrayList<Integer> bucket =buckets.get(j);  //关键字为j的桶  
                       while(bucket.size()>0){  
                              array[counter++] = bucket.remove(0);  //将桶中的第一个元素复制到数组,并移除  
                       }  
               }  
               System.out.print("第"+(i+1)+"轮排序:");  
               display();  
        }  
         
   }
}

猜你喜欢

转载自blog.csdn.net/u014231646/article/details/80267494