C++ 排序算法

C++ 常见排序算法

/*! @brief 冒泡排序
*
* 算法之原理: 相邻的数据进行两两比较,小数放在前面,大数放在后面, 
*              这样一趟下来,最小的数就被排在了第一位,第二趟也是如此,如此类推,直到所有的数据排序完成。 

* 时间复杂度: 平均 O(n^2) 最坏 O(n^2) 

* 空间复杂度: O(1) 

* 算法稳定性: 稳定 

* @param nums the array to sort
*/
void CSortAlgo::bubble_sort(vector<int> &nums)
{
    int len = nums.size();
    for (int i = 0; i < len - 1; i++)
    {
        for (int j = len - 1; j > i; j--)
        {
            if (nums[j] < nums[j - 1])
            {
                int temp = nums[j];
                nums[j] = nums[j - 1];
                nums[j - 1] = temp;
            }
        }
    }
}


/*! @brief 选择排序

* 算法之原理: 先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
*              然后,再从剩余未排序元素中继续寻找最小(大)元素,
*              然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

* 时间复杂度: 平均 O(n^2) 最坏 O(n^2) 

* 空间复杂度: O(1) 

* 算法稳定性: 不稳定 

* @param nums the array to sort
*/
void CSortAlgo::select_sort(vector<int> &nums)
{
    int len = nums.size();
    for (int i = 0; i < len; i++)
    {
        int index = i;
        for (int j = i + 1; j < len; j++)
        {
            if (nums[j] < nums[index])
                index = j;
        }
        if (index != i)
        {
            int temp = nums[i];
            nums[i] = nums[index];
            nums[index] = temp;
        }
    }
}


/*! @brief 插入排序

 * 算法之原理: 将数据分为两部分,有序部分与无序部分,一开始有序部分包含第1个元素,
 *               依次将无序的元素插入到有序部分,直到所有元素有序。
 *               插入排序又分为直接插入排序、二分插入排序、链表插入等,这里只讨论直接插入排序。

 * 时间复杂度: 平均 O(n^2) 最坏 O(n^2) 

 * 空间复杂度: O(1) 

 * 算法稳定性: 稳定 

 * @param nums the array to sort
*/
void CSortAlgo::insert_sort(vector<int> &nums)
{
    int len = nums.size();
    for (int i = 1; i < len; i++)
    {
        int j = i - 1;
        int k = nums[i];
        while (j > -1 && k < nums[j])
        {
            nums[j + 1] = nums[j];
            j--;
        }
        nums[j + 1] = k;
    }
}


/*! @brief 快速排序
 * @details
 * 算法之原理: 快速排序是目前在实践中非常高效的一种排序算法。
 *               通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
 *               然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

 * 时间复杂度: 平均 O(nlogn) 最坏 O(n^2) 

 * 空间复杂度: O(logn) 

 * 算法稳定性: 不稳定 

 * @param nums the array to sort
 * @param left 
 * @param right 
*/
void CSortAlgo::quick_sort(vector<int> &nums, int left, int right)
{
    int len = nums.size();
    if (left < right)
    {
        int i = left, j = right, target = nums[left];
        while (i < j)
        {
            while (i < j && nums[j] > target)
                j--;
            if (i < j)
                nums[i++] = nums[j];

            while (i < j && nums[i] < target)
                i++;
            if (i < j)
                nums[j] = nums[i];
        }
        nums[i] = target;
        quick_sort(nums, left, i - 1);
        quick_sort(nums, i + 1, right);
    }
}

/*! @brief 归并排序

 * 算法之原理: 归并排序具体工作原理如下(假设序列共有n个元素):
 * 将序列每相邻两个数字进行归并操作(merge),形成floor(n / 2)个序列,排序后每个序列包含两个元素
 * 将上述序列再次归并,形成floor(n / 4)个序列,每个序列包含四个元素
 * 重复步骤2,直到所有元素排序完毕。

 * 时间复杂度: 平均 O(nlogn) 最坏 O(nlogn) 

 * 空间复杂度: O(1) 

 * 算法稳定性: 稳定 

 * @param nums the array to sort
 * @note 如果是使用链表的实现的话,空间复杂度可以达到O(1),
 * 但如果是使用数组来存储数据的话,在归并的过程中,需要临时空间来存储归并好的数据,所以空间复杂度为O(n)
*/
void CSortAlgo::merge_sort(vector<int> &nums, vector<int> &nums_temp, int start_index, int end_index)
{
    if (start_index < end_index)
    {
        int mid_index = (start_index + end_index) / 2;
        merge_sort(nums, nums_temp, start_index, mid_index);
        merge_sort(nums, nums_temp, mid_index + 1, end_index);
        merge(nums, nums_temp, start_index, mid_index, end_index);
    }
}

void CSortAlgo::merge(vector<int> &nums, vector<int> &nums_temp, int start_index, int mid_index, int end_index)
{
    // 将有二个有序数列nums[start_index...mid_index]和nums[mid_index...end_index]合并。  
    int i = start_index, j = mid_index + 1;
    int k = 0;
    while (i < mid_index + 1 && j < end_index + 1)
    {
        if (nums[i] > nums[j])
            nums_temp[k++] = nums[j++];
        else
            nums_temp[k++] = nums[i++];
    }
    while (i < mid_index + 1)
        nums_temp[k++] = nums[i++];
    while (j < end_index + 1)
        nums_temp[k++] = nums[j++];

    for (i = 0, j = start_index; j < end_index + 1; i++, j++)
        nums[j] = nums_temp[i];
}

/*! @brief 堆排序

* 算法之原理: (以最大堆为例)
*            1. 先将初始数据R[1..n]建成一个最大堆,此堆为初始的无序区
*            2. 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,
*            由此得到新的无序区R[1..n - 1]和有序区R[n],且满足R[1..n - 1].keys≤R[n].key
*            3. 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n - 1]调整为堆。
*            重复2、3步骤,直到无序区只有一个元素为止。

* 时间复杂度: 平均 O(nlogn) 最坏 O(nlogn) 

* 空间复杂度: O(1) 

* 算法稳定性: 不稳定 

* @param nums the array to sort
*/
void CSortAlgo::heap_sort(vector<int> &nums)
{
    int len = nums.size();
    int i;
    // 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
    for (int i = len / 2 - 1; i >= 0; i--)
    {
        heap_adjust(nums, i, len);
    }

    for (i = len - 1; i > 0; i--)
    {
        // 将第1个元素与当前最后一个元素交换,保证当前的最后一个位置的元素都是现在的这个序列中最大的
        int temp = nums[0];
        nums[0] = nums[i];
        nums[i] = temp;
        // 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
        heap_adjust(nums, 0, i);
    }
}

/*!
 * 将数组arr构建大根堆
 * @param nums 待调整的数组
 * @param i    待调整的数组元素的下标
 * @param len  数组的长度
 */
void CSortAlgo::heap_adjust(vector<int> &nums, int i, int len)
{
    int child;
    int temp;
    for (; 2 * i + 1 < len; i = child)
    {
        child = 2 * i + 1;  // 子结点的位置 = 2 * 父结点的位置 + 1
                            // 得到子结点中键值较大的结点
        if (child < len - 1 && nums[child + 1] > nums[child])
            child++;
        // 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
        if (nums[i] < nums[child])
        {
            temp = nums[i];
            nums[i] = nums[child];
            nums[child] = temp;
        }
        else
            break;
    }
}
原文地址:https://www.cnblogs.com/VVingerfly/p/7401288.html