Explanation of the complexity of data structure learning and sharing

1 Introduction

After we have finished learning all the content of C language, we can start learning our data structure now. If some readers also want to take the direction of C/C++ research and development, they can follow me to learn later.This article will be included in the columnData structure learning and sharing ,Will continue to update the content.


2. Data Structure Preface

2.1 What are data structures and algorithms?

  • Data structure is a way for computers to store and organize data, and refers to a collection of data elements that have one or more specific relationships with each other.
  • Algorithm: It is a well-defined calculation process that takes one or a set of values ​​as input and produces one or a set of values ​​as output. Simply put, an algorithm is a series of computational steps used to transform input data into output results.

2.2 The Importance of Data Structures and Algorithms

The current campus recruitment written test generally adopts the form of Online Judge (referred to as OJ questions). Generally, there are 20-30 multiple-choice questions and 3-4 programming questions. For the proportion of data structure and algorithm, we can refer to Tencent 2017 School Recruitment Development Written test paper for engineers . And we enter the interview stage after passing the written test, and HR often asks us some more professional questions such as:

  • Do you know about queues and stacks?
  • How to implement a queue with two stacks.
  • Determine whether two linked lists intersect.
  • How does learning STL work?
  • The principle of red-black tree, time complexity, etc.;
  • Hashmap related;
  • Hash tables and quick sort ideas

These are questions that belong to our data structures


2.3 How to learn data structure and algorithm well?

My suggestion is to smash the code, just knock it like this [狗头]

insert image description here
Of course, this is an exaggeration, but the dead code is true!

Another one, in our sharing about the single-linked list Oj question (single-linked list oj question sharing) , we have already emphasized that this part must draw pictures, and drawing analysis is also very important

2.4 Some articles and books

For more understanding of data structures, here I recommend an article on the importance of data structures and algorithms for programmers . I have almost learned data structures. I recommend everyone to read "Sword Pointer Offer" and "Programmer Code Interview Do the questions in the guide



3. Algorithm efficiency

How do we measure the quality of an algorithm? Is the algorithm with less code the better? For example, for the following Fibonacci sequence:

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

This code is very concise, but when we learn C language, we know that it is unwise to use recursion to find the Fibonacci sequence, because it has a very large amount of calculation, so the quality of the algorithm is not related to the amount of code :


3.1 Algorithm complexity

  • After the algorithm is compiled into an executable program, it needs to consume time resources and space (memory) resources when running. Therefore, to measure the quality of an algorithm, it is generally measured from two dimensions of time and space, that is, time complexity and space complexity.
  • Time complexity mainly measures how fast an algorithm runs, while space complexity mainly measures the extra space required for an algorithm to run. In the early days of computer development, computers had very little storage capacity. So I am very concerned about the space complexity. However, with the rapid development of the computer industry, the storage capacity of computers has reached a very high level. So we no longer need to pay special attention to the space complexity of an algorithm.

3.2 Investigation of complexity in interviews

We often encounter this kind of time and space constraints when doing OJ questions:

insert image description hereWe will encounter this kind of question not only in the written test stage, but also often asked during the interview



4. Time complexity

4.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 that algorithm. Theoretically speaking, the time it takes for an algorithm to execute cannot be calculated. Only when you put your program on the machine and run it can you know it. But do we need to test each algorithm on the computer? It is possible to test them all on the computer, but it is very troublesome, so there is an analysis method of time complexity. The time spent by an algorithm is proportional to the number of executions of the statements in it, and the number of executions of the basic operations in the algorithm is the time complexity of the algorithm.

In fact, we only need to find the mathematical expression between a certain basic sentence and the problem size N to calculate the time complexity of the algorithm

  • For example, the following code:
// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(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);
}

The number of times Func1 is executed: F(N)=N 2 +2*N+10

  • N=10,F(N)=130
  • N=100,F(N)=10210

4.2 Asymptotic representation of Big O

