DataStructure_2.Algorithm

本文介绍了算法的基本概念,包括其定义、特征及设计要求。同时深入探讨了算法效率的度量方法,如事后统计与事前分析估算,并详细解释了时间复杂度的概念及其重要性。此外,还提供了常见时间复杂度级别的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2.1 算法(是解决特定问题求解步骤的描述)在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

2.2 算法五个基本特征{输入,输出,有穷性,确定性,可行性}

2.2.1 输入输出:算法至少有0个输入,必须有输出。

2.2.2 有穷性:算法在执行有限的步骤后,自动结束而不会出现死循环,且每一个步骤在可接受的时间内完成

2.2.3 确定性:算法的每一个步骤都有确定的意义,不会出现二义性,即无歧义,相同的输入只会得出唯一的输出结果

2.2.4 可行性:算法的每一步必须是可行的,都能通过执行有限次数完成,即意味着可以被转换为程序并上机运行。

2.3 算法的设计{正确性,可读性,健壮性,时间效率高和存储量低}

2.3.1 健壮性:当输入数据不合法的时候算法也能做出相关处理,而不是产生异常或莫名其妙的结果。

2.3.2 时间效率高和存储量低:花最短的时间最少的钱办最大的事。

2.4 算法效率的度量方法{事后统计,事前分析估算}

2.4.1 事后统计方法:通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,以确定

效率高低。但是,受硬件性能程序必须先编好测试数据设计困难影响,导致这种方法有很大缺陷。

2.4.2 事前分析估算方法:依据统计方法在程序编写前对算法进行估算。测定运行时间最可靠的方法就是对运行的时间有消耗

的基本操作的执行次数。运行时间与这个执行次数成正比

例:求1+2+…+100的值

算法1

int i,sum=0,n=100; //执行1次

for(i=1;i<=n;i++){ //执行n+1

sum=sum+i; //执行n

}

cout<<sum; //执行1

/*共执行2n+3次*/

算法2

int sum=0,n=100; //执行1

sum=(1+n)*n/2; //执行1

cout<<sum; //执行1

  

/*共执行3次*/

2.5 函数的渐进增长

  • 给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n > N,f(n)总是比g(n)大,那么,我们说f(n)的渐近增长快于g(n)。

    f(n)和g(n)或更多函数之间进行比较时的几点便利条件(注:以下条件仅在函数之间比较时才成立,单个函数不可以直接如此化简)

    • 可以忽略加法常数 e.g. (2n+3)(2n)
    • 与最高次项相乘的常数并不重要 e.g. (4n+8)(n) (2n^2+1)(n^2)
    • 最高此项指数越大后期(即随着n的不断增加)函数的增长会越快
    • 判断一个算法的效率时,函数中的常数和其它次要项常常可以忽略,而应该关注最高此项的阶数
  • 综上,某个算法,随着n的增大,他会越来越优于另一算法,或差于另一算法。这就是事前估计方法的理论依据,通过算法时间复杂度来估算算法时间效率。

2.6 算法时间复杂度

  • 算法的时间复杂度,也就是算法的时间量度,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。

    这样用大写O( )来体现算法时间复杂度的记法,我们称之为大O记法。

    一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法

  • 推导大O阶方法
    • 用常数1取代运行时间中的所有加法常数

        

       

  1. 在修改后的运行次数函数中,只保留最高阶项
  2. 如果最高阶项存在且不是1,则去除与这个项相乘的常数。得到的结果就是大O
  • level one 常数级

    例如算法2

int sum=0,n=100; //执行1

sum=(1+n)*n/2; //执行1次

cout<<sum; //执行1

/*共执行3*/

  

step1:运行次数函数为f(n)=3,故用常数1取代3。

step2:只保留最高阶项,而由于它没有最高阶项,故时间复杂度为O(1)

注:单纯不包含在循环结构中的分支结构(if else switch)时间复杂度也是O(1)

  • level two 对数级

    例如while(count < n){count = count *3//O(1)时间复杂度}

    count每次3倍,x次后大于n,即3^x = n,即x=log3n,故循环的时间复杂度为O(logn)

  • level three 线性级

    关键是分析循环结构运行情况

    例如for(i=0;i<n;i++){cout<<"test!"//O(1)时间复杂度}

    函数体中的O(1)时间复杂度的代码一共要执行n次,故时间复杂度为O(n)

  • level four 平方级

    嵌套循环

    • 1 两个O(n)的循环嵌套

      for(i=0;i<n;i++){

      for(j=0;j<n;j++){

      cout<<"test!"//O(1)时间复杂度

      }

      } //时间复杂度为O(n^2=n*n)

    • 2 O(m),O(n)嵌套

      for(i=0;i<m;i++){

      for(j=0;j<n;j++){

      cout<<"test!"//O(1)时间复杂度

      }

      } //时间复杂度为O(n*m)

    • 例3 相互嵌套

      for(i=0;i<n;i++){

      for(j=i;j<n;j++){

      cout<<"test!"//O(1)时间复杂度

      }

      }

i

内层循环执行次数

0

n

1

n-1

2

n-2

3

n-3

4

n-4

n-1

1

共执行n+(n-1)+(n-2)+(n-3)+…+1=(n*(n+1))/2=n^2/2+n/2

推导big O三步法替换加法常数(由于没有加法常数故略过);保留最高阶项(剩下n^2/2);去除与这个最高阶项相乘的常数(剩下n^2)

故时间复杂度为O(n^2)

  • 用的时间复杂度所耗费的时间从小到大依次是

    O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

2.7 最坏情况和平均情况

最坏情况运行时间是运行时间最坏就是这么长,不会再长了。通常,除非说明,我们提到的运行时间都是指最坏情况运行时间

平均运行时间是期望的运行时间,但很难分析得到,都是实际运行数据后估算出来的。

### 使用大数据技术计算学生各科目平均分数 为了高效地计算大量学生成绩数据中的每门课程的平均分,可以借助 Hadoop 和 Spark 这样的大数据处理框架。以下是具体方法: #### 数据准备 假设输入的数据是一个结构化的文件(如 CSV 文件),其中每一行表示一名学生的成绩记录,字段可能包括 `student_id`、`course_name` 和 `score`。 #### 处理流程概述 通过 MapReduce 或 Spark 的操作模式,将原始数据转换为键值对形式 `(course_name, score)` 并对其进行聚合运算以求得平均分。 --- #### 基于 Hadoop 的解决方案 在 Hadoop 中可以通过编写自定义 Mapper 和 Reducer 来完成此任务。 ##### Mapper 阶段 Mapper 将输入的成绩数据解析成键值对的形式,其中键为课程名称 (`course_name`),值为该课程对应的分数 (`score`)。 ```java public class AverageScoreMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text courseName = new Text(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); if (fields.length >= 3 && !fields[1].isEmpty() && !fields[2].isEmpty()) { // 跳过表头和其他异常情况 courseName.set(fields[1]); // 设置课程名为key int score = Integer.parseInt(fields[2]); context.write(courseName, new IntWritable(score)); } } } ``` ##### Combiner 阶段 Combiner 是可选组件,在本地节点上先做一次汇总减少网络传输量。 ```java public class AverageScoreCombiner extends Reducer<Text, IntWritable, Text, IntWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; int count = 0; for (IntWritable val : values) { sum += val.get(); count++; } context.write(key, new IntWritable(sum)); // 输出总分为value } } ``` ##### Reducer 阶段 Reducer 收集来自不同 Mapper 的相同 Key 对应的所有 Value 列表,并最终算出平均数。 ```java public class AverageScoreReducer extends Reducer<Text, IntWritable, Text, DoubleWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { double totalSum = 0d; long totalCount = 0L; for (IntWritable v : values) { totalSum += v.get(); totalCount++; } double average = totalSum / totalCount; context.write(key, new DoubleWritable(average)); } } ``` --- #### 基于 Spark 的解决方案 相比 Hadoop,Spark 提供更简洁高效的 API 接口来进行类似的操作。 ##### RDD 方法 使用 Resilient Distributed Datasets (RDDs),我们可以快速实现同样的功能。 ```scala val input = sc.textFile("hdfs://path/to/input") // 解析并映射到 (course_name, score) val scoresByCourse = input.map(line => line.split(",")) .filter(_.length >= 3) // 确保有至少三列数据 .map(parts => (parts(1), parts(2).toDouble)) // 计算每个科目的总分和计数值 val totalsAndCounts = scoresByCourse.aggregateByKey((0.0, 0))( seqOp = (acc, value) => (acc._1 + value, acc._2 + 1), combOp = (acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2) ) // 求取平均分 val averages = totalsAndCounts.mapValues{ case (total, num) => total / num }.collect() averages.foreach(println(_)) ``` ##### DataFrame/SQL 方法 如果希望进一步简化代码,则可以直接利用 Spark SQL 功能。 ```python from pyspark.sql import SparkSession spark = SparkSession.builder.appName("AverageScores").getOrCreate() df = spark.read.csv("hdfs://path/to/input", header=True, inferSchema=True) result_df = df.groupBy('course_name').agg({'score': 'avg'}) result_df.show() ``` 上述 Python 版本展示了如何加载 CSV 文件并通过内置函数直接获取结果[^2]。 --- ### 总结 无论是选择传统的 Hadoop MapReduce 方式还是现代化的 Spark 工具链路,都能够满足对学生多学科成绩统计的需求。考虑到易用性和性能优势,建议优先考虑 Spark 实现方案[^1]。 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值