数据结构与算法学习之分析排序算法时间的复杂度

24 篇文章 0 订阅
13 篇文章 0 订阅

本人的数学功底不是很好,如果有说得不好的地方请大佬指正。

这里需要已经对这些排序算法理解了,才能听得懂我讲得复杂度问题.我不会再介绍他的具体动作.

排序源码传送门

时间复杂度:

最内层循环的语句执行的次数。

冒泡排序:

  • 最优状态:

已经是我们所需的序列,外层for循环 第一轮,利用flag,因为已经是目标序列,所以第一轮内层for循环执行了一轮后,无任何元素交换,因此通过flag直接中断,跳出最外层循环,此时我们只执行了n-1次内层的for循环。
结论:最优状态时冒泡排序的时间复杂度为O(n)。

如果我们没有利用flag,那么最优状态会退化成最差状态 O(n^2),使用了flag可以优化冒泡排序。

  • 最坏状态:

逆序状态:每次进行比较都要进行交换,因此里面的循环必须走完,flag永远无法满足,所以外层循环也是走完n-1次,因此受到外层循环的影响,内层循环次是一个以a1=1,d=1,an=n-1的等差数列和n*(n+1)/2,此时我们就得出n*(n+1)/2是内层循环的次数,所以内层循环里的语句需要执行n*(n+1)/2次数,然后我们取数量级 O(n^2);

//冒泡排序
void bubble_sort(int length) {
    for (int i = 1; i < length; i++)
    {
        bool flag = 1;
        for (int j = 1; j <= length - i; j++)
        {
            if (a[j] < a[j + 1])
            {
                swap(a[j], a[j + 1]);
                flag = 0;
            }
        }
        if (flag)    break;
    }
}

简单选择排序

每次从剩余的集合中选一个最大的,也就是说每次选择要遍历一遍剩余的集合.
所以遍历的次数是一个以a1=1,d=1,an=n-1的等差数列和n*(n+1)/2,此时我们就得出n*(n+1)/2是内层循环的次数,所以内层循环里的语句需要执行n*(n+1)/2次数,然后我们取数量级 O(n^2),这一点与冒泡排序的最差情况的计算方法类似.
简单选择排序无最优最差,或者说两个都是一样的,那么平均时间复杂度也是一样的.

//选择排序
void select_sort(int length) {
    for (int i = 1; i < length; i++) {
        a[0] = a[i];
        int minarr = i;
        for (int j = i; j <= length; j++)
        {
            if (a[j] < a[0])
            {
                minarr = j;
                a[0] = a[j];
            }
        }
        swap(a[i], a[minarr]);
    }
}

直接插入排序

最优状态:

已经是我们所需的序列,那么我们从2-n,每一次进入第一层循环我们都不会满足内层循环的条件,因为假设我们已经是1 2 3 4 5
我们找到2这个元素准备往左边的有序序列中插入,因为已经按照我们的序列拍好了,所以我们不需要移动位置.在外层的最后一句中插入.因此我们只用了外层循环的n-2次遍历了一遍,所以我们的时间复杂度是O(n).

最坏情况:

逆序.每次都要移动到有序的集合中的最前面,也就是每次都遍历了一遍有序集合.外层循环的i影响了内层循环的次数,使得内层循环的次数也成了一个等差数列,可以看成以a1=1,d=1,an=n-1的等差数列和n*(n+1)/2,此时我们就得出n*(n+1)/2是内层循环的次数,所以内层循环里的语句需要执行n*(n+1)/2次数,然后我们取数量级 O(n^2),这一点与冒泡排序的最差情况的计算方法类似.

void my_inset_sort(int *a, int n) {
	int	j;
	for (int i = 2; i <=n; i++) {
		a[0] = a[i];
		for (j = i; a[j-1] <a[0];j--) {		
			a[j] = a[j - 1];		
		}
		a[j] = a[0];
	}
}

快速排序

最坏情况:

退化成冒泡的最坏情况O(n^2).
这里默认我们的pivot是从最左边的元素开始获取,我们每次的pivot定位后都是在最右边,这样的话我们的区间长度每次-1,所以也是一个以a1=1,d=1,an=n-1的等差数列和n*(n+1)/2,此时我们就得出n*(n+1)/2是内层循环的次数,所以内层循环里的语句需要执行n*(n+1)/2次数,然后我们取数量级 O(n^2).

最优情况:

每次pivot结束后都能正好在中间,左右两区间的大小相等,那么我们就以2的倍数减少我们的分的区间次数。2^k=n , k=log2底n 也就是log2底n次分区间,每次分区间我们还要相当于对整个区间的长度n遍历一遍,两者的积即为所求,
nlog2底n

//pivot
int portion(int l, int h) {
    a[0] = a[l];
    while (l < h)
    {
        while (l < h && a[h] >= a[0])  h--;
        a[l] = a[h];
        while (l < h && a[l] <= a[0])  l++;
        a[h] = a[l];
    }
    a[h] = a[0];
    return h;
}

//快排
void quick_sort(int l, int length) {
    if (l < length) {
        int pivot = portion(l, length);
        quick_sort(l, pivot - 1);
        quick_sort(pivot + 1, length);
    }
}

归并排序

归并排序与快速排序的时间复杂度的计算方法差不多。
归并排序是先分区间后操作,因此不会受到类似于快排的pivot的影响.
直接以2倍的速度分区间,也就是形成一个2^k=n , k=log2底n 也就是log2底n次分区间,每次分区间我们还要相当于对整个区间的长度n遍历一遍,两者的积即为所求,
nlog2底n

//归并操作
void my_merge(int l, int h, int length) {
    int* b = (int*)malloc(sizeof(int) * length);
    for (int i = 1; i <= length; i++)
    {
        b[i] = a[i];
    }
    int m = (l + h) / 2;
    int i, j, k;
    for (i = l, j = m + 1, k = l; i <= m && j <= h; k++)
    {
        if (b[i] < b[j]) {
            a[k] = b[i++];
        }
        else {
            a[k] = b[j++];
        }
    }
    while (i <= m)
    {
        a[k++] = b[i++];
    }
    while (j <= h)
    {
        a[k] = b[j++];
    }
}

//归并排序
void merge_sort(int l, int h, int length) {
    if (l < h) {
        int m = (l + h) / 2;
        merge_sort(l, m, length);
        merge_sort(m + 1, h, length);
        my_merge(l, h, length);
    }
}

堆排序

建堆过程是O(n),这里知道就好
循环 n -1 次,每次都是从根节点往下循环查找,所以每一次时间是 log2底n
(n-1)*log2底n -> nlog2底n
因为O(nlog2底n) >O(n)
我们取最大的O(nlog2底n)

//调整堆
void adjust_heap(int key, int length) {
    a[0] = a[key];
    for (int i = key * 2; i <= length; i *= 2)
    {
        if (i < length && a[i] < a[i + 1])
        {
            i++;
        }
        if (a[0] >= a[i])
        {
            break;
        }
        else {
            a[key] = a[i];
            key = i;
        }
    }
    a[key] = a[0];
}

//建堆
void build_heap(int length) {
    for (int i = length / 2; i > 0; i--)
    {
        adjust_heap(i, length);
    }
}

//堆排序
void heap_sort(int length) {
    build_heap(length);
    for (int i = length; i > 0; i--)
    {
        swap(a[1], a[i]);
        adjust_heap(1, i - 1);
    }
}


求点赞👍👍👍
原创不易,点赞容易。
您的鼓励就是我的最大动力!!!。
本篇博客到此结束,谢谢大家观看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值