简单排序之选择排序、冒泡排序、插入排序

选择排序

什么是选择排序呢?选择排序就是每次从数组中选择出一个最小(最大)的放在排好序的后面。

流程

数组[2,1, 4 ,3 ,5, 6]
索引 0, 1, 2, 3, 4, 5
第一步 :从0-5中选择最小的数与0位置交换。[1, 2, 4, 3, 5, 6] 1位置数比0位置数小,所以两两位置互换
第二步 :从1-5中选择最小的数与1位置交换。[1, 2, 4, 3, 5, 6] 遍历之后发现1位置的数最小不需交换
第三步 :从2-5中选择最小的数与2位置交换。[1, 2, 3, 4, 5, 6] 3位之的3比2位置的4小,交换两个位置。
第四步 :从3-5中选择最小的数与3位置交换。[1, 2, 3, 4 ,5, 6] 无需交换
第五步 :从4-5中选择最小的数与4位置交换。[1, 2, 3, 4 ,5, 6] 无需交换
第六步 :从5-5中选择最小的数与5位置交换。[1, 2, 3, 4 ,5, 6] 无需交换
总结一下上面的步骤:
首先从原始数组中选择最小的(或最大的)s数,和第0位交换。
接着从剩下的n-1个数据中选择次小的1个元素,和第1位交换
然后,不断递增位数,直到最后两个数据完成交换。可以表示位 从m - n (0 <= m < n)中选择最小的一个数与m位交换。从0递增m,重复交换步骤知道m = n-1即可完成排序。

编码

template<typename T>
void swap(T &a, T &b)
{
    
    
   T temp = a;
   a = b;
   b = temp;
}

void selection_sort(int *arr, int arr_size) // 1
{
    
    
   for (int i = 0; i < arr_size; i++) // 2
   {
    
    
       for (int j = i; j < arr_size; j++) // 3
       {
    
    
           if (arr[i] > arr[j]) // 4
           {
    
    
               swap(arr[i], arr[j]); 
           }
       }
   }
}
int main()
{
    
    
   int arr[] = {
    
     2, 1, 4, 3, 5, 6 };
   selection_sort(arr, sizeof(arr) / sizeof(int)); // 5
   return 0;
}

代码分析:

  1. 使用指针作为参数,并传入素组的大小是素组可以退化为指针,数组名即是首地址
  2. 外层循环控制交换位值,从0开始到n-1结束,可以把选择排序想成两个指针,第一个指针指的位置就是当前需要放入的最小(最大值),第二个指针就是遍历后面的每个数,与当前值进行比较,如果小于(大于)第一个指针指向位置的值,就交换两个位置的值。外层循环就是控制第一个指针。
  3. 内层循环控制第二个指针。用于遍历第一个指针指向位置到数组末尾的值。
  4. 第一指针指向的大于(小于)第二指针的值,就交换两个位置的值。
  5. 数组名就是数组的地址,sizeof(一个地址)相当于求一个指针内容得大小。sizeof(类型) 表示该种类型占了多少空间,两个相除就可以得到数组得大小。

优化

我们发现每次只要第一指针指向得值大于(小于)第二指针指向得值就得交换,能不能每次只交换一次呢?这种是可以实现的。只需要定义额外的一个变量来保存剩余数组元素的最小值索引,等遍历完之后,在交换最小索引位置的数和第一指针位置的数即可。

void selection_sort_optimize(int *arr, int arr_size) // 优化过后的函数 交换仅仅只需要执行n此、次
{
    
    
   int min_index = 0;
   for (int i = 0; i < arr_size; i++) 
   {
    
    
       min_index = i;
       for (int j = i; j < arr_size; j++) 
       {
    
    
           if (arr[min_index] > arr[j]) 
           {
    
    
               min_index = j; // 找到最小值索引
           }
       }
       swap(arr[i], arr[min_index]); // 只需要交换最小值即可。
   }
}

冒泡排序

为什么叫冒泡排序呢?冒泡排序和选择排序有点区别,选择排序是固定左边,左边都是排好的,然后从右边选择相应的数与之交换。冒泡
排序是固定右边,将最大的值排好,然后又把第二大的值冒泡到倒数第二位上。像泡泡一样,一个一个交换上来。

流程

  1. 从第0为开始,如果当前位比下一位大就交换两数,直到n位,最大的数交换到了n位。
  2. 从第0位开始,如果当前位比下一位大就交换两数,直到n-1位,第二大的数交换到了n-1位。
  3. 从第0位开始,如果当前位比下一位大就交换两数,直到n-2位,第三大的数交换到了n-2位。
  4. 一直重复此步骤,知道倒数第一大的数交换到了0位置即可。
    [6,5,4,3,2,1]
    第一步:从第0未开始,比后一位数大的交换,直到第5位,[5,4,3,2,1, 6]
    第二步:从第0未开始,比后一位数大的交换,直到第4位,[4,3,2,1,5,6]
    第三步:从第0未开始,比后一位数大的交换,直到第3位,[3,2,1,4,5,6]
    第四步:从第0未开始,比后一位数大的交换,直到第2位,[2,1,3,4,5,6]
    第五步:从第0未开始,比后一位数大的交换,直到第1位,[1,2,3,4,5,6]
    第六步:从第0未开始,比后一位数大的交换,直到第0位,[1,2,3,4,5,6] 到此步,0位肯定是比第1位小的

编码

