排序算法学习整理二(选择)

二、选择排序:

  选择排序十分的简单和直观,其的工作原理是每一次从待排序的数组中选出最小(或最大)的一个元素,存放在序列的起始位置。因此,选择排序也是像我们这种萌新最容易写出来的排序算法。

  排序步骤:

  1. 选出最小的元素
  2. 放在数组最前面
  3. 选出第二小的元素
  4. 放在第二位

  重复如此直到完成排序

  下面举个栗子:

    有一个数组其元素如下 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 }
View Code

  现在我们来想一想如何优化一下选择排序,其实很明显了,在交换方面我们已经没有办法优化了(至少对我这个蒟蒻来说)。那么我们来思考一下,选择排序每次选出最小的放在最前面,或者选出最大的放在最前。我们不仅可以选出最小的也可以选出最大,所以我们就在选出最小值的同时选出最大值,比较所消耗的时间要比循环少,略有优化。

  按照这个思想可以写出代码如下:

 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 }
View Code

  这里要特别说明一下这段代码

        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交换一样

所以为了防止出现这种情况就需要加上这块代码。

  目前作为蒟蒻的我只能优化到这里,如果有更好的优化,大家自行探索,我也会慢慢学习,继续更新。

猜你喜欢

转载自www.cnblogs.com/daker-code/p/10328571.html