数据结构篇-时间复杂度

基于我学的课程知识来写的这篇博客,希望大家能喜欢,之后会持续更新

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#include
#include
数据结构初阶
初阶用C语言实现基础的数据结构
针对C语言刚学完的情况下可以巩固C语言的语法知识
 数据结构是什么
 计算机存储、组织数据的方式
 指相互之间存在的一种或多种特定关系的数据元素的集合
 单一的数据结构我们不能掌握所有题目的解法,因此要学如线性表、树图、哈希等数据结构
 数据结构与算法不分家
 算法的定义:
 良好的计算过程,取一个或一组的值为输入,并产生出一个数或一组值作为输出。
 算法即为一系列的计算步骤,
 用来将输入数据转化成输出结果
 但是一个题目可以有多重算法,因此选择好算法一定会对将来的工作等有益处
 数据结构和算法的重要性:
 校园招聘笔试必考,校园招聘面试必考
 如何衡量一个算法的好坏?
 看代码执行的时间吗?
 错,原因:
 1.程序运行和编译环境和运行机器的配置有关系
 还与CPU的温度等也有关,
 如:同一个代码在老编译器会运行很慢,在新编译器运行很快,故不能衡量
 2.时间只能程序写好后测试,不能在程序前通过理论思想计算评估
 确实有计算程序运行时间的函数:clock()
 clock函数的使用
int main()
{
    int t1 = clock();
    for (int i = 0; i < 1000; i++)
    {
        for (int j = 0; j < 1000; j++)
        {
            int a = 1;
        }
    }
    int t2 = clock();
    printf("%d", t2 - t1);
    //如果把该程序运行几次
    //会发现每次程序的结果都不一样
    //clock函数的单位是毫秒
    //所以不能用该算式来算这个程序是否好
    return 0;

 }
 如果给出一个数组,要求数组的每个元素向右移动m个位置,而且最后一个元素放置到第一位,求:如何写代码
 分析:我们可以先把最后一个元素赋值给一个临时变量,然后其他元素从当前位置都移动一位,最后再把该变量赋值给这个数组下标为0的位置
 先进行倒数第二个元素的赋值
 再在外面写过while循环即可
 可以按如下写
int main()
{
    int n;
    scanf("%d", &n);
    int a[n];
    int num;
    scanf("%d", &num);
 for(int i = 0;i< n ;i++)
 {
 scanf("%d",&a[i]);
 }
 //    while (num--)
    {
        int b = a[n - 1];
        for (int i = n - 1; i > 0; i--)
        {
            a[i] = a[i - 1];
        }
        a[0] = b;
    }
    //这样就可以实现一些简单的数组移动
    //但是如果是num大于n的数,是不是也可以减去呢
    //而且这个如果输入的值很大,会爆int,这样也有风险,所以我们需要学习算法去解决这种问题
    //先把这个代码优化一下过程
    return 0;
}
int main()
{
    unsigned long long  n, num;
    long long int i;
    scanf("%lld %lld", &n, &num);
    long double a[n];
    for (i = 0; i < n; i++)
    {
        scanf("%llf", &a[i]);
    }
    if (num >= n)
    {
        num -= n;
    }
    //这样可以减少很多没用的事情
    while (num--)
    {
        long double b = a[n - 1];
        for (i = n - 2; i > 0; i--)
        {
            a[i] = a[i - 1];
        }
        a[0] = b;
    }
    //是不是这样就会比之前适用范围广了一些
    //但是,这个代码可以更加简化
    //如果num-=n之后num仅仅与n相差1,2,3这种小数时可以向左移动
    //只要num-=n之后num>=n/2都可以这样算,但是循环条件需要改变
    //当上述情况出现时,需要改变代码变为向左移动
    //这样是不是更简便呢?
    //这代码简化有很多方法,我们无法知道有多少种简化方法,可以根据算法的复杂度来看
    return 0;
}
 复杂度的概念:
 算法在编写成可执行程序后,运行时需要耗费时间资源和空间资源。
 因此。衡量一个算法的好坏一般是从时间和空间两个维度来衡量的
 即可分为时间复杂度和空间复杂度
 之后我将讲的是时间复杂度
 补充:复杂度在校招中考察也很常见
 定义:算法的时间复杂度是一个函数式:T(n)
 定量描述了该算法的运行时间,衡量程序的时间效率
 为什么不用程序的运行时间来判断呢,之前已经讲过了
 T(N)是什么?
 T(n)代表程序的执行次数
 我们计算的前提是假设每条语句的执行时间都是一样的
 那么执行次数和运行时间就是等比正相关了
 因此可以计算程序的运行次数来求算法复杂度
 提醒:n是一个变量,我们不知道它的值,它可理解为一个从0到无穷大的变量
 光说不练假把式
