[C data structure] Introduction to data structure and algorithm complexity

Table of contents

1. Basic concepts

2. Time complexity

【2.1】Concept of time complexity

【2.2】Big O’s asymptotic representation

【2.3】Example time complexity calculation

3. Space complexity


1. Basic concepts

Data structure : A collection of data elements that have one or more specific relationships with each other. Logically it can be divided into linear structure, hash structure, tree structure, graph structure and so on.

Algorithm : A description of the steps to solve a specific problem. The code is expressed as a limited set of instruction sequences to solve a specific problem.

Algorithm complexity : time and space complexity, which measures the efficiency of the algorithm. During the execution of the algorithm, as the data size n increases, the time and space spent on the execution of the algorithm increase.

Common time complexity :

expression Big O notation equation
5201314 O(1) constant order
3n+4 O(n) linear order
3n^2+4n+5 O(n^2) square order
3log(2)n+4 O(logn) Logarithmic order
2n+3nlog(2)n+14 O(nlogn) nLogn order
n^3+2n^2+4n+6 O(n^3) cubic order
2^n O(2^n) Exponential order

Time complexity relationship of common algorithms : O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n!) < O( n^n)

[Comparison of complexity indicators]

[Complexity of the created data structure algorithm]

Big O plan Application examples
O(1) Array random access, hash table
O(logn) Binary search, binary heap adjustment, AVL, red-black tree search
O(n) linear search
O(nlogn) Heap sort, quick sort, merge sort
O(n^2) Bubble sort, selection sort, insertion sort
O(2^n) subset tree
O(n!) Arrangement tree

2. Time complexity

【2.1】Concept of time complexity

        Definition of time complexity: In computer science, the time complexity of an algorithm is a function that quantitatively describes the running time of the algorithm. The time it takes to execute an algorithm cannot be calculated theoretically. You can only know it if you put your program on the machine and run it. But do we need to test every algorithm on a computer?

        It is possible to test everything on a computer, but this is very troublesome, so the time complexity analysis method is introduced. The time an algorithm takes is proportional to the number of executions of its statements. The number of executions of basic operations in the algorithm is the time complexity of the algorithm.

        That is: finding the mathematical expression between a certain basic statement and the problem size N is to calculate the time complexity of the algorithm.

// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{
	int count = 0;
	for (int i = 0; i < N; ++i) // 循环次数为N^2
	{
		for (int j = 0; j < N; ++j)
		{
			++count;
		}
	}

	for (int i = 0; i < 2 * N; i++) // 循环次数为2 * N
	{
		++count;
	}

	int M = 10;
	while (M--) // 循环次数为10
	{
		++count;
	}

	printf("%d\n", count);
}
// Func1 执行的基本操作次数 :F(N) = (N^2) + (2 * N) + 10
// 比如:N = 10		F(N) = 130
// 比如:N = 100		F(N) = 10210
// 比如:N = 1000	F(N) = 1002010
// 上面N越大后面两项对整个表达式的影响是越来越小的
// 实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
// 最终以大概的次数:上面代码的时间复杂度是N^2

【2.2】Big O’s asymptotic representation

Big O notation : is a mathematical notation used to describe the asymptotic behavior of a function.

[Derivation of the big O order method]

  • Replace all addition constants in the running time with a constant 1. O1 does not mean 1 time, but a constant time.

  • In the modified number of runs function, only the highest order terms are retained .

  • If the highest-order term is present and is not 1, remove the constant multiplied by this term . The result is Big O order.

[After using the asymptotic representation of Big O, the time complexity of pushing Func1 is]

// Func1 执行的基本操作次数 :F(N) = (N^2) + (2 * N) + 10
// 比如:N = 10		F(N) = 130
// 比如:N = 100		F(N) = 10210
// 比如:N = 1000		F(N) = 1002010
// 结果:去掉(2*N)去掉(10) -> 时间复杂度是N^2

        Through the above, we will find that the asymptotic representation of Big O removes items that have little impact on the results, and expresses the number of executions concisely and clearly. In addition, the time complexity of some algorithms has the best, average and worst cases:

  • Worst case : maximum number of runs (upper bound) for any input size.

  • Average case : the expected number of runs for any input size.

  • Best case : minimum number of runs (lower bound) for any input size.

  • For example : Search for a data x in an array of length N.

  • Best case : found 1 time.

  • Worst case : Found N times.

  • Average situation : N/2 times found.

