数据结构(四)基本排序算法——冒泡、选择排序

一、冒泡排序

1、排序原理

        冒泡排序作为排序算法中最为基础的一种排序算符,他的排序原理也是最为简单的:相邻位比较,反序则互换。所谓的相邻位,实际上就是排序数组中下标紧挨着的两个元素,用数组下标进行说明,就是下标为j的元素和下标为j+1的元素之间的关系。反序则互换指的是,如果两个相邻元素之间的取值,和约定的数组顺序(例如升序或者降序)是相反的那么数组中这两个位置上的元素要通过一个临时变量来完成一次交换。

2、算法实现

import java.util.Arrays;

public class BubbleSort {
	
	/**
	 * 冒泡排序
	 * @param array
	 */
	public void bubbletSort(int[] array) {
		
		//[3]用最外层循环i控制比较的轮次,第i轮比较的时候,就有i个元素已经有序,则这i个元素是不需要进行排序的,而在整个长度为n的数组中,只要进行n-1轮排序即可
		for(int i = 0; i < array.length-1; i++) {
			//[2]内层循环控制两个相邻位下标的变化,在第i轮比较中,数组的后i个元素就是有序的,所以不需要参与比较
			for(int j = 0; j+1 <= array.length-1-i; j++) {
				//[1]相邻位比较,反序则互换
				if(array[j] > array[j+1]) {
					int tmp = array[j];
					array[j] = array[j+1];
					array[j+1] = tmp;
				}
			}
		}
		
	}

	public static void main(String[] args) {
		int[] array = new int[] {7,0,1,9,2,6,3,8,5,4};
		BubbleSort bs = new BubbleSort();
		bs.bubbletSort(array);
		System.out.println(Arrays.toString(array));
	}
	
}

3.时间复杂度、空间复杂度、稳定性分析
  1.冒泡排序的时间复杂度
    在进行冒泡排序的时候,假设数组中存在n个元素:
    第0轮排序:n个元素比较n­1次
    第1轮排序:n­1个元素比较n­2次
    第2轮排序:n­2个元素比较n­3次
    ...
    第n­3轮排序:3个元素比较2次
    第n­2轮排序:2个元素比较1次
那么将上面比较的次数相加:(n­1) + (n­2) + (n­3) + ... + 3 + 2 + 1 = n*(n­-1)/2 = (n^2 -­n)/2,
根据之前讲解的计算算法时间复杂度的方式,我们可以知道,上述公式中的最高次幂项是n^2 /2
所以冒泡排序的时间复杂度是O(n^2 )
  2.冒泡排序的空间复杂度
  整个冒泡排序在运算过程中,仅仅使用了一个可以重复利用,用来交换相邻位元素的临时变量所以冒泡排序的空间复杂度O(1)
 3.冒泡排序的稳定性
  在冒泡排序进行相邻位比较的时候,如果相邻的两个元素大小是相等的,那么这两个元素并不会进行互换并且,冒泡排序算法每一轮比较都是从数组的最开始,一步一步向后进行比较所以并不会出现相同取值的两个元素之间互换的情况,所以冒泡排序是一种稳定的排序算法.

二、选择排序

1、排序原理

        选择排序的原理与冒泡排序相比更加容易理解:选定一个标准位,将待排序序列中的元素与标准位元素逐一比较,反
序则互换,其中所谓的标准位实际上也是数组中的一个下标位,在选定了这个下标位之后,在一轮排序中这个标准位将不再移
动,然后将待排序序列——也就是这个标准位之后所有元素组成的序列——中的元素逐个和标准位上的值进行比较,如果某一个待排序序列上的值比标准位上的值更小(升序排序)或者更大(降序排序),那么就将这个元素和标准位上的元素进行互换即可。标准位的选择一般选取待排序序列的第一个下标作为标准位使用。

2、算法实现

import java.util.Arrays;

public class SelectionSort {
	
	/**
	 * 选择排序
	 * @param array
	 */
	public void selecttionSort(int[] array) {
		
		//[1]使用外层循环指定标记位,然后将待排序序列中的元素一个一个的和标记位上的值进行比较,反序则互换;比较依然是进行n-1次即可
		for(int i = 0; i < array.length-1; i++) {
			//[2]使用内层循环j循环倒序遍历整个待排序序列,使用array[j]和标准位的array[i]进行大小比较
			for(int j = array.length-1; j > i; j--) {
				//[3]标准位上的元素和待排序序列中的元素进行比较,反序则互换
				if(array[i] > array[j]) {
					int tmp = array[i];
					array[i] = array[j];
					array[j] = tmp;
				}
			}
		}
		
	}
	
	public static void main(String[] args) {
		
		int[] array = new int[] {7,0,1,9,2,6,3,8,5,4};
		SelectionSort ss = new SelectionSort();
		ss.selecttionSort(array);
		
		System.out.println(Arrays.toString(array));
		
	}
	
}

3、时间复杂度、空间复杂度、稳定性分析

   1.选择排序的时间复杂度
    从上述原理图看来,选择排序每一轮比较的情况如下:
    第0轮排序:使用后n­1个元素与标准位比较
    第1轮排序:使用后n­2个元素与标准位比较
    第2轮排序:使用后n­3个元素与标准位比较
    ...
    第n­3轮排序:使用后2个元素与标准位比较
    第n­2轮比较:使用最后一个元素与标准位比较
   将上述比较的次数相加得到:(n­1) + (n­2) + (n­3) + ... + 3 + 2 + 1 = n*(n­-1)/2 = (n^2 -­n)/2
   这个时间复杂度的公式与冒泡排序的时间复杂度公式是相同的所以,选择排序的时间复杂度依然是O(n 2 )
2.选择排序的空间复杂度
   在选择排序中,在使用待排序序列中的元素与标准位上的元素进行比较的时候,如果发现两个元素是反序的,那么仅需要使用一个可以重复利用的临时变量进行元素交换即可这一点依然与冒泡排序中的情况相同,所以,选择排序的空间复杂度依然是O(1)
3.选择排序的稳定性
从图中的流程我们可以看出,选择排序每一轮次的比较都是从待排序序列的最后,逐个向前进行比较的,这就导致如果待排序序列中如果存在两个取值相同的元素,那么后面的元素一定会首先与标准位上的值进行互换。

注意:在遍历待排序序列的时候,如果不是倒序遍历,而是从i+1位下标开始,逐个向后正序遍历,那么与标准位首先进行互换的同值元素就是2a,也就是说这种选择排序变成了一种稳定的排序算法。是的,这一情况是没错的,但是从实践运行的角度出发我们发现:倒序遍历待排序序列的方式,能够在一定程度上提升选择排序算法的效率,比正序遍历待排序序列要快上一些,所以在这里我们依旧选择使用倒序的方式遍历待排序序列。

发布了98 篇原创文章 · 获赞 165 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/cyl101816/article/details/93747684