9二、选择排序:
选择排序十分的简单和直观,其的工作原理是每一次从待排序的数组中选出最小(或最大)的一个元素,存放在序列的起始位置。因此,选择排序也是像我们这种萌新最容易写出来的排序算法。
排序步骤:
- 选出最小的元素
- 放在数组最前面
- 选出第二小的元素
- 放在第二位
重复如此直到完成排序
下面举个栗子:
有一个数组其元素如下 5 1 4 3 2 6 7 0 9,其选择排序流程如下
第一轮: 0 1 4 3 2 6 9 5 7 0最小,0和5换 第二轮: 0 1 4 3 2 6 9 5 7 1最小,不变 第三轮: 0 1 2 3 4 6 9 5 7 2最小,2和4换 第四轮: 0 1 2 3 4 6 9 5 7 3最小,不变 第五轮: 0 1 2 3 4 5 9 6 7 5最小,5和6换 第六轮: 0 1 2 3 4 5 6 9 7 6最小,6和9换 第七轮: 0 1 2 3 4 5 6 9 7 7最小,7和9换 第八轮: 0 1 2 3 4 5 6 9 7 已经完成排序,但任然需要比较
从这个栗子,我们可以得出选择排序的核心代码:
if (arr[min] > arr [j]) { min = j; } arr[min] = arr[min] ^ arr[i]; arr[i] = arr[min] ^ arr[i]; arr[min] = arr[min] ^ arr[i];
接着我们继续思考,两层for循环,首先是第一层,第一层循环有两个作用,第一个:从0~n-1一个个进行排序;二:表示需要比较的次数。由第一个作用我们可以知道第一层for循环的循环变量i<=n-1,由第二个作用可知i<n-1。(十个数比较只需要比较9次,且c的数组从0开始所以到9就是第十个元素,所以第九次比较就是当i=8的时候)由此可以得出第一层for的代码为 for (int i = 0; i < n-1; i++)。注:我们也可以从i = 1开始,略微改动一下循环终止条件就可以了。
然后我们来确定一下第二层for循环,第二层for循环的作用很简单就是让当前元素与无序的元素进行比较,这里的难点在于无序的元素和当前元素的确定,让我们来回忆一下,第一层for循环的i是从0开始的那么我们的第二层for循环为了避免重复比较,所以第二层for循环的循环变量j应该从i+1开始,到哪里里终止呢?因为每一次都是把最下的元素放到最前面所以从 i 到 n-1都是无序元素,所以当j < n 时,进行循环。由此推出第二层循环 for (int j = i+1; j < n; j++)。如果一层for循环从1开始的,自行调整一下即可。
至此我们就能写出完整的选择排序代码了。嗯?不对,还有个问题,我们的min该等于什么,嗯,没错,min = i,就可以了。
好了现在我们开始写最后的代码吧
1 void sort(int *arr, int count) 2 { 3 for (int i = 0; i < count-1; i++) 4 { 5 int min = i; 6 for (int j = i+1; j < count; j++) 7 { 8 if (arr[min] > arr[j]) 9 { 10 min = j; //找到比arr[min]小的先不交换,先保留下标 11 } 12 } 13 if (min != i) //为了避免arr[i],本身就是最小值任然进行交换的情况 。 14 { 15 arr[min] = arr[min] ^ arr[i]; 16 arr[i] = arr[min] ^ arr[i]; 17 arr[min] = arr[min] ^ arr[i]; 18 } 19 } 20 }
现在我们来想一想如何优化一下选择排序,其实很明显了,在交换方面我们已经没有办法优化了(至少对我这个蒟蒻来说)。那么我们来思考一下,选择排序每次选出最小的放在最前面,或者选出最大的放在最前。我们不仅可以选出最小的也可以选出最大,所以我们就在选出最小值的同时选出最大值,比较所消耗的时间要比循环少,略有优化。
按照这个思想可以写出代码如下:
1 void sort(int *arr, int count) 2 { 3 int left = 0; 4 int right = count-1; 5 int min = left; 6 int max = right; 7 8 while (left < right) 9 { 10 min = left; 11 max = left; 12 for (int i = left; i <= right; i++) 13 { 14 if (arr[min] > arr[i]) 15 { 16 min = i; 17 } 18 if (arr[max] < arr[i]) 19 { 20 max = i; 21 } 22 } 23 24 if (left != min) //最小交换 25 { 26 arr[min] = arr[min] ^ arr[left]; 27 arr[left] = arr[min] ^ arr[left]; 28 } 29 30 if (max == left) //防止当min最小,max最大时产生连续交换 31 { 32 max = min; 33 } 34 35 if (right != max) //最大交换 36 { 37 arr[max] = arr[max] ^ arr[right]; 38 arr[right] = arr[max] ^ arr[right]; 39 arr[max] = arr[max] ^ arr[right]; 40 } 41 left++; 42 right--; 43 } 44 }
这里要特别说明一下这段代码
if (max == left) { max = min; }
为什么一定要写这段代码呢,
我们 来看个例子 :
有个数组其元素为 9 1 5 6 0;
max = 0,min = 4;
执行min交换后,0 1 5 6 9;
max = 0,min = 4;
这时候如果执行max循环那么,数组就和没有执行min交换一样
所以为了防止出现这种情况就需要加上这块代码。
目前作为蒟蒻的我只能优化到这里,如果有更好的优化,大家自行探索,我也会慢慢学习,继续更新。
原文地址:https://www.cnblogs.com/daker-code/p/10328571.html