【选择排序】和【堆排序】

选择排序


  • 假设升序
    排序思想
    选择排序的思想是每一趟在n-i+1个记录中选取关键字最小的记录,作为有序序列的第i个记录,并和第i个记录交换
    在将要排序的区间中选择出最小的一个数字,与排序区间中的第一个数进行交换,让后依次缩小区间,直到区间只剩下一个数字为止
    算法总共分为三步:选择数据->将数据放入正确的位置->缩小排序范围
    这里写图片描述
    如图,待排序的数组为{9,1,5,8,3,7,4,6,2},用min表示有序区间最小值的下标,然后对待排序区间进行遍历与最小值的下标进行交换,遍历一遍,找到最小的一个下标,最终与待排序区间的第一个值进行交换,最后缩小排序范围
    代码实现如下:

#include<iostream>
#include<assert.h>
using namespace std;
//简单选择排序  9,1,5,8,3,7,4,6,2
void SelectSort1(int arr[], int sz)
{
    assert(arr);
    int left = 0;//记录排序区间的首下标
    while (left < sz)//对前sz-1个数进行比较
    {
        int begin = left;//遍历有序区间
        int min = left;//记录有序区间中的最小值
        while (begin <= sz)//要遍历完有序区间中的每一个数
        {
            if (arr[min]>arr[begin])
            {
                min = begin;
            }
            ++begin;//对待排序区间中的数字逐一进行比较
        }
        swap(arr[min], arr[left]);
        ++left;//缩小有序区间
    }
}
void Printf(int arr[], int sz)
{
    assert(arr);
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i]<<"  ";
    }

}
int main()
{
    int arr[] = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    SelectSort1(arr, sz-1);
    Printf(arr, sz);
    system("pause");
}

选择排序的优化:
上面的选择排序中,每一趟只能找出一个最小值,那么要找N趟,每躺要比较/交换N次,所以时间复杂度为O(n^2)
优化的方法是:每一趟排序可以找出一个最大值和最小值,将找出的最小值放在数组的待第一位,将找出的最大值放在数组的待最后位,排序区间的那么比较的就缩小为N/2趟
这里写图片描述
代码实现如下:

#include<iostream>
#include<assert.h>
using namespace std;
//简单选择排序  9,1,5,8,3,7,4,6,2
void SelectSort2(int arr[], int sz)
{
    assert(arr);
    int left = 0;//待排序区间的首下标
    int right = sz;//待排序区间的尾下标
    while (left < right)//遍历整个有序区间
    {
        int max = right;//记录最大值的下标
        int min = left;//记录最小值的下标
        int begin = left;//遍历待排序区间
        while (begin <= right)//遍历待排序有序区间
        {
            if (arr[begin]<arr[min])
            {
                min = begin;
            }
            if (arr[begin]>arr[max])
            {
                max = begin;
            }
            ++begin;
        }
        swap(arr[min], arr[left]);
        if (max == left)//处理第一个值为最大值的情况
        {
            max = min;
        }
        swap(arr[max], arr[right]);
        ++left;
        --right;
   }
}
void Printf(int arr[], int sz)
{
    assert(arr);
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i]<<"  ";
    }

}
int main()
{
    int arr[] = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    SelectSort2(arr, sz - 1);
    Printf(arr, sz);
    system("pause");
}

时间复杂度&&空间复杂度分析:
无论最好的情况还是最坏的情况,比较的次数都是一样多,第i趟排序需要第n-i次的比较,所以需要比较的次数为:n-1+n-2+n-3+…+2+1=n*(n-1)/2,最好的情况,只交换一次,最差的时候,交换n-1次,所以总的时间复杂度为O(n^2)

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

堆排序

堆排序的思想主要分为两步:

  • 将一个无序序列构建成一个堆
    首先根据所给元素进行建堆,从下往上,从右向左将每个非叶子节点当作根节点,将其和其子树调整成大堆,如图为建堆的整个过程
    这里写图片描述
    • 输出堆顶元素,将剩余元素调整为一个新的堆
      当大堆建好之后,堆顶 元素一定是堆中元素的最大值,将堆顶元素与堆尾du素交换,再对交换后剩下的元素重新建堆
      代码实现如下:

#include<iostream>
#include<assert.h>
using namespace std;
//堆排序 90,10,50,80,30,70,40,60,20
void AdjustDown(int *arr, int father, int sz)
{
    assert(arr);
    int child = father * 2 + 1;
    while (child < sz)
    {
        if (child + 1 < sz && arr[child+1] > arr[child])
        {
            child++;
        }
        if (arr[child]>arr[father])
        {
            swap(arr[child], arr[father]);
            father = child;
            child = father * 2 + 1;
        }
        else
            return;
    }
}
void HeapSort(int *arr, int sz)
{
    assert(arr&&sz > 0);
    //默认建大堆
    int i = 0;
    for (i = (sz - 2) / 2; i >=0; i--)
    {
        AdjustDown(arr, i, sz);
    }
    //建好堆之后,堆顶元素一定是堆中最大的元素,将堆顶元素与堆尾元素进行交换
    while (sz>1)//要排序的数据个数必须大于1
    {
        sz--;
        swap(arr[0], arr[sz]);
        AdjustDown(arr, 0, sz);
    }
}
void Printf(int arr[], int sz)
{
    assert(arr);
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i]<<"  ";
    }

}
int main()
{
    int arr[] = { 90, 10, 50, 80, 30, 70, 40, 60, 20 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    HeapSort(arr, sz);
    Printf(arr, sz);
    system("pause");
}

时间复杂度&&空间复杂度分析

  • 建堆的时间复杂度为O(n)
  • 正式排序进行重建堆的时间复杂度为O(nlogn)
  • 堆排序堆原始状态并不敏感,所以堆排序的最好、最坏、平均时间复杂度都是O(nlogn)
  • 在性能上高于冒泡排序,选择排序以及直接插入排序的O(n^2)
  • 空间复杂度上,堆排序是一种不稳定的排序
  • 不适合待排序序列个数较少的情况

猜你喜欢

转载自blog.csdn.net/wyn126/article/details/81607259