所有排序的时间复杂度和空间复杂度的比较

  • 冒泡排序
    数据结构 ---------- 数组
    最差时间复杂度 ---- O(n^2)
    最优时间复杂度 ---- 如果序列在一开始已经大部分排序过的话,会接近O(n)
    平均时间复杂度 ---- O(n^2)
    所需辅助空间 ------ O(1)
    稳定性 ------------ 稳定

  • 选择排序
    数据结构 ---------- 数组
    最差时间复杂度 ---- O(n^2)
    最优时间复杂度 ---- O(n^2)
    平均时间复杂度 ---- O(n^2)
    所需辅助空间 ------ O(1)
    稳定性 ------------ 不稳定

  • 插入排序 时间:最差O(n2)最优O (n),平均O(n2)空间:O(1)

  • 改进插入排序–二分插入排序 时间:数据结构 ---------- 数组
    最差时间复杂度 ---- O(n^2)
    最优时间复杂度 ---- O(nlogn)
    平均时间复杂度 ---- O(n^2)
    所需辅助空间 ------ O(1)
    稳定性 ------------ 稳定

    **void InsertionSortDichotomy(int A[], int n)
    {
    for (int i = 1; i )
    {
    int get = A[i]; // 右手抓到一张扑克牌
    int left = 0; // 拿在左手上的牌总是排序好的,所以可以用二分法
    int right = i - 1; // 手牌左右边界进行初始化
    while (left // 采用二分法定位新牌的位置
    {
    int mid = (left + right) / 2;
    if (A[mid] > get)
    right = mid - 1;
    else
    left = mid + 1;
    }
    for (int j = i - 1; j >= left; j–) // 将欲插入新牌位置右边的牌整体向右移动一个单位
    {
    A[j + 1] = A[j];
    }
    A[left] = get; // 将抓到的牌插入手牌
    }
    }

    int main()
    {
    int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大二分插入排序
    int n = sizeof(A) / sizeof(int);
    InsertionSortDichotomy(A, n);
    printf(“二分插入排序结果:”);
    for (int i = 0; i )
    {
    printf("%d ", A[i]);
    }
    printf(“n”);
    return 0;
    }**

  • 插入排序的跟进一步改进—希尔排序
    最差时间复杂度 ---- 根据步长序列的不同而 不同。已知最好的为O(n(logn)^2)
    最优时间复杂度 ---- O(n)
    平均时间复杂度 ---- 根据步长序列的不同而不同。
    所需辅助空间 ------ O(1)
    稳定性 ------------ 不稳定

    **void ShellSort(int A[], int n)
    {
    int h = 0;
    while (h // 生成初始增量
    {
    h = 3 * h + 1;
    }
    while (h >= 1)
    {
    for (int i = h; i )
    {
    int j = i - h;
    int get = A[i];
    while (j >= 0 && A[j] > get)
    {
    A[j + h] = A[j];
    j = j - h;
    }
    A[j + h] = get;
    }
    h = (h - 1) / 3; // 递减增量
    }
    }

    int main()
    {
    int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大希尔排序
    int n = sizeof(A) / sizeof(int);
    ShellSort(A, n);
    printf(“希尔排序结果:”);
    for (int i = 0; i )
    {
    printf("%d ", A[i]);
    }
    printf(“n”);
    return 0;
    }**

  • 归并排序
    数据结构 ---------- 数组
    最差时间复杂度 ---- O(nlogn)
    最优时间复杂度 ---- O(nlogn)
    平均时间复杂度 ---- O(nlogn)
    所需辅助空间 ------ O(n)
    稳定性 ------------ 稳定

    void Merge(int A[], int left, int mid, int right)// 合并两个已排好序的数组A[left…mid]和A[mid+1…right]
    {
    int len = right - left + 1;
    int *temp = new int[len]; // 辅助空间O(n)
    int index = 0;
    int i = left; // 前一数组的起始元素
    int j = mid + 1; // 后一数组的起始元素
    while (i right)
    {
    temp[index++] = A[i] // 带等号保证归并排序的稳定性
    }
    while (i mid)
    {
    temp[index++] = A[i++];
    }
    while (j right)
    {
    temp[index++] = A[j++];
    }
    for (int k = 0; k )
    {
    A[left++] = temp[k];
    }
    }

    void MergeSortRecursion(int A[], int left, int right) // 递归实现的归并排序(自顶向下)
    {
    if (left == right) // 当待排序的序列长度为1时,递归开始回溯,进行merge操作
    return;
    int mid = (left + right) / 2;
    MergeSortRecursion(A, left, mid);
    MergeSortRecursion(A, mid + 1, right);
    Merge(A, left, mid, right);
    }

    void MergeSortIteration(int A[], int len) // 非递归(迭代)实现的归并排序(自底向上)
    {
    int left, mid, right;// 子数组索引,前一个为A[left…mid],后一个子数组为A[mid+1…right]
    for (int i = 1; i 2) // 子数组的大小i初始为1,每轮翻倍
    {
    left = 0;
    while (left + i // 后一个子数组存在(需要归并)
    {
    mid = left + i - 1;
    right = mid + i 1;// 后一个子数组大小可能不够
    Merge(A, left, mid, right);
    left = right + 1; // 前一个子数组索引向后移动
    }
    }
    }

    int main()
    {
    int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 }; // 从小到大归并排序
    int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
    int n1 = sizeof(A1) / sizeof(int);
    int n2 = sizeof(A2) / sizeof(int);
    MergeSortRecursion(A1, 0, n1 - 1); // 递归实现
    MergeSortIteration(A2, n2); // 非递归实现
    printf(“递归实现的归并排序结果:”);
    for (int i = 0; i )
    {
    printf("%d “, A1[i]);
    }
    printf(“n”);
    printf(“非递归实现的归并排序结果:”);
    for (int i = 0; i )
    {
    printf(”%d ", A2[i]);
    }
    printf(“n”);
    return 0;
    }

  • 堆排序

数据结构 ---------- 数组
最差时间复杂度 ---- O(nlogn)
最优时间复杂度 ---- O(nlogn)
平均时间复杂度 ---- O(nlogn)
所需辅助空间 ------ O(1)
稳定性 ------------ 不稳定

  void Swap(int A[], int i, int j)
{
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
}
 
void Heapify(int A[], int i, int size)  // 从A[i]向下进行堆调整
{
    int left_child = 2 * i + 1;         // 左孩子索引
    int right_child = 2 * i + 2;        // 右孩子索引
    int max = i;                        // 选出当前结点与其左右孩子三者之中的最大值
    if (left_child  A[max])
        max = left_child;
    if (right_child  A[max])
        max = right_child;
    if (max != i)
    {
        Swap(A, i, max);                // 把当前结点和它的最大(直接)子节点进行交换
        Heapify(A, max, size);          // 递归调用,继续从当前结点向下进行堆调整
    }
}
 
int BuildHeap(int A[], int n)           // 建堆,时间复杂度O(n)
{
    int heap_size = n;
    for (int i = heap_size / 2 - 1; i >= 0; i--) // 从每一个非叶结点开始向下进行堆调整
        Heapify(A, i, heap_size);
    return heap_size;
}
 
void HeapSort(int A[], int n)
{
    int heap_size = BuildHeap(A, n);    // 建立一个最大堆
    while (heap_size > 1)           // 堆(无序区)元素个数大于1,未完成排序
    {
        // 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素
        // 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法
        Swap(A, 0, --heap_size);
        Heapify(A, 0, heap_size);     // 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn)
    }
}
 
int main()
{
    int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大堆排序
    int n = sizeof(A) / sizeof(int);
    HeapSort(A, n);
    printf("堆排序结果:");
    for (int i = 0; i )
    {
        printf("%d ", A[i]);
    }
    printf("n");
    return 0;
}
  • 快速排序
    数据结构 --------- 数组
    最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
    最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
    平均时间复杂度 ---- O(nlogn)
    所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)
    稳定性 ---------- 不稳定

    void Swap(int A[], int i, int j)
    {
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
    }

    int Partition(int A[], int left, int right)  // 划分函数
    {
        int pivot = A[right];               // 这里每次都选择最后一个元素作为基准
        int tail = left - 1;                // tail为小于基准的子数组最后一个元素的索引
        for (int i = left; i // 遍历基准以外的其他元素
        {
            if (A[i] // 把小于等于基准的元素放到前一个子数组末尾
            {
                Swap(A, ++tail, i);
            }
        }
        Swap(A, tail + 1, right);           // 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
                                            // 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
        return tail + 1;                    // 返回基准的索引
    }
     
    void QuickSort(int A[], int left, int right)
    {
        if (left >= right)
            return;
        int pivot_index = Partition(A, left, right); // 基准的索引
        QuickSort(A, left, pivot_index - 1);
        QuickSort(A, pivot_index + 1, right);
    }
     
    int main()
    {
        int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 }; // 从小到大快速排序
        int n = sizeof(A) / sizeof(int);
        QuickSort(A, 0, n - 1);
        printf("快速排序结果:");
        for (int i = 0; i )
        {
            printf("%d ", A[i]);
        }
        printf("n");
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值