In practice, when we calculate the time complexity, we don't actually have to calculate the exact number of executions, ***But you only need to know the approximate number of executions, so here we use the asymptotic notation of big O.***We saw that the time complexity is O(1) is expressed in this way

Big O notation (Big O notation): is a mathematical notation used to describe the asymptotic behavior of a function.
Derive the big O order method:

  • Replace all additive constants in run time with the constant 1.

  • In the modified run count function, only the highest order term is kept

  • If the highest-order term exists and is not 1, remove the constant that is multiplied by this term. The result is big O order. (eg 2*n 2 is actually n 2 )

Using our Big O notation to simplify the time complexity of the above code is: O(N 2 )


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

  • Worst case: maximum number of runs for any input size (upper bound)
  • Average case: 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: 1 find

Worst case: N times to find

Average case: N/2 found

In practice, the general situation is concerned with the worst operation of the algorithm, so the time complexity of searching data in the array is O(N)



4.3 Examples of Judging Time Complexity

4.31 Example 1

// 计算Func2的时间复杂度?
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);
}

The number of runs is: 2*n+10, and the time complexity is: O(N).


4.32 Example 2

// 计算Func3的时间复杂度?
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);
}

The number of operations is: N+M, and the time complexity is: O(N+M)


4.33 Example 3

// 计算Func4的时间复杂度?
void Func4(int N)
{
    
    
 int count = 0;
 for (int k = 0; k < 100; ++ k)
 {
    
    
 ++count;
 }
 printf("%d\n", count);
}

The number of runs is 100, which is a constant time, and the time complexity: O(1).


4.34 Example 4 (bubble sort)

// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
    
    
 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;
 }
}

The number of runs is: (n-1)+(n-2)+(n-3)+...+1, which is equal to (n 2 -n)/2 times. The time complexity is O(N 2 )


4.35 Example 5 (Binary Search)

// 计算BinarySearch的时间复杂度?
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;
 else
 return mid;
 }
 return -1;
}

Let's see how many times it will run in the worst case by drawing a graph:

insert image description here

The time complexity is O(log 2 N) , so we say that the efficiency of binary search is very high, even if we want to find a person through binary search among 1.4 billion Chinese, the worst case only needs 31 times can be found.



5. Space complexity

5.1 Space Complexity Definition

  • Space complexity is also a mathematical expression, which is a measure of the size of the storage space temporarily occupied by an algorithm during operation

  • Space complexity is not how many bytes 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 (storage parameters, local variables, some register information, etc.) required by the function at runtime has been determined during compilation, so the space complexity is mainly determined by the additional space explicitly requested by the function at runtime.


5.2 Example of Judging Space Complexity

5.21 Example 1 (bubble sort)

// 计算BubbleSort的空间复杂度?
void BubbleSort(int* a, int n)
{
    
    
 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 1 uses a constant amount of extra space, so the space complexity is O(1)


5.22 Example 2 (Fibonacci of non-recursive solution)

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t 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 2 dynamically opens up N spaces, and the space complexity is O(N)


5.23 Example 3 (recursion)

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

Recursive calls are made N times, and N stack frames are opened up (N space is opened up), and each stack frame uses a constant amount of space. The space complexity is O(N)



6. Common complexity comparison

Number of runs the complexity Classification
5201314 O(1) constant order
3n+2 O(N) linear order
3n 2 +4n+5 O(N 2 ) square order
8log 2 (n)+4 O(log 2 N ) logarithmic order
10n+6nlog2(n)+22 O(N*log 2 N) NlogN order
2n +8 _ O(2 N ) Exponential order


7. Summary

This chapter is the beginning of the data structure. We introduced and became familiar with the big O asymptotic representation of time complexity and space complexity, laying the foundation for the following algorithm questions. In the following content, I will introduce common data storage modes for you. : Sequence list, single linked list, double linked list, stack, queue, binary tree, etc., so stay tuned!.

Guess you like

Origin blog.csdn.net/m0_61982936/article/details/130419904