一、数据结构涵盖的内容:
二、算法的基本概念:
1、算法的概念:
Algorithm,是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每一条指令表示一个或者多个操作。
2、算法的特性:
- 有穷性:指令序列是有限的
- 确定性:每条语句的含义明确,无二义性
- 可行性:每条语句都应在有限的时间内完成
- 输入:零个或者多个输入
- 输出:一个或者多个输出
3、算法与程序的区别:
程序:
(program)程序是软件开发人员根据用户需求开发的、用程序设计语言描述的适合计算机执行的指令(语句)序列。
程序包含的四个要素:
- 数据结构
- 算法
- 程序设计方法
- 编程语言
程序与算法不同。程序是算法用某种程序设计语言的具体实现。程序可以不满足算法的有穷性,比如操作系统也是一种程序,如果你愿意,你的电脑可以一直开着,保持操作系统的运行。
比如:
while(true)
{
...
} //是一段程序,但不是一个算法
4、程序、算法、软件之间的关系:
程序:算法的计算机实现。用某种编程语言实现。
算法:表示问题的解。
软件:程序+文档
如下图所示:
程序设计=数据结构+算 法
三、数据类型:
1、数据类型:
是指一个值的集合和定义在集合上的操作集合。例如:java的整数类型(Integer),就不仅仅表示整数数值的集合,还包括对整数类型进行的操作集合,加、减、乘、除、模等操作。
我们通常所指的数据类型是某种高级语言支持的基本数据类型。 比如java的基本数据类型:int、double、float、char等等。
Java中的数据类型:
2、抽象数据类型:
一个数学模型以及定义在这个模型上的一组操作(由用户定义,用以表示应用问题的数据模型)。
看起来抽象数据类型和数据类型的定义基本相同。不同点在于:数据类型通常是指某种高级语言的,而抽象数据类型用户重新设计,一种概念。这里的"抽象"的含义在于数学抽象。
- 原子类型:比如整型
- 固定聚合类型:比如复数,两个实数确定的次序构成
- 可变聚合类型:比如汽车类型,构成的成分是不确定的。(因为小轿车、大卡车的构成是不同的)
抽象数据类型抽象的层次越高,那么可复用性也越高。比如:java中的Object是对所有对象的抽象,那么就可以作为所有类的父类。
四、抽象数据类型的表示(代码举例):
- C语言使用结构体
- Java语言使用类
下面分别用C语言与java语言,来定义学生抽象数据类型。已知学生的信息如下:
学号:111222
姓名:生命壹号
性别:男
出生日期:1991-11
专业:电子与通信工程(计算机方向)
电子邮箱:smyhvae@163.com
1、用C代码定义一个学生类:
test1.c:
#include <stdio.h>
//日期结构体
typedef struct
{
int year;//年
int month;//月
int day;//日
}Date;
//学生结构体
typedef struct
{
char sid[20];//学号
char name[20];//姓名
char gender;//性别
Date birthday;//出生日期
char contact[50];//联系方式
}Students;
void PrintStudentsInfo(Students s)
{
printf("学号:%s\n",s.sid);
printf("姓名:%s\n",s.name);
printf("性别:%c\n",s.gender);
printf("出生日期:%d-%d-%d\n",s.birthday.year,s.birthday.month,s.birthday.day);
printf("联系方式:%s\n",s.contact);
}
int main()
{
Students s1;//生成一个学生对象
Date d1;
d1.year = 1995;
d1.month = 6;
d1.day =30;
strcpy(s1.sid,"S0001");
strcpy(s1.name,"张三丰");
strcpy(s1.contact,"西安市高新四路50号");
s1.gender = 'M';
s1.birthday = d1;
PrintStudentsInfo(s1);
getch();
return 0;
}
运行效果:
2、用Java代码定义一个学生类:
(1)Student.java:
package test1;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by smyhvae on 2015/8/12.
*/
public class Student {
String num; //学号
String name; //姓名
char sex; //性别
Date birthday; //出生日期
String contact; //联系方式
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-mm-dd"); //将Date日期转化为String字符串打印出来
return "Student{" +
"num='" + num + '\'' +
", name='" + name + '\'' +
", sex=" + sex +
", birthday=" + sdf.format(birthday) +
", contact='" + contact + '\'' +
'}';
}
}
(2)JavaTest.java:(给学生类赋值并打印出来)
package test1;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
/**
* Created by smyhvae on 2015/8/12.
*/
public class JavaTest {
public static void main(String[] args) throws ParseException {
Student s = new Student();
s.setNum("111222");
s.setName("生命壹号");
s.setSex('男'); //这里面赋值可以用中文
s.setContact("smyhvae@163.com");
Calendar calendar = Calendar.getInstance();
calendar.set(1991, 11, 28);
Date date = calendar.getTime();
s.setBirthday(date);
System.out.println(s.toString());
}
}
运行效果如下:
五、算法的设计目标:
1、算法的设计目标:
(1)正确性:满足具体问题的解,基本目标。
(2)可读性:有利于人去理解算法。
(3)健壮性:输入非法数据,能适当做出处理,不产生莫名其妙的输出。
(4)高效性:包括时间的高效性(时间复杂度)和空间的高效性(空间复杂度)。
2、算法性能指标:
(1)算法的时间效率也称为时间复杂度。
(2)算法的空间效率也称为空间复杂度。
(3)同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。
(4)算法时间的高效性和空间的高效性通常是矛盾的。所有一般只会取一个平衡点。(这里体现的是一种哲学思想,研究计算机,不就是研究时间和空间的概念嘛,鱼与熊掌不可兼得哦)
(5)通常我们假设程序运行在内存中,且内存容量足够使用,所以更多的是讨论时间复杂度。
六、算法的时间复杂度:
算法的时间复杂度反映了算法执行的时间长短。度量一个算法在计算机上执行的时间通常有两种方式:
1.事后统计法:
2.事前分析法:(常用)
编写算法使用的高级语言
编程产生的机器语言代码质量
机器指令执行速度
问题规模
注:算法的时间复杂度是由最深层嵌套语句的频度决定的。
2、O()函数:
表示算法的时间效率与算法所处理的数据元素个数n函数关系的最常用函数,即O()函数。
定义:
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。看下图便知:
情况一:T(n)与问题规模n无关
当算法的时间复杂度T(n)与问题规模n无关时,此时算法的时间复杂度T(n)=O(1)。
情况二:T(n)与问题规模n有关
例1:设数组a和b在前面部分已经赋值,求如下两个n阶矩阵相乘算法的时间复杂度:
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
c[i][j]=0; //基本语句1
for(k=0;k<n;k++)
{
c[i][j]=c[i][j]+a[i][k]*b[k][j];//基本语句2
}
}
}
上方代码中:
例2 :设n为如下算法处理的数据元素个数,求算法时间复杂度。代码如下:
for(i=1;i<=n;i=i*2)
{
System.out.println(i);
}
分析:
例3:分析冒泡排序算法的时间复杂度。代码如下:
//冒泡排序算法
public static void bubbleSort(int[] data) {
if (data == null) {
return;
}
for (int i = 0; i < data.length - 1; i++) {
boolean flag = false;
for (int j = 0; j < data.length - 1 - i; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
flag = true;
}
}
if (!flag) {
return;
}
}
}
时间复杂度分析:
(1)最佳情况下,冒泡排序算法只需要遍历一次就能确定数组已经排序好了,此时的时间复杂度为O(n);
(2)最差情况下,需要进行n-1次遍历,则需进行n(n-1)/2次比较和记录移动,此时冒泡排序算法的时间复杂度T(n)=O(n2)。
3、时间复杂度的大小关系:
以下六种计算算法时间的多项式是最常用的。其关系为:
指数时间的关系为:
当n取很大的值时,指数时间算法和多项式时间算法在所需时间上非常悬殊。
小知识:
NP(nondeterministic polynomial)难问题:是指算法复杂度难以用多项式表示的问题,算法复杂度通常是n的指数级,常规算法很难求解。
七、算法的空间复杂度:
空间复杂度是指算法在运行期间所需要的内存空间的数量级。记作:S(n)=O(f(n)),其中n为问题的规模(或大小)。
注:由于大部分算法的空间复杂度问题并不严重,并且算法的空间复杂度分析方法和算法的时间复杂度分析方法基本相同,所以一般数据结构只讨论算法的时间复杂度,不讨论算法的空间复杂度。
代码举例:分析如下算法的空间复杂度
static void reserse(int[] a,int[] b)
{
int n= a.length;
for(int i=0;i<n;i++)
{
b[i]=a[n-1-i];
}
}
上方代码中,当程序调用reserse(a,b)函数时,要分配的内存空间包括:引用a,引用b,局部变量n和局部变量i;
因此f(n)=c;其中c为常量。所以该算法的空间复杂度S(n)=O(1)。
八、总结:
算法的时间复杂度和两个因素有关:算法中的最大嵌套循环层数;最大嵌套循环结构中每次循环的次数。
一般来说,具有多项式时间复杂度的算法是可以接受的;具有指数(不是对数)时间复杂度的算法,只有当n足够小时才可以使用。一般效率较好的算法要控制在O(N)或者O(log2 N)
1、用C代码定义一个学生类:
test1.c: