排序算法汇总

         由于即将面临找工作的现实,将八大基本的排序算法进行汇总,为以后的找工作做准备:

八大排序算法主要包括:

1、插入排序(insertSort)

2、希尔排序(shellSort)

3、冒泡排序(BubbleSort)

4、快速排序(quickSort)

5、选择排序(selectSort)

6、堆排序(heapSort)

7、归并排序(mergeSort)

8、基数排序(oddSort)

一、稳定性:


    稳定:冒泡排序、插入排序、归并排序和基数排序


  不稳定:选择排序、快速排序、希尔排序、堆排序


二、平均时间复杂度


  O(n^2):直接插入排序,简单选择排序,冒泡排序。


  在数据规模较小时(9W内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。
        性能为O(n^2)的算法基本上是相邻元素进行比较,基本上都是稳定的。


  O(nlogn):快速排序,归并排序,希尔排序,堆排序。


  其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。


三、排序算法的选择


  1.数据规模较小


    (1)待排序列基本序的情况下,可以选择直接插入排序;


    (2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡


  2.数据规模不是很大


  (1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。


  (2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序


  3.数据规模很大


     (1)对稳定性有求,则可考虑归并排序。


      (2)对稳定性没要求,宜用堆排序


  4.序列初始基本有序(正序),宜用直接插入,冒泡

public class SortAlgorithm {

	public static void main(String[] args) {
	 int n[]={1,4,2,5,7,33,3,9,6,11,111};
	 int m=8;
	 //insertSort(n, m);
	 //shellSort(n, m);
	 //BubbleSort(n, m);
	 //quickSort(n, 0, 6);
	 //selectSort(n, m);
	 //heapSort(n, m);
	 //mergeSort(n, 0, 7);
	 oddSort(n);
	 for (int i : n) {
		System.out.println(i);
	}
	}
	
	
/**
1、插入排序:
	时间复杂度:
	最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为O(n)  
          最坏的情况下:逆序有序,这样每一个元素就需要比较n次,共有n个元素,因此实际复杂度为O(n­2)  
          平均情况下:O(n­2)
         稳定性:稳定
*/
	
	public static void insertSort(int[] n,int m){
		for(int i=1;i<m;i++){
			int j=i-1;
			int temp=n[i];
			while(j>=0){
				if(n[j]>temp){
					n[j+1]=n[j];
					j--;
					
				}else{
				n[j+1]=temp;
				break;
				}
			}
			
		}
	}
	
/**
 * 希尔排序:
 * 时间复杂度:
 * 最好情况:由于希尔排序的好坏和步长d的选择有很多关系,因此,目前还没有得出最好的步长如何选择(现在有些比较好的选择了,但不确定是否是最好的)。所以,不知道最好的情况下的算法时间复杂度。  
     最坏情况下:O(N*logN),最坏的情况下和平均情况下差不多。  
     平均情况下:O(N*logN)
  稳定性:不稳定
 */
	public static void shellSort(int[] n,int m){
		int d=m/2;//初始步长
		int j=0;
		while(d>0){
			for(int i=d;i<m;i++){
				int temp=n[i];
				for( j=i;j>=d;j=j-d){
					if(n[j-d]>temp){
						n[j]=n[j-d];
					}else{
						break;
					}
				}
				n[j]=temp;
			}
			d=d/2;
		}
	}
	
	/**
	 * 冒泡排序:
	 * 最好情况下:正序有序,则只需要比较n次。故,为O(n)  
                  最坏情况下: 逆序有序,则需要比较(n-1)+(n-2)+……+1,故,为O(N*N)
                 稳定性:稳定
	 * @return 
	 */
	public static void BubbleSort(int[]n,int m){
		int temp=0;
		for(int i=0;i<m-1;i++){
			for(int j=i+1;j<m;j++){
				if(n[j]<n[i]){
					temp=n[i];
					n[i]=n[j];
					n[j]=temp;
				}
			}
		}
	}
	/**
	 * 快速排序(思想):以a[0]为初识值定义两个指针i,j分别指向数组的第一个和最后一个元素,先从右往左执行j--找到第一个比初始值小的数a[p],
	 *              交换位置,此时j=p;再从左往右执行i++找到第一个比初始值大的数a[q],交换位置,此时i=q;重复执行上述步骤直到i=j.
	 * 时间复杂度:
	 * 最好的情况下:因为每次都将序列分为两个部分(一般二分都复杂度都和logN相关),故为 O(N*logN)  
                  最坏的情况下:基本有序时,退化为冒泡排序,几乎要比较N*N次,故为O(N*N)
                  稳定性:不稳定
	 * 
	 */
public static void quickSort(int[]n,int start,int end){
    	int flag=0;
    	if(start<end){
    		flag=partition(n, start,end);
    		quickSort(n, start, flag-1);
    		quickSort(n, flag+1, end);
    	}
    }
    public static int partition(int[]n,int start,int end){
    	int temp=n[start];
    	while(start<end){
    		while(start<end&&temp<=n[end]){
    			end--;
    		}
    		if(start<end){
    			n[start]=n[end];
    			start++;
    		}
    		while(start<end&&temp>=n[start]){
    			start++;
    		}
    		if(start<end){
    			n[end]=n[start];
    			end--;
    		}
    	}
    	//跳出循环时start=end
    	n[end]=temp;
    	return end;
    }
    
    
    /**
     *选择排序(思想):首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,
     *            直到所有元素均排序完毕。具体做法是:选择最小的元素与未排序部分的首部交换,使得序列的前面为有序。
     *时间复杂度:最好情况下:交换0次,但是每次都要找到最小的元素,因此大约必须遍历N*N次,因此为O(N*N)。减少了交换次数! 
                                      最坏情况下,平均情况下:O(N*N)
               稳定性:不稳定
     *
     */
    
    public static void selectSort(int[]n,int m){
    	int flag;
    	int temp;
    	for(int i=0;i<m-1;i++){//排序需要的趟数
    		flag=i;
    		for(int j=i+1;j<m;j++){
    			if(n[flag]>n[j]){
    				flag=j;//flag标记每趟排序中最小元素的位置坐标
    			}
    			if(flag!=i){
    				temp=n[i];
    				n[i]=n[flag];
    				n[flag]=temp;
    				break;
    			}
    		}
    		
    	}
    }
  
  
    /**
     * 堆排序:从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。
     *       一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
     * 时间复杂度:最坏情况下,接近于最差情况下:O(N*logN),因此它是一种效果不错的排序算法。
     * 稳定性:不稳定
     * 堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
     */
    //以大顶堆为例
    public static void heapSort(int[]n,int m){
    	//循环建堆
    	for(int i=0;i<m-1;i++){
    		buildMaxHeap(n, m-i-1);
    		//交换堆顶和最后一个元素
    		swap(n, 0, m-i-1);
    		System.out.println(Arrays.toString(n));
    	}
    }
    //对数组n中的元素从0到lastindex建立大顶堆
   public static void buildMaxHeap(int[]n,int lastindex){
	   //从lastindex节点的父节点开始
	   for(int i=lastindex/2;i>=0;i--){
		   //k保存正在判断的节点
		   int k=i;
		   //当前节点的子节点存在
		   while(2*k+1<=lastindex){
			   //k节点的左子节点的索引
			   int maxindex=2*k+1;
			   //如果k节点的右子节点存在
			   if(maxindex<lastindex){
				   //左子节点的值小于右子节点的值
				   if(n[maxindex]<n[maxindex+1]){
					   //保证maxindex指向子节点中的最大值
					   maxindex++;
				   }
			   }
			   //如果k节点的值小于子节点的值
			   if(n[k]<n[maxindex]){
				   //交换
				   swap(n, k, maxindex);
				   //将maxindex的值赋予k,执行下一次循环,保证k节点的值大于其子节点的值
				   k=maxindex;
			   }else{
				   break;
			   }
		   }
	   }
   } 
    //交换数组中的两个元素
    public static void swap(int[]n,int i,int j){
    	int temp;
    	temp=n[i];
    	n[i]=n[j];
    	n[j]=temp;
    }
    
    
    /**
     * 归并排序:基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,
     *               即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
     * 时间复杂度:最好的情况下:一趟归并需要n次,总共需要logN次,因此为O(N*logN) 
                                         最坏的情况下,接近于平均情况下,为O(N*logN) 
                                         说明:对长度为n的文件,需进行logN 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是
                                         在最好情况下还是在最坏情况下均是O(nlgn)。
                 稳定性:稳定
     */
    public static void mergeSort(int[]n,int left,int right){
    	if(left<right){
    		int middle=(right+left)/2;
    		//对左边进行递归
    		mergeSort(n, left, middle);
    		//对右边进行递归
    		mergeSort(n, middle+1, right);
    		//合并
    		merge(n, left, middle, right);
    	}
    }
    
    public static void merge(int[]n,int left,int middle,int right){
    	int[] temp=new int[n.length];
    	int mid=middle+1;//右边的起始位置
    	int p=left;
    	int k=left;
    	while(left<=middle&&right>=mid){
    		//从两个数组中选取较小的数放在temp数组中
    		if(n[left]<=n[mid]){
    			temp[k++]=n[left++];
    		}else{
    			temp[k++]=n[mid++];
    		}
    		
    	}
    	//将剩余部分放入temp数组中
    	while(left<=middle){
    		temp[k++]=n[left++];
    	}
    	
    	while(mid<=right){
    		temp[k++]=n[mid++];
    	}
    	//将temp数组中的数据复制回原数组
    	while(p<=right){
    		n[p]=temp[p++];
    	}
    }
    
    /**
     * 基数排序:它是一种非比较排序。它是根据位的高低进行排序的,也就是先按个位排序,然后依据十位排序……以此类推。
     * 时间复杂度:分配需要O(n),收集为O(r),其中r为分配后链表的个数,以r=10为例,则有0~9这样10个链表来将原来
     *          的序列分类。而d,也就是位数(如最大的数是1234,位数是4,则d=4),即"分配-收集"的趟数。因此时间复杂
     *          度为O(d*(n+r))。
     *稳定性:稳定
     */
    public static void oddSort(int[]n){
    	//找到最大数,确定要排序的趟数
    	int max=0;
    	for(int i=0;i<n.length;i++){
    		if(max<n[i]){
    			max=n[i];
    		}
    	}
    	//判断最大数的位数
    	int times=0;
    	while(max>0){
    		max=max/10;
    		times++;
    	}
    	//建立十个队列
    	List<ArrayList> queue=new ArrayList();
    	for(int i=0;i<10;i++){
    		ArrayList queue1=new ArrayList();
    		queue.add(queue1);
    	}
    	//进行times次分配和收集
    	for(int i=0;i<times;i++){
    		//分配
    		for(int j=0;j<n.length;j++){
    			int a= n[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
    			ArrayList queue2=queue.get(a);
    			queue2.add(n[j]);
    			queue.set(a, queue2);
    		}
    		//收集
    		int count=0;
    		for(int j=0;j<10;j++){
    			while(queue.get(j).size()>0){
    				ArrayList<Integer> queue3=queue.get(j);
    				n[count]=queue3.get(0);
    				queue3.remove(0);
    				count++;
    			}
    		}
    	}
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值