int main()
{
    unsigned long long n;
    int count = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            count++;
        }
    }
    for (unsigned long long int k = 0; k < 2 * n; k++)
    {
        count++;
    }
    for (int i = 0; i < 10; i++)
    {
        count++;
    }
    //可以看出第一个程序需要执行n*n次即为n^2
    //第二个程序需要执行n次
    //第三个程序需要执行10次
    //T(n)计算结果为n^2+2*n+10
    //但是时间复杂度只能用来表示输入条件对时间的影响趋势
    //因为n我们未赋值,所以我们可以看成一个一直变大到无穷大的数
    //我们给n赋值为10,100,1000发现n^2总是对结果影响最大
    //我们可以认为当n为无穷大的时候,除最高阶项外其余的项可以忽略不计
    //这时我们要用大O渐进表示法,用该种算法求出的结果放至O()中的括号中
    // 省略其他低阶项,为n^2
    // 所以写为O(n^2)
    //
    return 0;
}
 但是这种表示法不只有这一种规则
我们用题目来讨论
int main()
{
    unsigned long long int n;//我们要假设它是一个很大的数
    int count = 0;
    for (int k = 0; k < 2 * n; k++)
    {
        count++;
    }
    int m = 10;
    while (m--)
    {
        count++;
    }
    //按照之前的想法会变为2*n
    // 但是如果最高阶项有系数,我们都要改为1
    // 为什么呢?
    // 因为在n变为无穷大的时候前面的系数影响很小,所以不需要前面的系数
 哪怕n为200000000也要省略
    //最终表示为:O(n)
    return 0;
}
如果最高阶项为0怎么办
 这样都要写为O(1)
 这里面的1没有任何意思,都可以认为n对时间复杂度没影响
int main()
{
    unsigned long long int n, m;
    int count = 0;
    for (int k = 0; k < m; k++)
    {
        count++;
    }
    for (int k = 0; k < n; k++)
    {
        count++;
    }
    //很容易算出T(n)=n+m
    // 但是如何表示呢
    // 此时要分情况讨论
    // 如果m远大于n则O(m)
    // 反之O(n)
    // 因为我们需要的是那种很大的数,所以我们要分情况讨论
    //所以答案是O(m)或O(n)
    return 0;
}
const char* strchr(const char* str, int character)
{
    //计算character字符第一次出现的位置
    //并返回该位置的地址
    const char* p = str;//先把第一项的值传递给一个变量,如果没找到要返回地址的
    assert(p);//如果p未创建成功,则会结束编译
    while (*p != character)
    {
        if (*p == '\0')
        {
            return NULL;
        }
        p++;
    }
    //这种情况我们需要按最好最坏的两种情况来讨论
    //最好的情况就是第一个字符就是character
    // 则可写为O(1)
    // 最坏的情况就是找到最后一个字符或者最后一个字符之前的几个有限个字符
    // 可写为O(n)
    // 遇到这种情况时,我们需要写最坏的情况的答案
    //所以最终的答案是:O(n)
    return p;
}
int main()
{
    //冒泡排序
    int arr[10];
    int i, j;
    for (i = 0; i < 10; i++)
    {
        scanf("%d", &arr[i]);
    }
    for (i = 0; i < 9; i++)
    {
        int cha = 0;//判断是否有交换,如果没交换就直接结束循环了
        for (j = 0; j < 9 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                cha = 1;
            }
        }
        if (cha == 0)
        {
            break;
        }
        //让我们来分析一下这个程序
        // 我们如今只考虑最坏的情况,即为全部乱序(从大到小的排列)
        // 假设数组10个元素开始的顺序为 9 8 7 6 5 4 3 2 1 0
        // 一次比较后变为8976543210
        // 在进行一次比较变为8796543210
        // 这样要进行9次比较才可以排列好
        // 第二次排序就要排列8次
        // 内层循环次数从第一次到最后一次分别为(假设有n个元素)
        // n-1 n-2 n-3 ... 3 2 1
        // 可以看出是等差数列,因此可以这样求出
        // T(n)=(n-1)*n/2
        // 得出结果为O(n^2)
    }
    return 0;
}
int main()
{
    int cnt = 1;
    unsigned long long int n;
    while (cnt < n)
    {
        cnt *= 2;
    }
    //这种情况我们可以让n为2 4 8等值,求出此时执行的次数
    // 为1 2 3
    // 设执行次数为x次,则2^x=n
    // x=log2 n
    // x为n对2的对数
    // 但是当n为无穷大时,底数的大小对结果影响不大,因此可以不写底数
    // 写成O(log n)
    return 0;
}
long long fac(size_t n)
{
    if (0 == n)
    {
        return 1;
    }
    else
        return n * fac(n - 1);
    可以看出来最好的情况是一次,写为
     O(1)
     最坏的情况就是执行n+1次
     写为O(n)
     在阶乘递归算法中,时间复杂度即为递归调用次数(n次)
    因此为O(n)
}
//

你可能感兴趣的:(数据结构篇,数据结构,算法,c语言)