《Java数据结构与算法》第3章——简单排序算法——冒泡排序、选择排序、插入排序

(1)冒泡排序

package thirdchapter;

class ArrayBub
{
	private long[] a;
	private int nElems;
	
	public ArrayBub(int max)
	{
		a = new long[max];
		nElems = 0;
	}
	
	public void insert(long value)
	{
		a[nElems] = value;
		nElems++;
	}
	
	public void display()
	{
		for (int j=0; j1; out--)
		for (in=0; in a[in+1])
				swap(in,in+1);
	}
	
	private void swap(int one, int two)
	{
		long temp = a[one];
		a[one] = a[two];
		a[two] = temp;
	}
}

public class BubbleSortApp {

	public static void main(String[] args) {

		int maxSize = 100;
		ArrayBub arr = new ArrayBub(maxSize);
		
		arr.insert(77);
		arr.insert(99);
		arr.insert(44);
		arr.insert(55);
		arr.insert(22);
		arr.insert(88);
		arr.insert(11);
		arr.insert(00);
		arr.insert(66);
		arr.insert(33);
		
		arr.display();
		
		arr.bubbleSort();
		arr.display();
	}
}

  • 这个算法的思路是要将最小的数据项放在数组的最开始(数组下标为0),并将最大的数据项放在数组的最后(数组下标为nElems-1)。外层for循环的计数器out从数组的最后开始,即out等于nElems-1,每经过一次循环out减1。下标大于out的数据项都已经是排好序的了。变量out在每完成一次内部循环(计数器为in)后就左移一位,因此算法就不再处理那些已经排好序的数据了。

  • 内层for循环计数器in从数组的最开始算起,即in=0,每完成一次内部循环体加1,当它等于out时结束一次循环。在内层for循环体中,数组下标为in和in+1的两个数据项进行比较,如果下标为in的数据项大于下标为in+1的数据项,则交换两个数据项。


冒泡排序中的不变性

  • 在许多算法中,有些条件在算法执行时是不变的。这些条件被称为不变性。
  • 在bubbleSort.java程序中,不变性是指out右边的所有数据项为有序。在算法的整个运行过程中这个条件始终为真。(在第一趟排序开始前,尚未排序,因为out开始时在数据项的最右边,没有数据项在out的右边)

冒泡排序的效率

  • 一般来说,数组中有N个数据项,则第一趟排序中有N-1次比较,第二趟中有N-2次比较,如此类推。这种序列的求和公式如下:
    (N-1)+(N-2)+(N-3)+…+2+1=N*(N-1)/2
  • 这样,算法做了约N²/2次比较。只有在需要时才交换,所以交换的次数少于比较的次数。如果数据是随机的,那么大概有一半数据需要交换,则交换的次数为N²/4。(不过在最坏的情况下,即初始数据逆序时,每次比较都需要交换。)
  • 冒泡排序运行需要O(N²)时间级别

(2)选择排序

    选择排序改进了冒泡排序,将必要的交换次数从O(N²)减少到O(N)。不幸的是比较次数仍保持为O(N²)。然而,选择排序仍然为大记录量的排序提出了一个非常重要的改进,因为这些大量的记录需要在内存中移动,这就使交换的时间和比较的时间相比起来,交换的时间更为重要。(一般来说,,在Java语言中不是这种情况,Java中只是改变了引用位置,而实际对象的位置并没有发生改变。)

package thirdchapter;

class ArraySel
{
	private long[] a;
	private int nElems;
	
	public ArraySel(int max)
	{
		a = new long[max];
		nElems = 0;
	}
	
	public void insert(long value)
	{
		a[nElems] = value;
		nElems++;
	}
	
	public void display()
	{
		for (int j=0; j

选择排序中的不变性

  • 在selectSort.java程序中,下标小于或等于out的位置的数据项总是有序的。

选择排序的效率

  • 选择排序和冒泡排序执行了相同次数的比较:N*(N-1)/2。对于10个数据项,需要45次比较。然而,10个数据项只需要少于10次交换。对于100个数据项,需要4950次比较,但只进行了不到100次的交换。N值很大时,比较的次数是主要的,所以结论是选择排序和冒泡排序一样运行了O(N²)时间。但是,选择排序无疑更快,因为它进行的交换少得多。当N值较小时,特别是如果交换的时间级比比较的时间级大得多时,选择排序实际上是相当快的。

(3)插入排序

    在大多数情况下,插入排序算法是简单排序算法里最好的一种。虽然插入排序算法仍然需要O(N²)的时间,但是在一般情况下,它要比冒泡排序快一倍,比选择排序还要快一点。尽管它比冒泡排序算法和选择排序算法都更麻烦一些,但它也并不很复杂。它经常被用在较复杂的排序算法的最后阶段,例如快速排序。

    在外层的for循环中,out变量从1开始,向右移动。它标记了未排序部分的最左端的数据。而在内层的while循环中,in变量从out变量开始,向左移动,直到temp变量小于in所指的数组数据项,或者它已经不能再往左移动为止。while循环的每一趟都向右移动了一个已排序的数据项。

package thirdchapter;

class ArrayIns
{
	private long[] a;
	private int nElems;
	
	public ArrayIns(int max)
	{
		a = new long[max];
		nElems = 0;
	}
	
	public void insert(long value)
	{
		a[nElems] = value;
		nElems++;
	}
	
	public void display()
	{
		for (int j=0; j0 && a[in-1] >= temp)
			{
				a[in] = a[in-1];
				--in;
			}
			a[in] = temp;
		}
	}
}

public class InsertSortApp {

	public static void main(String[] args) {

		int maxSize = 100;
		ArrayIns arr = new ArrayIns(maxSize);
		
		arr.insert(77);
		arr.insert(99);
		arr.insert(44);
		arr.insert(55);
		arr.insert(22);
		arr.insert(88);
		arr.insert(11);
		arr.insert(00);
		arr.insert(66);
		arr.insert(33);
		
		arr.display();
		
		arr.insertionSort();
		
		arr.display();
	}
}


插入排序中的不变性

  • 在每趟结束时,在将temp位置的项插入后,比outer变量下标号小的数据项都是局部有序的。

插入排序的效率

  • 这个算法需要多少次比较和复制呢?在第一趟排序中,它最多比较一次,第二趟最多比较两次,以此类推。最后一趟最多,比较N-1次。因此有:
    1+2+3+…+N-1=N*(N-1)/2

  • 然而,因为在每一趟排序法先插入点之前,平均只有全体数据项的一半真的进行了比较,我们除以2得到
    N*(N-1)/4

  • 复制的次数大致等于比较的次数。然而,一次复制与一次交换的时间耗费不同,所以相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。

  • 在任意情况下,对于随机顺序的数据进行插入排序也需要O(N²)的时间级。

  • 对于已经有序或基本有序的数据来说,插入排序要好得多。当数据有序的时候,while循环的条件总是假,所以变成了外层循环中的一个简单语句,执行N-1次。在这种情况下,算法运行只需要O(N)的时间。如果数据基本有序,插入排序几乎只需要O(N)的时间,这对把一个基本有序的文件进行排序是一个简单而有效的方法。

  • 然而,对于逆序排列的数据,每次比较和移动都会执行,所以插入排序不比冒泡排序快。

你可能感兴趣的:(Java数据结构与算法,java,数据结构与算法,冒泡排序,选择排序,插入排序)