void bubble_sort(int arr[], int arr_size)
{
    
    
    for (int i = arr_size-1; i >= 0; i--) // 从数组最后以为开始,向0位靠近
    {
    
    
        for (int j = 0; j < i; j++) // 交换 0 到 n -j位置
        {
    
    
            if (arr[j] > arr[j + 1])
            {
    
    
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}
int main()
{
    
    
    int arr[] = {
    
     6,5,4,3,2,1 };
    bubble_sort(arr, sizeof(arr) / sizeof(int));
    return 0;
}

插入排序

插入排序就像打扑克牌一样,我拿到手里的必须先排好序,比如我拿到2,3,5, 后面又拿到一个1,那么我应该把一插入到2的前面,保证现在的都有序。计算机不像人脑一样,一下子就可以直接插到2的前面,肯定是得遍历一遍,1,小于5,与5交换,1,小于3,与3交换,1小于2,与2 交换,就可以完成插入到2的前面。

流程

  1. 保证0位置上的元素有序,因为只有一个元素,肯定是有序的。
  2. 保证0-1位置上元素有序,两个元素比较后,小的放前面,大的放后面
  3. 保证0-2位置上的元素有序,由于0-1有序,那么仅仅只需要将2位置比较,如果他不小于1位置,那么就放在2位置。如果小于1位置,与1位置交换,在和0位置比较,如果小于0位置交换,不小于直接结束。保证下0-下一个位置的数有序。
  4. 直到0-n上的元素都有序,那么数组就有序了。
    举个例子:
    [2,1, 4 ,3 ]
    第一步:保证0位有序,一个元素肯定有序 [2, 1,4,3]
    第二步,保证0-1位有序, 发现第1位的1小于第0位的2,交换位置 [1,2,4,3]
    第三步,保证0-2位有序, 发现第2位4大于2,由于0-1已经有序,那么这里不要再交换, [1,2,4,3]
    第四步, 保证0-3位有序 ,发现第3位3比第2位4小,交换位置,[1,2,3,4],又发现,2位置3比1位置2大,不需要交换结束循环,因为0-2已经有序,只需大于前面的数那么就可以结束。

编码

void insert_sort(int arr[], int arr_size)
{
    
    
    for (int i = 0; i < arr_size; i++) // 外层控制0-i有序
    {
    
    
        for (int j = i; j > 0; j--) // j 一定是大于0的,不然j-1小于0,数组越界,发生未定义行为
        {
    
    
            if (arr[j] < arr[j - 1]) // j 位置与j-1位置比较,如果小于交换,
            {
    
    
                swap(arr[j], arr[j - 1]);
            }
            else // 因为已经有序,大于j-1就不可能大于j-2了,直接break
            {
    
    
                break;
            }
        }
    }
}
int main()
{
    
    
    int arr[] = {
    
     6,5,4,3,2,1 };
    insert_sort(arr, sizeof(arr) / sizeof(int));
    return 0;
}

代码模板化

将这几个排序都模板一下,只需要类型支持大于小于运算符,拷贝构造函数能正确拷贝对象值。那么基本任何类型都是可以排序的额。

template<typename T>
void swap(T &a, T &b)
{
    
    
    T temp = a;
    a = b;
    b = temp;
}

template<typename T>
void selection_sort(T *arr, int arr_size) // 优化过后的函数 交换仅仅只需要执行n此、次
{
    
    
    int min_index = 0;
    for (int i = 0; i < arr_size; i++) 
    {
    
    
        min_index = i;
        for (int j = i; j < arr_size; j++) 
        {
    
    
            if (arr[min_index] > arr[j]) 
            {
    
    
                min_index = j; // 找到最小值索引
            }
        }
        swap(arr[i], arr[min_index]); // 只需要交换最小值即可。
    }
}

template<typename T>
void bubble_sort(T arr[], int arr_size)
{
    
    
    for (int i = arr_size-1; i >= 0; i--) // 从数组最后以为开始,向0位靠近
    {
    
    
        for (int j = 0; j < i; j++) // 交换 0 到 n -j位置
        {
    
    
            if (arr[j] > arr[j + 1])
            {
    
    
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}

template<typename T>
void insert_sort(T arr[], int arr_size)
{
    
    
    for (int i = 0; i < arr_size; i++) // 外层控制0-i有序
    {
    
    
        for (int j = i; j > 0; j--) // j 一定是大于0的,不然j-1小于0,数组越界,发生未定义行为
        {
    
    
            if (arr[j] < arr[j - 1]) // j 位置与j-1位置比较,如果小于交换,
            {
    
    
                swap(arr[j], arr[j - 1]);
            }
            else // 因为已经有序,大于j-1就不可能大于j-2了,直接break
            {
    
    
                break;
            }
        }
    }
}

总结

这三种排序是最基础的排序方法。他们各种特点,选择排序固定左边,从剩余元素中选择一个最大(最小)的值与左边第i位交换。冒泡排序呢又是从右边固定,从0位开始,依次把最大的值冒泡到数组最后,倒数第二,直到0位。而插入排序呢有点特殊,第一保证0索引有序,然后有保证0-1有序,0-2有序,0-3有序,。。。其中呢,新进来的数只需和前一位比较,如果小于交换,不小于就结束比,保证下一个有序环节,因为前面的数都是有序的,只要大于某一位置,那么他肯定是大于该位置前面的所有数的。

猜你喜欢

转载自blog.csdn.net/qq_33944628/article/details/127032653
今日推荐