In practice, the general concern is the worst case operation of the algorithm!

【2.3】Example time complexity calculation

【Example】

// 计算Func2的时间复杂度?
void Func2(int N)
{
	int count = 0;
	for (int k = 0; k < 2 * N; ++k)// 循环次数:2 * N
	{
		++count;
	}

	int M = 10;
	while (M--) // // 循环次数:10
	{
		++count;
	}
	printf("%d\n", count);
}
// 正常为:(2 * N) + 10 
// 在此:10 影响不大, 2为固定值影响不大,影响时间复杂度的是N
// 以大O表示法:O(N)

【Example】

// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
	int count = 0;
	for (int k = 0; k < M; ++k) // 循环次数为:M次
	{
		++count;
	}
	for (int k = 0; k < N; ++k) // 循环次数为:N次
	{
		++count;
	}
	printf("%d\n", count);
}
// 正常为:M + N
// 在此:M 和 N 的准确值都是不知道的,所以都算在时间复杂度成员里
// 以大O表示法:O(M + N)

【Example】

// 计算Func4的时间复杂度?
void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k) // 循环次数为:100次
	{
		++count;
	}
	printf("%d\n", count);
}
// 正常为:100次
// 在此:N 没有参与任何东西
// 以大O表示法:O(1)

【Example】

// 计算strchr的时间复杂度?
const char * strchr(const char * str, int character); // O(N)

【Example】

// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
	assert(a);
	for (size_t end = n; end > 0; --end) // 循环次数为:等差数列
	{									 // N-1 N-2 N-3 N-4.... -> 1+2+3+N-1 = N(N-1)/2
		int exchange = 0;
		for (size_t i = 1; i < end; ++i)  
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}
// 正常为:
// 在此:实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N ^ 2)
// 以大O表示法:O(N^2)这是最坏的情况加
// 最好的情况下是O(N)

【Example】

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x) // O(N * Log(2)n)
{
	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;
}

【Example】

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N) // O(N)
{
	if (0 == N)
		return 1;
	return Fac(N - 1)*N;
}

【Example】

// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N) // O(2^N) -> 斐波那契数相当于是一个很垃圾的算法
{
	if (N < 3)
		return 1;
	return Fib(N - 1) + Fib(N - 2);
} 

// 优化
long long Fac(size_t N) 
{
	if (N < 3)
		return 1;

	long long f1 = 1, f2 = 1, f3;
	for (size_t i = 3; i <= N; i++)
	{
		f3 = f1 + f2;
		// 迭代
		f1 = f2;
		f2 = f3;
	}

	return f3;
}

3. Space complexity

Space complexity is also a mathematical expression, which is a measure of the amount of storage space an algorithm temporarily occupies during operation .

        Space complexity is not how many bytes of space the program occupies, because this is not very meaningful, so space complexity is calculated by the number of variables. The space complexity calculation rules are basically similar to the practical complexity, and the big O asymptotic notation is also used .

        Note: The stack space required when the function is running (storing parameters, local variables, some register information, etc.) has been determined during compilation, so the space complexity is mainly determined by the additional space explicitly applied for by the function at runtime.

【Example】

// 计算BubbleSort的空间复杂度?
void BubbleSort(int* a, int n) // 常数个额外空间,所以空间复杂度为 O(1)
{
	assert(a);
	for (size_t end = n; end > 0; --end)
	{
		int exchange = 0;
		for (size_t i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

【Example】

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n) // 动态开辟了N个空间,空间复杂度为 O(N)
{
	if (n == 0)
	{
		return NULL;
	}
		
	long long * fibArray = (long long *)malloc((n + 1) * sizeof(long long));

	fibArray[0] = 0;
	fibArray[1] = 1;
	for (int i = 2; i <= n; ++i)
	{
		fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
	}
	return fibArray;
}

【Example】

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N) // 递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)
{
	if (N == 0)
		return 1;
	return Fac(N - 1) * N;
}

Guess you like

Origin blog.csdn.net/lx473774000/article/details/132680431