数据结构与算法01 - 时间复杂度

什么是数据结构?

在内存中对数据进行管理。

算法效率包括:时间效率 + 空间效率

1、时间效率(时间与环境有关:例如硬件等)

算法中的基本操作的执行次数,为算法的时间复杂度!(抛掉环境因素)

算法的时间复杂度是一个函数!

示例1:计算++count语句共执行多少次?

void Func(int N)
{
	int count = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			count++;
		}
	}
	for (int k = 0; k < 2 * N; ++k)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	
	printf("%d\n", count);
}

Func1的执行次数为F(N) = N2 + 2*N + 10 

随着N的变大,后面两项的数值影响越来越小!

F(N)可以简化为F(N) = N2

1.1 大O的渐进表示法:

大O符号:适用于描述函数渐进行为的数学函数。(忽略对结果影响不大的项)

  • 用常数1取代运行时间中的所有加法常数;
  • 在修改后的运行次数函数中,只保留最高项;
  • 如果最高阶项存在且不是1,则去除与这个项目相乘的常数,得到的结果就是大O阶。

例如Func1的时间复杂度:O(N2)

示例2:计算++count语句共执行多少次?

void Func2(int N)
{
	int count = 0;
	for (int k = 0; k < 2 * N; ++k)
	{
		count++;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	printf("%d\n", count);

}

func2的时间复杂度O(N)

示例3:计算++count语句共执行多少次?

void Func3(int N, int M)
{
	int count = 0;
	for (int k = 0; k < M; ++k)
	{
		++count;
	}
	for (int k = 0; k < N; k++)
	{
		++count;
	}
	printf("%d\n", count);
}

func3的时间复杂度O(M+N)

如M远远大于N,则时间复杂度为O(M)

示例4:计算Func4的时间复杂度

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; k++)
	{
		++count;
	}
	printf("%d\n", count);
}

 func3的时间复杂度O(1) --- O(1指的是常数次,不是1次)

示例5:计算strchr的时间复杂度

strchr 函数的主要功能是遍历字符串 str,并查找 character 在其中第一次出现的位置。如果找到了,则返回该位置的指针,如果没有找到,则返回 NULL

char* my_strchr(const char* str, int character) {
	// 遍历字符串,直到找到指定字符或到达字符串末尾  
	while (*str != '\0') {
		if (*str == (char)character) {
			return (char*)str;  // 返回指向匹配字符的指针  
		}
		str++;  // 移动到下一个字符  
	}
	// 如果没有找到,返回NULL  
	return NULL;
}

 该搜索与字符的个数和位置有关系,因此我们需要考虑最好,最坏和平津三种情况:

  • 最坏情况:任意输入规模的最大运行次数(上界)
  • 平均情况:任意输入规模的期望运行次数
  • 最好情况:任意输入规模的最小运行次数(下界)

一般取最坏情况!

实例6:冒泡排序

void BubbleSort(int* a, int n)
{
	assert(a);
	for (size_t end = 0; end > 1; --end)
	{
		int exchange = 0;
		for (size_t i = 0; i < end; ++i)
		{
			if (a[i-1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange = 0);
		break;
	}
	

}

冒泡排序的最好效率为O(N)

最差的效率为O(N2),一般直接取O(N2);不加指示位一定为O(N2)。

实例7:二分查找

int BinarySearch(int* a, int n, int x)
{
	assert(a);
	int begin = 0;
	int end = n - 1;
	while(begin <= end)
	{
		int mid = begin + ((end - begin) >> 1);
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid - 1;
		else
			return mid;
	}
	return -1;

}

最好为:O(1);最坏为O(log2N)

前提条件为数组有序!O(log2N)效率远远大于O(N)!!! --- 红黑树效率好!

由于对数不好写,在时间复杂度中可以将log2N = logN(只有以2为底才简化)

实例8:计算Fac的时间复杂度

long long Fac(size_t N)
{
	if (0 == N)
		return 1;

	return Fac(N - 1) * N;

}

 从Fac(N)到Fac(0)共计算了N + 1次,时间复杂度为O(N)

实例9:计算斐波那契递归Fib的时间复杂度

long long Fib(size_t N)
{
	if (N < 3)
		return 1;
	return Fib(N - 1) + Fib(N - 2);
}

 时间复杂度为O(2的n次方)(等比数列 - 缺失的常数)

一般常见的时间复杂度排序:

O(1) > O(logN) > O(N^2) > O(N*logN) > O(N^2) > O(N^3) > O(2^N)

实例10、消失的数字

. - 力扣(LeetCode)

解题思路:

  • 排序 + 二分查找  O(N*logN)
  • 异或:a ^ a = 0; a ^ 0 = 0;(异或支持交换律)
  • 0 ~ N求和  - 数组的值

接下来使用异或来完成:

int missingNumber(int* nums, int numsSize)
{
    int val = 0;
    int i = 0;
    for (i = 0; i<numsSize; i++)
    {
        val = val ^ nums[i]; // 取出num[0]~num[n-1]的异或的和
    }
    for (i = 0; i<=numsSize; i++)
    {
        val = val ^ i;  // 依次从i异或num[0]~num[n-1]
    }
    return val;
}

时间复杂度为O(N)!

使用求和来计算:

int missingNumber(int* nums, int numsSize)
{
    int sum = numsSize * (numsSize+1) / 2;
    int i = 0;
    for (i = 0; i < numsSize ;i++)
    {
        sum = sum - nums[i];
    }
    return sum;
}

时间复杂度也为O(N)! 

实例11、转轮数组

. - 力扣(LeetCode)

如果考虑每次移动一个数,需要移动K次,那么时间复杂度为O(K*N)(下节有)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一道秘制的小菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值