声明:此为老师上课笔记相关整理,如有错误之处请指出
¦ 引言
现在学的这门数据结构,是为了给大三的大数据打基础,是使用java语言来学习
Java分为:JavaSE JavaEE JavaME
JavaSE:桌面应用,不是Java的市场,C或C++的使用更为强大;
JavaEE:Java企业级开发(Java的使用更为广泛);
JavaME:嵌入式开发(e.g. 智能家电:通过在硬件当中植入软件系统,完成更加智能的操作),同样也不是Java的市场,C或C++的使用更为强大;
关于数据库的使用
我们之前学的SQL,包括大三的Oracle,都不足以支撑更加庞大的数据,这时候,Hadoop就有了很大的作用,Hadoop开发之前,普通的老百姓是不能运用这样庞大的数据存储,除非是国家级项目的使用。
Google公司有许多关于产品的论文,他们不会将公司的产品开源,但是他们会将产品的原理用论文形式公开出来。有一个名不见经传的资深JavaEE开发师,他很喜欢看这些关于Google的产品论文,当他看完了关于搜索引擎的有关三篇论文,激发了灵感,他在想如何用Java也能模拟出相关的搜索引擎,然后他就每天下班回家写一些些代码,两年之后Hadoop诞生。
Hadoop的名字是因为开发的这个作者完成Hadoop的代码时,他两岁的儿子正在玩他最心爱的小象,那个小象被他儿子取名为Hadoop。
我们学习数据框架的许多理论基础时建立在数学的线性代数与概率统计之上,所有一定要打好基础。
¦ 算法相关
— 什么是算法?
— 算法是 解决问题的思想,方法,步骤
举例:
1 public class Demo01 { 2 //不使用中间变量temp,如何交换a,b 3 public static void main(String[] args) { 4 int a = 10; 5 int b = 20; 6 System.out.println("a="+a+",b="+b); 7 /* 8 int temp = a; 9 a = b; 10 b = temp;*/ 11 a = a + b; 12 b = a - b; 13 a = a - b; 14 System.out.println("a="+a+",b="+b); 15 16 } 17 }
使用中间变量或者不使用中间变量,都是各自的算法
>算法的概念
算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后再调用。
算法是独立存在的一种解决问题的方法和思想。
对于算法而言,实现的语言并不重要,重要的是思想。
算法可以有不同的语言描述实现版本(如C描述、C++描述、Python,Java描述等),我们现在是在用Java语言进行描述实现。
- 举一些利用思想的例子:
- 1.简单的排序
-
1 package demo; 2 3 import java.util.Arrays; 4 5 public class Demo02 { 6 //选择排序 7 public static void main(String[] args) { 8 int[] arr = {3,2,45,78,66,70,23,99,5}; 9 //排序前 10 //Arrays是一个工具,点出toString方法可以直接将数组数据全部打印出来 11 System.out.println(Arrays.toString(arr)); 12 //将数组中的元素从小到大排列 13 for (int i = 0; i < arr.length-1; i++) { 14 for (int j = i+1; j < arr.length; j++) { 15 if(arr[i]>arr[j]){ 16 //升序大数放后面 17 int temp = arr[i]; 18 arr[i] = arr[j]; 19 arr[j] = temp; 20 } 21 } 22 } 23 //排序后 24 System.out.println(Arrays.toString(arr)); 25 } 26 }
- 计算出简单排列的时间
- 补充:
- * random 可以随机产生double的数
- 下面两种方法都能表示random的用法
- random本身只有0~1之间的小数,如果乘以10000,就会变成随机抽取的0~9999的数值,因为random本身是取不到1的,所以乘以10000之后,也取不到10000
-
1 package demo; 2 3 import java.util.Random; 4 5 public class Demo04 { 6 //产生0~9999的整数 7 public static void main(String[] args) { 8 //将原本是double的值强转成int类型 9 //int d = (int) (Math.random()*10000); 10 //System.out.println(d); 11 Random r = new Random(); 12 //1~10000 13 int num = r.nextInt(10000)+1; 14 System.out.println(num); 15 } 16 }
- 2.我们肉眼可见进行插入排序算法速度好像挺快,但如果试试十万个数值呢,这个排序所需要的时间呢?
- 整体思路:
- 将之前写的简单排序在这里写成了sort的方法;
- System.currentTimeMillis();这个方法是用来计算 计算机第一个操作系统开始的时间,也就是从1970年0时0分到现在的时间长度,以毫秒为单位,这个数值很长,要用long来存储;
- 在排序之前,计算一次时间,排序之后,再计算一次时间,end-start=排序使用的时间
-
1 import java.util.Arrays; 2 3 public class Demo03 { 4 public static void main(String[] args) { 5 int[] arr = new int[100000]; 6 for (int i = 0; i < arr.length; i++) { 7 arr[i] = (int) (Math.random()*100000); 8 } 9 //排序前 10 //System.out.println(Arrays.toString(arr)); 11 //将数组中的元素从小到大排列 12 //记录排序前的时间 13 long start = System.currentTimeMillis(); 14 /**1 15 * sort(arr); 16 * */ 17 /**2 18 * Arrays.sort(arr); 19 * */ 20 sort(arr); 21 //Arrays.sort(arr); 22 long end = System.currentTimeMillis(); 23 System.out.println("排序用时:"+(end-start)+"ms"); 24 //排序后 25 //System.out.println(Arrays.toString(arr)); 26 } 27 28 public static void sort(int[] arr){ 29 //将数组中的元素从小到大排列 30 for (int i = 0; i < arr.length-1; i++) { 31 for (int j = i+1; j < arr.length; j++) { 32 if(arr[i]>arr[j]){ 33 //升序大数放后面 34 int temp = arr[i]; 35 arr[i] = arr[j]; 36 arr[j] = temp; 37 } 38 } 39 } 40 } 41 }
- 这里使用了两种测试方式
- 1.自己写的sort方法,测试出用时31226ms,大概有31秒多
- 2.而另一种排序方法,则是Java自带的Arrays.sort()方法,测试出用时大概12ms
- 这里,就可以体现出来优化的思想
- 3.问题的提出:
1000以内的3个数 i,j,k,满足 i的平方加j的平方等于k的平方
同时 i + k + j = 1000
列举出所有的可能性 - 我们用两种方法来解决问题,并试着对比一下各自解决问题的时间
- 第一种:按照题意来写出代码
- 第二种:优化
- 我们在同一个代码框里面展示出来
-
1 public class Demo06 { 2 /** 3 * 问题的提出: 1000以内的3个数 i,j,k,满足 i的平方加j的平方等于k的平方 4 同时 i + k + j = 1000 5 列举出所有的可能性 6 7 * @param args 8 * i^2 + j^2 = k^2 9 */ 10 public static void main(String[] args) { 11 //开始时间 12 long start = System.currentTimeMillis(); 13 /* 14 for (int i = 1; i <=1000; i++) { 15 for (int j = 1; j <=1000; j++) { 16 for (int k = 1; k <=1000; k++) { 17 if(i*i+j*j==k*k && i+j+k==1000){ 18 System.out.println("i="+i+",j="+j+",k="+k); 19 } 20 } 21 } 22 } */ 23 /* 24 1000*1000*1000*2 25 n*n*n*2 26 T(n) = 2n^3 27 O(T(n)) = n^3 28 O(n^3)*/ 29 30 //优化 31 for (int i = 1; i <=1000; i++) { 32 for (int j = 1; j <=1000-i; j++) { 33 int k = 1000-i-j; 34 if(i*i+j*j==k*k){ 35 System.out.println("i="+i+",j="+j+",k="+k); 36 } 37 } 38 } 39 /* 40 1000*1000*3 41 T(n) = n*n*3 = 3*n^2 42 O(T(n)) = n^2*/ 43 //结束时间 44 long end = System.currentTimeMillis(); 45 System.out.println("用时:"+(end-start)+"ms"); 46 } 47 }
-
优化前
i=200,j=375,k=425
i=375,j=200,k=425
用时:546ms
优化后
i=200,j=375,k=425
i=375,j=200,k=425
用时:0ms -
对于同一问题,我们给出了两种解决算法,在两种算法的实现中,我们对程序执行的时间进行了测算,发现两段程序执行的时间相差悬殊(2248ms相比于1ms),由此我们可以得出结论:实现算法程序的执行时间可以反应出算法的效率,即算法的优劣。
单靠时间值绝对可信吗?
- >算法效率的衡量
- 执行时间反应的算法效率
-
假设我们将第二次尝试的算法程序运行在一台配置古老性能低下的计算机中,情况会如何?很可能运行的时间并不会比在我们的电脑中运行算法一的214.583347秒快多少。
单纯依靠运行的时间来比较算法的优劣并不一定是客观准确的!
程序的运行离不开计算机环境(包括硬件和操作系统),这些客观原因会影响程序运行的速度并反应在程序的执行时间上。那么如何才能客观的评判一个算法的优劣呢?
-
时间复杂度与“大O记法”
-
分析算法时,存在几种可能的考虑:
- 算法完成工作最少需要多少基本操作,即最优时间复杂度
- 算法完成工作最多需要多少基本操作,即最坏时间复杂度
- 算法完成工作平均需要多少基本操作,即平均时间复杂度
-
对于最优时间复杂度,其价值不大,因为它没有提供什么有用信息,其反映的只是最乐观最理想的情况,没有参考价值。
对于最坏时间复杂度,提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。
对于平均时间复杂度,是对算法的一个全面评价,因此它完整全面的反映了这个算法的性质。但另一方面,这种衡量并没有保证,不是每个计算都能在这个基本操作内完成。而且,对于平均情况的计算,也会因为应用算法的实例分布可能并不均匀而难以计算。
因此,我们主要关注算法的最坏情况,亦即最坏时间复杂度。
- 我们可以在上面的代码中发现,绿色的注释就是大O记法的具体解释
- 现在重新将代码贴出来
-
1 public class Demo06 { 2 /** 3 * 问题的提出: 1000以内的3个数 i,j,k,满足 i的平方加j的平方等于k的平方 4 同时 i + k + j = 1000 5 列举出所有的可能性 6 7 * @param args 8 * i^2 + j^2 = k^2 9 */ 10 public static void main(String[] args) { 11 //开始时间 12 long start = System.currentTimeMillis(); 13 /* 14 for (int i = 1; i <=1000; i++) { 15 for (int j = 1; j <=1000; j++) { 16 for (int k = 1; k <=1000; k++) { 17 if(i*i+j*j==k*k && i+j+k==1000){ 18 System.out.println("i="+i+",j="+j+",k="+k); 19 } 20 } 21 } 22 } */ 23 /* 24 1000*1000*1000*2 25 n*n*n*2 26 T(n) = 2n^3 27 O(T(n)) = n^3 28 O(n^3)*/ 29 30 //优化 31 for (int i = 1; i <=1000; i++) { 32 for (int j = 1; j <=1000-i; j++) { 33 int k = 1000-i-j; 34 if(i*i+j*j==k*k){ 35 System.out.println("i="+i+",j="+j+",k="+k); 36 } 37 } 38 } 39 /* 40 1000*1000*3 41 T(n) = n*n*3 = 3*n^2 42 O(T(n)) = n^2*/ 43 //结束时间 44 long end = System.currentTimeMillis(); 45 System.out.println("用时:"+(end-start)+"ms"); 46 } 47 }
- 第一个算法中,for循环执行了1000次,那么写下 1000
- 继续往下看,下面的两层for循环同样执行了1000次 得到:1000*1000*1000
- 最后一个for循环里面,有一个if语句,就算做执行一次
- 再加上if语句中有一个打印语句,再加一次 最后的算术公式为:1000*1000*1000*2
- 将1000用n代替,则为 n*n*n*2 →2n^3
- 记为函数T(n)=2n^3
- 而在大O记法下,将n无限放大,就可以直接忽略常数2
- 得到:O(T(n))=n^3
- 同理:第二个算法得到大O记法:O(T(n))=n^2
- 我们可以比较一下,n^3>n^2 得出:优化后的算法效率更高
浙公网安备 33010602011771号