排序算法

1.1排序算法概述

排序算法分类:内部排序和外部排序。
内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排 序操作都在内存中完成。
外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排 序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最 常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
十大内部排序算法
选择排序
直接选择排序、堆排序
交换排序
冒泡排序、快速排序
插入排序
直接插入排序、折半插入排序、Shell排序
归并排序
桶式排序
基数排序
它们之间的关系:在这里插入图片描述

1.2算法的5大特征

在这里插入图片描述
说明:满足确定性的算法也称为:确定性算法。现在人们也关注更广泛的概念,例如 考虑各种非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终 止的计算描述,这种描述有时被称为过程(procedure)。
1.3 冒泡排序
介绍: 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元 素,如果他们的顺序错误就把他们交换过来。

//冒泡排序
  /*
	*  排序思想: 
         *比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。 
        *对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步 做完后,最后的元素会是最大的数。 
         *针对所有的元素重复以上的步骤,除了最后一个。 
         *持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要 比较为止。
	 */
  public static void bubblesort(int [] arr) {
	  //外层循环控制n的次数
	  for (int i = arr.length - 1; i > 0; i--) {
		//内层循环控制当前所在位置
		     for (int j = 0; j < i; j++) {  
		       if (arr[j] > arr[j + 1]) {
		    	 swap(arr, j,j+1);
		       }
		  }
	  }
  }

1.4 快速排序

介绍: 快速排序通常明显比同为O(nlogn)的其他算法更快,因此常被采用,而且快 排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。可 见掌握快排的重要性。
快速排序(Quick Sort)由图灵奖获得者Tony Hoare发明,被列为20世纪十 大算法之一,是迄今为止所有内排序算法中速度最快的一种。冒泡排序的升 级版,交换排序的一种。快速排序的时间复杂度为O(nlog(n))。

//快速排序
	 /*排序思想:
	  *  从数列中挑出一个元素,称为"基准"(pivot), 
             *  重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准 值大的摆在基准的后面(相同的数可以到任一边)。
             *  在这个分区结束之后, 该基准就处于数列的中间位置。这个称为分区(partition)操作。 
             *递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数 列排序。 
            *递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好 了。
            *虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代 (iteration)中,它至少会把一个元素摆到它最后的位置去。
	 */
	 public static void QuickSort(int [] arr,int left,int right) {
		 if(left>right) {  //对于传入参数的判断,不能交叉越界
			 return;
		 }
		 int base = arr[left];  //基准元素
		 int l =left;
		 int r =right;
		 while(l!= r) {  //只知道循环条件,不知道循环具体情况
			 while(arr[r]>base&&l<r) {
				 r--;
			 }
			 //从左往右找
			 while(arr[r]<base&&l<r) {
				 l++;
			 }
			 //交换left与right位置
			 if(l<=r) {
		    	 swap(arr, l,r);
			 }
			 //base回归中间位置  第一次排序结束
			 arr[left]=arr[l];
			 arr[l]= base;
			 //递归,继续向基准的左右两边执行和上面同样的操作
			 QuickSort(arr, left, l-1);
			 QuickSort(arr, l+1, right);
		 }		 
	 } 

1.5 选择排序

选择排序是一种简单直观的排序算法,工作原理为:在未排序的序列中找出最小(大)元素与第一个位置的元素交换位置
注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。
然后在剩下的元素中再找最小(大)元素与第二个元素的位置交换,依此类推,直到所有元素排序排序完成。根据上述描述,一共进行n-1趟比较后,就能完成整个排队过程。我们可以知道,第k趟比较需要进行的数组元素的两两比较的次数为n-k次,所以共需要的比较次数为n*(n-1) / 2,因此选择排序算法的时间复杂度与冒泡排序一样,也为O(n^2)。

//选择排序
	 /*排序思想
	   * 初始状态:序列为无序状态。
              *第1次排序:从n个元素中找出最小(大)元素与第1个记录交换
              *第2次排序:从n-1个元素中找出最小(大)元素与第2个记录交换
              *第i次排序:从n-i+1个元素中找出最小(大)元素与第i个记录交换
               *以此类推直到排序完成
	  */
	 public static void SelectSort(int arr []) {
		//控制比较次数
		 for (int i = 0; i < arr.length; i++) { 
			//记录最小元素min所在位置,假设开始位置为最小元素
		     int min = i;  
		     //控制元素之间的比较
		     for (int j = i + 1; j < arr.length; j++) {
		       if (arr[j] < arr[min]) {
		         min = j;
		       }
		       else {break;}
		     }
		     // 进行位置的交换
		     if (min != i) {
		    	 swap(arr, i, min);
		     }
		  }
	 }

1.6 插入排序(InsertSort)

插入排序是一种简单直观的排序算法,工作原理为构建有序序列,对于未排序元素,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间,直到排序完成,如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。理解了插入排序的思想后,我们便能够得到它的时间复杂度。对于n个元素,一共需要进行n-1轮比较,而第k轮比较需要进行k次数组元素的两两比较,因此共需要进行的比较次数为:1 + 2 + … + (n-1),所以插入排序的时间复杂度同冒泡排序一样,也为O(n^2)。

//插入排序
 /* 排序思想:
    * 从第一个元素开始,该元素可认为已排序。
      *取出下一个元素,在排序好的元素序列中从后往前扫描
     *如果元素(已排序)大于新元素,将该元素移到下一位置
     *重复3.直到找到已排序的元素小于或等于新元素的位置
     *将新元素插入该位置后
     *重复2-5直到排序完成
 */
	public static void InsertSort(int [] arr) {
		   for (int i = 0; i < arr.length-1; i++) {
	            for (int j = i+1; j > 0; j--) {
	                if (arr[j] < arr[j - 1]) {
	                   swap(arr, j, j-1);
	                }else {
	                	break;
	                }
	            }
		   }
	 }

附上整体的程序:

import java.util.Scanner;
/**
 * 使用最多的几种内部排序算法
 * 冒泡排序
 *快速排序
 *选择排序
 *插入排序
 */
public class Sort {
  public static void main(String[] args) {
    int[] data = { 12,6,9,23,51,34,52};
    bubblesort(data);
    System.out.println("冒泡排序");
    bianli(data);
    System.out.println("---------------");
    QuickSort(data, 0, data.length-1);
    System.out.println("快速排序");
    bianli(data);
    System.out.println("---------------");
    SelectSort(data);
    System.out.println("选择排序");
    bianli(data);
    System.out.println("---------------");
    InsertSort(data);
    System.out.println("直接插入排序");
    bianli(data);


  };
  //冒泡排序
  /*
	*  排序思想: 
         *比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。 
        *对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步 做完后,最后的元素会是最大的数。 
         *针对所有的元素重复以上的步骤,除了最后一个。 
         *持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要 比较为止。
	 */
  public static void bubblesort(int [] arr) {
	  //外层循环控制n的次数
	  for (int i = arr.length - 1; i > 0; i--) {
		//内层循环控制当前所在位置
		     for (int j = 0; j < i; j++) {  
		       if (arr[j] > arr[j + 1]) {
		    	   swap(arr, j, j+1);
		       }
		  }
	  }
  }
  //遍历数组输出
	 public static void bianli(int [] arr) {
		 for (int i = 0; i < arr.length; i++) {
		     System.out.println(arr[i]);
		    }
		 }
 //交换数组中两个元素的位置
	 public static void swap(int [] arr,int a,int b) {
		 int temp = arr[a];
         arr[a] = arr[b];
         arr[b] = temp;
	 }
//快速排序
	 /*排序思想:
	  *  从数列中挑出一个元素,称为"基准"(pivot), 
             *  重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准 值大的摆在基准的后面(相同的数可以到任一边)。
             *  在这个分区结束之后, 该基准就处于数列的中间位置。这个称为分区(partition)操作。 
             *递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数 列排序。 
            *递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好 了。
            *虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代 (iteration)中,它至少会把一个元素摆到它最后的位置去。
	 */
	 public static void QuickSort(int [] arr,int left,int right) {
		 if(left>right) {  //对于传入参数的判断,不能交叉越界
			 return;
		 }
		 int base = arr[left];  //基准元素
		 int l =left;
		 int r =right;
		 while(l!= r) {  //只知道循环条件,不知道循环具体情况
			 while(arr[r]>base&&l<r) {
				 r--;
			 }
			 //从左往右找
			 while(arr[r]<base&&l<r) {
				 l++;
			 }
			 //交换left与right位置
			 if(l<=r) {
		     swap(arr, l, r);
			 }
			 //base回归中间位置  第一次排序结束
			 arr[left]=arr[l];
			 arr[l]= base;
			 //递归,继续向基准的左右两边执行和上面同样的操作
			 QuickSort(arr, left, l-1);
			 QuickSort(arr, l+1, right);
		 }		 
	 } 
 //选择排序
	 /*排序思想
	   * 初始状态:序列为无序状态。
              *第1次排序:从n个元素中找出最小(大)元素与第1个记录交换
              *第2次排序:从n-1个元素中找出最小(大)元素与第2个记录交换
              *第i次排序:从n-i+1个元素中找出最小(大)元素与第i个记录交换
               *以此类推直到排序完成
	  */
	 public static void SelectSort(int arr []) {
		//控制比较次数
		 for (int i = 0; i < arr.length; i++) { 
			//记录最小元素min所在位置,假设开始位置为最小元素
		     int min = i;  
		     //控制元素之间的比较
		     for (int j = i + 1; j < arr.length; j++) {
		       if (arr[j] < arr[min]) {
		         min = j;
		       }
		       else {break;}
		     }
		     // 进行位置的交换
		     if (min != i) {
		    	 swap(arr, i, min);
		     }
		  }
	 }
//插入排序
 /* 排序思想:
    * 从第一个元素开始,该元素可认为已排序。
      *取出下一个元素,在排序好的元素序列中从后往前扫描
     *如果元素(已排序)大于新元素,将该元素移到下一位置
     *重复3.直到找到已排序的元素小于或等于新元素的位置
     *将新元素插入该位置后
     *重复2-5直到排序完成
 */
	public static void InsertSort(int [] arr) {
		   for (int i = 0; i < arr.length-1; i++) {
	            for (int j = i+1; j > 0; j--) {
	                if (arr[j] < arr[j - 1]) {
	                   swap(arr, j, j-1);
	                }else {
	                	break;
	                }
	            }
		   }
	 }

}

输出结果:
在这里插入图片描述
在这里插入图片描述
最后,排序算法性能对比
在这里插入图片描述
1.从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归 并排序。
2.从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较 简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序 算法,其算法比较复杂,认为是复杂排序。
3.从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排 序、快速排序、 Shell排序和堆排序是不稳定排序
4.从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采 用改进排序。

1.8 排序算法的选择

(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排 序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或 归并排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值