日撸 Java 三百行(47 天: 简单选择排序:最朴实、简单的排序)

注意:这里是JAVA自学与了解的同步笔记与记录,如有问题欢迎指正说明

目录

一、关于简单选择排序

二、简单选择排序的逻辑——选极值的擂台思想

三、代码实现

性能与特性分析

总结


一、关于简单选择排序

        在完成了昨天快排的学习,今天稍微放松放松,让我们了解下排序中最容易接收的一种排序:简单选择排序。

简单选择排序属于选择排序的类别,不同于插入排序和shell排序那种基于信息表插入的策略,也不同于冒泡和快排那种基于数据交换的策略。选择排序是线性表中元素有目标的选择,并且放到合适的位置。当然,数组中任何位置都是有元素的,所以这个“ 放到合适的位置 ”本质可能还是一种交换的过程,但是这不同于交换排序那种交换。交换排序的交换是一系列交换的组合,而且这些交换不一定某次交换就决定了元素的最终位置,而交换排序有非常明显的一次性交换确定元素最终位置的操作,我们称这种交换为选择。

二、简单选择排序的逻辑——选极值的擂台思想

        我在冒泡排序那天的博客那里提到过简单选择排序。因为冒泡的思想与简单选择排序的思想有个共同点:都目标在于从不定序列中选择出极值元素,然后将其交付到数组一端的有序序列中。但是当时我也提到过:就增序排序来说,冒泡是“ 把最大数冒到最右端 ”,而不是简单选择的“ 挑出最大元素,然后放到最右端 ”。前者利用了多次交换具有的传递性将最大元素传递到有序序列中,而后者利用了擂台思想求规定范围的极值元素,然后一次交换移动到确定序列中。

        这个过程很简单,不要想太难。擂台思想说着高端,其实就是编程的基础思想:首先确定一个假定值,这个值可以是极端值(比如求最大时赋值为数据类型的最小值),也可以是第一个元素的值,然后到一个数组中比大小,每次比大小之后确定一个新的极值,如此遍历下来,最终便确定此数据范围内的一个极值。

        这个过程就像擂台比赛,首先冠军只有一个席位和一个擂台,然后我们先让一个极其弱鸡的人或者第一位选手站上擂台,称为冠军,之后不断不服的人上擂台来比拼,最终赢的人(当前极值)霸占冠军,于是继续让人上擂台比拼,如此反复,等到最后一个人比拼完,最后站在擂台上笑到最后的人就是真的冠军(这一系列数据中的极值)。那么我们最开始为什么要放一个弱鸡的人(与极值相反的极端值)呢?因为假如我们当前无法把第一位选手邀请上擂台时(暂时不知道第一个数据的信息),选择一个弱鸡总是能保证他会被第一个选择的选手打败,从而保证最后站在台上的肯定是这堆选手中的一个(保证极值是数列中的某个元素)。

        具体体现在排序中,全局的思路我们还是模仿冒泡,即在无需序列中确定有序元素交换到有序序列的一端。(下图只是展示思想,具体我们将确定序列设置在右端,因此每次是找最小元素放到左端)

         而在细节上,在无序序列中选择极值元素的手段变为采用擂台思想,而非利用冒泡的传递特性。

         正如上图,我们先通过\(i\)遍历确定擂台,然后将\([i+1,n)\)内的全部元素都与擂台元素进行比较,最终确定了极值元素1,于是将1与擂台进行一次交换。

         下一次便将\(i\)后延的第一个元素确定为新擂台,然后继续将\([i+1,n)\)内的全部元素都与擂台元素进行比较,最终确定擂台元素就是最小的,故无实际交换。

         后续操作类似,故不再说明。

        以上,就是简单选择排序。

三、代码实现

        由于在讲述概念之时就基本说清楚了代码的流程了,这里我们就直接贴出代码和单元测试:

	/**
	 *********************
	 * Selection sort. All data are valid.
	 *********************
	 */
	public void selectionSort() {
		DataNode tempNode;
		int tempIndexForSmallest;

		for (int i = 0; i < length - 1; i++) {
			// Initialize.
			tempNode = data[i];
			tempIndexForSmallest = i;
			for (int j = i + 1; j < length; j++) {
				if (data[j].key < tempNode.key) {
					tempNode = data[j];
					tempIndexForSmallest = j;
				} // Of if
			} // Of for j

			// Change the selected one with the current one.
			data[tempIndexForSmallest] = data[i];
			data[i] = tempNode;
		} // Of for i
	}// Of selectionSort

	/**
	 *********************
	 * Test the method.
	 *********************
	 */
	public static void selectionSortTest() {
		int[] tempUnsortedKeys = { 5, 3, 6, 10, 7, 1, 9 };
		String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
		DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);

		System.out.println(tempDataArray);

		tempDataArray.selectionSort();
		System.out.println("Result\r\n" + tempDataArray);
	}// Of selectionSortTest

         代码中我们用tempNode与tempIndexForSmallest存储了擂台元素的数值与下标,这样操作使得最终元素的交换的操作能够统一最小值更新和最小值还在初始擂台的情况,不必额外用条件语句判断。

        测试结果:

性能与特性分析

        简单选择排序的空间复杂度为\(O(1)\),然后执行观察其代码,可以发现无论有序还是逆序,我们每次都会照常遍历全部数组找最小,可见for循环一共执行的频度恒定地确定为\(\frac{n(n-1)}{2} \),因此这个算法极其稳定地具有\(O(N^2)\)复杂度,没有最优,同时也没有最差情况

        因为简单选择排序的过程中选出极值元素会进行交换,如果这时擂台元素与极值元素中间有一个与擂台元素相同的元素,交换后,擂台元素与这个元素的相对位置会发生改变,因此,简单排序是一种不稳定的排序算法。同时,简单选择进行元素比对是是顺序依次进行地,因此是适用于链表的,它对于链表和顺序表都适用

        虽然说这个算法的复杂度是固定的\(O(N^2)\),而且没有最优环境,但是相比于其他的简单排序,它只有在确定了极值才发生交换,因此简单选择排序的交换次数的复杂级只有\(O(N)\),而\(O(N^2)\)的复杂级更多只体现在条件比较过程。因此其在对于那些数据项非常庞大的数据时,他是所有简单排序中最可靠,效率最高的(因为数据相比较大的时候进行交换是相对来说比较花时间的,这种时候尽可能不要选择交换排序比如冒泡和快排)

        同时,因为简单选择排序每次都会确定一个元素到有序序列中,因此这些元素的最终位置就是确定了,因此对于任何选择排序来说,在排序中断打断排序时,有序序列部分的元素位置就是其最终的元素位置

总结

        简单选择全篇给人的感觉就是:朴实、简单。这个排序算法没有许多弯弯绕绕,与直接插入排序一样都是通过基础的编程思想的推广而得到的算法。同时它也是简单排序中唯一一个复杂度稳定的算法,因此能在更复杂多变的环境下相比其他简单简单排序算法有相对比较稳定的开销。

        简单排序最大的贡献其实还是其对于后续的堆排序提供的思路,而堆排序也正是简单选择排序关于“ 找最小值 ”的优化进阶版本。

猜你喜欢

转载自blog.csdn.net/qq_30016869/article/details/124467939