排序白银三兄弟——冒泡排序&选择排序&插入排序

       算法一定是建立在数学公理或定理上的,即使是简单的排序也是如此。既然以排序作为算法学习的第一课,那一定要从中分析出算法所遵循的原理,培养这样的习惯有助于我们在学习以后更复杂的算法时抓住算法的主干,这比了解它的实现更重要!!!

1、排序原理

  • 冒泡排序:冒泡排序基于排序算法引言中的命题1(有序数组其任何一个元素不小于其存在的左边元素 )设计的算法。如果数组中的每一个元素都满足定义,那么整个数组便满足有序了。
          假设存在数组长度为N,比较第一个和第二个元素,若第一个大与第二个,交换二者;否则保持不变,接着比较第二个和第三个,交换原则同上,这样循环往复直至最后一个,就将最大的元素“推”到了数组顶部,这样最后一个元素不小于前面N-1个所有元素,对最后一个元素满足有序的命题1。对前N-1个元素执行相同的将最大元素推至顶部的操作,直至前N-x=1,这样每一个元素都满足有序的命题1,排序完成。
  • 选择排序:选择排序是基于排序算法引言中的命题2(有序数组其任何一个元素不大于其右边的元素)设计的算法。如果数组中的每一个元素都满足命题1,那么整个数组便满足有序了。
          假设存在数组长度为N,首选寻找出N个元素中最小的,将该元素与第一个元素交换,那么对于第一个元素与N-1个元素组成的集合满足命题2;接着对N-1部分做重复的寻找最小并交换的操作,这样第一个、第二个、N-2个元素组成的集合满足命题2;循环如上操作,直至N-x=1,这样整个数组的每一个元素都满足了命题2,排序完成。
  • 插入排序:插入排序是基于排序算法引言中的命题3(有序数组其任何一个元素不小于其左边元素且不大于右边元素)。如果数组中的每一个元素都满足命题3,那么整个数组便满足有序了。
          假设有一个长度为N的数组,首先选取第一个元素与第二个元素比较排序,然后第三个元素插入前2个序列中合适的位置(这个位置让其不小于左边且不大于右边);接着取第四个元素插入前3个元素组成的序列,重复这一过程,直至最后一个元素插入到前N-1个元素的序列中。这样每一个元素都满足了命题3,该算法使数组有序。

2、Java实现

继承排序算法引言中的抽象类,重写sort方法。(排序本身是一个很简单的算法,没有必要使用抽象类,但是可以锻炼自己OOP的思想)

  • 冒泡排序java实现:
package sort;

public class PopSort extends AbstractSort{
	/*
	 * 冒泡算法
	 * 1、第一个for循环,初始化泡泡,
	 * 2、第二个for循环,泡泡上升堆到数组末尾。
	 */
	public PopSort() {
	}

	public static void sort(Comparable<?>[] a) {
		for (int i=0; i<a.length; i++) {
			/*
			 * 两种写法没本质区别,就是上限是j+1还是j
			 * j与j+1比较,j+1的情况循环最后是length-i-1
			 * j与j-1比较,j的情况循环最后是length-i
			 */
//			for (int j=1; j<a.length-i; j++) {
//				if(less(a[j], a[j-1])) {
//					exch(a, j-1, j);
//				}
//			}
			for (int j=0; j<a.length-i-1; j++) {
				if(less(a[j+1], a[j])) {
					exch(a, j+1, j);
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Integer[] a = {1,5,3,42,5,68,89,12,57,90};
		PopSort.sort(a);
		show(a);
		System.out.println("数组是否有序:"+isSort(a));
	}
}

冒泡排序效果:

1
3
5
5
12
42
57
68
89
90
数组是否有序:true
  • 选择排序java实现:
package sort;

public class SelectSort extends AbstractSort{
	/*       选择排序流程:
	 * 1、找到数组最小的元素
	 * 2、和数组第一个元素交换
	 * 3、找除第一个元素外的最小元素
	 * 4、与除第一个元素外的数组的第一个元素交换
	 * 		时间复杂度O(N^2)
	 */
	public SelectSort() {
	}
	
	public static void sort(Comparable<?>[] a){
		//按升序排序
		int min;
		Comparable<?> x = null;
		for(int j = 0; j<a.length-1; j++) {
			min = j;
			for(int i=j+1; i<a.length; i++) {
				if(SelectSort.less(a[i], a[min])) {
					min = i;
				}
			}
			x = a[min];
			a[min] = a[j];
			a[j] = x;
		}	
	}
	
	public static void main(String[] args) {
		Integer[] a = {1,2,34,5,2,3,5,6};
		SelectSort.sort(a);
		show(a);
		System.out.println("数组是否有序:"+isSort(a));
	}
}

选择排序效果:

1
2
2
3
5
5
6
34
数组是否有序:true
  • 插入排序java实现:
package sort;

public class InsertSort extends AbstractSort{
	/*
	 * 插入排序思想:
	 * 1、选择一个活动指针,该元素对应值应该比后一个小,
	 *    (完整表述是比前一个大并比后一个小,但是,如果不满足这个条件的前一个值一定是比这个元素小的!!);
	 * 	    如果满足就更换指针,否则继续寻找合适的位置。
	 * 2、直至循环结束。
	 * 		时间复杂度O(N^2)
	 * 		插入排序比选择排序相对而言快,在数组部分有序的情况下甚至最快。
	 * 		也由此设计出一种基于插入排序的快速排序法-希尔排序(Shell sort) 
	 */

	public InsertSort() {
	}
	
	public static void sort(Comparable<?>[] a) {
		for (int i=0; i<a.length; i++) {
			for (int j=i; j>0 && less(a[j],a[j-1]); j--) {
				exch(a, j, j-1);
			}
		}
	}
	public static void main(String[] args) {
		Integer[] a = {1,3,4,3,7,9,2,10,11,6};
		InsertSort.sort(a);
		show(a);
		System.out.println(isSort(a));
	}
}

插入排序效果:

1
2
3
3
4
6
7
9
10
11
数组是否有序:true

3、算法性能

  • 冒泡排序对于长度为N的数组,冒泡排序需要N*N/2次比较和N次交换,时间复杂度为O(n^2 ),空间复杂度为1,属于原地排序方式。
  • 选择排序对于长度为N的数组,选择排序需要N*N/2次比较和N次交换,时间复杂度为O(n^2 ),空间复杂度为1,属于原地排序方式。
  • 插入排序对于长度为N的数组,插入排序最多需要N*N/2次比较和N次交换,时间复杂度为O(n^2 ),空间复杂度为1,属于原地排序方式。 但要注意的是,如果数组已经部分有序,那么插入排序的交换次数可能会少很多!

4、小结

       选择排序&冒泡排序时间复杂度均较高,不适用于实际大量数据的应用,但是具有学习价值^ ^。以上三种算法均遵守一个思想——让数组中的每一个元素都满足有序的定义,那么这个数组就是有序的。插入排序是三兄弟中较强的一个,从for循环的结构中也可以看出来,冒泡&选择排序第二层for循环是一定要遍历完整的,插排可以在满足less(a[j],a[j-1])时结束。所以实际应用中插入排序比冒泡&选择排序效率更高。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值