基础数据结构和算法1:简介
1、数据结构
1.1 数据结构是什么?
数据结构是由数据和结构两方面组成,例如:
学生信息:
No. | 姓名 | 年龄 | 性别 |
---|---|---|---|
1 | 张三 | 21 | 男 |
2 | 李四 | 22 | 男 |
3 | 王五 | 23 | 女 |
数据就是姓名、年龄和性别,结构就是姓名、年龄和性别的关系。
数据结构指的是数据与数据之间的逻辑关系
(1)计算机存储、组织数据的方式
(2)相互之间存在一种或多种特定关系的数据元素的集合
1.2 数据结构有什么用?
解决问题,如何高效(多快好省)的从已知数据求解未知数据
1.3 数据结构分类
No. | 分类 | 特点 | e.g. |
---|---|---|---|
1 | 集合结构 | 杂乱无章无序关系 | 班级座次 |
2 | 线性结构 | 一对一前驱后继顺序关系 | 糖葫芦 |
3 | 树形结构 | 一对多祖先后代层次关系 | 族谱 |
4 | 图状结构 | 多对多错综复杂网状结构 | 铁路路线图 |
2、算法
2.1 算法是什么?
算法指的是解决特定问题的步骤和方法。
2.2 算法有什么用?
解决问题,如何高效(多快好省)的从已知数据求解未知数据。
2.3 如何判断算法的好坏?
对于一个问题的算法来说,之所以称之为算法,首先它必须能够解决这个问题(称为准确性)。其次,通过这个算法编写的程序要求在任何情况下不能崩溃(称为健壮性)。
如果准确性和健壮性都满足,接下来,就要考虑最重要的一点:通过算法编写的程序,运行的效率怎么样。
运行效率体现在两方面:
(1)算法的运行时间(称为“时间复杂度”)
(2)运行算法所需的内存空间大小(称为“空间复杂度”)
2.3.1 时间复杂度
1、时间复杂度是什么?
(1)算法的运行时间受硬件、系统等多个因素影响。我们使用一个与环境无关的方式评价算法执行速度:时间复杂度
(2)时间复杂度主要度量基本操作重复执行的次数,是输入规模和基本操作的数量关联,随着输入规模扩大的增长量
2、如何表示时间复杂度?
用O记号表示算法的时间性能
P(多项式)
No. | 表示 | 级别 |
---|---|---|
1 | O(1) | 常数(Constant) |
2 | O(log_2n) | 对数(Logarithmic) |
3 | O(n) | 线性(Linear) |
4 | O(nlog_2n) | 线性对数(Linear Logarithmic) |
5 | O(n^2) | 平方(2 Square) |
6 | O(n^2) | 立方(3 Square) |
NP(非确定多项式)
No. | 表示 | 级别 |
---|---|---|
1 | O(2^n) | 指数(Exponential) |
2 | O(n!) | 阶乘(Factorial) |
3 | O(n^n) | n次方(n Square) |
常用的时间复杂度从小到大的排序
O(1) < O(log_2n) < O(n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
2.3.2 空间复杂度
1、空间复杂度是什么?
空间复杂度是指运行完一个程序所需内存的大小
2、如何表示空间复杂度
一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据外,还需要对一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。程序执行时所需存储空间包括一下两部分:
(1)固定部分,这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
(2)可变空间。这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。
3、如何计算时间复杂度?
(1)找出基本语句(执行次数最多的语句)
最内层循环的循环体
(2)计算基本语句的执行次数的数量级
(3)只保留最高次幂,忽略低次幂和最高次幂的系数
用大O记号表示算法的时间性能
No. | 语句 | 时间复杂度 |
---|---|---|
1 | 顺序语句、分支语句 | O(1) |
2 | 循环语句 | O(n^2) |
3 | 递归 | O(log_2n)、O(2^n) |
3.1 O(1)
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
printf("%d\n",n);
}
3.2 O(n)
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
int count=0;
for(int i=0;i<n;i++){
printf("%d\n",++count);
}
}
3.3 O(n^2)
int n;
scanf("%d",&n);
int count = 0;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
printf("%d\n",++count);
}
}
3.4 O(n^3)
int n;
scanf("%d",&n);
int count = 0;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
for(int k=0;k<n;++k){
printf("%d\n",++count);
}
}
}
3.5 O(log_2n)
int n;
scanf("%d",&n);
int count = 0;
for(int i=0;i<n;i*=2){
printf("%d\n",++count);
}
3.6 O(2^n)
int n;
scanf("%d",&n);
int count = 0;
for(int i=0;i<pow(2,n);++i){
printf("%d\n",++count);
}
3.7 O(n!)
long long factorial(int n){
int res = 1;
for(int i=1;i<n;++i){
res*=i;
}
return res;
}
int n;
scanf("%d",&n);
int count = 0;
for(int i=0;i<factorial(n);++i){
printf("%d\n",++count);
}
4、线性结构
数据结构中最常用最简单的结构是线性结构。
线性结构,又称线性表。逻辑结构上数据元素之间存在一个对一个的相邻关系。线性结构是n个数据元素的有序(次序)集合,它有下列几个特征:
1.集合中必存在唯一的一个"第一个元素";
2.集合中必存在唯一的一个"最后的元素";
3.除最后元素之外,其它数据元素均有唯一的"后继";
4.除第一元素之外,其它数据元素均有唯一的"前驱"。
5、练习
1、未出现的数字
从非负整数序列 0, 1, 2, …, n 中给出包含其中 n 个数的子序列,请找出未出现在该子序 列中的那个数。
输入描述:
输入为 n+1 个非负整数,用空格分开。
其中:首个数字为非负整数序列的最大值 n,后面 n 个数字为子序列中包含的数字。
输出描述:
输出为 1 个数字,即未出现在子序列中的那个数。
示例:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n = 0;
scanf("%d",&n);
int sum = 0;
for(int i=0;i<n;++i){
int n = 0;
scanf("%d",&n);
sum += n;
}
int sum_n = n*(n+1)/2; // 等差数列求和公式
printf("%d\n",sum_n - sum);
return 0;
}
2、两个有序数组的归并输出
输入一个整数n(n <= 10000)和n个整数a[i],保证这n个整数已按照从小到大进行排序。然后输入一个整数m(m <= 10000),和m个整数b[j],保证这m个整数已按照从小到大进行排序。
将两组数归并后输出。
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
int a[n];
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int m;
scanf("%d",&m);
int b[m];
for(int j=0;j<m;j++){
scanf("%d",&b[j]);
}
int c[n+m];
int i=0,j=0,k=0;
while(i<n && j <m){
if(a[i] < b[j]){
c[k++] = a[i++];
}else if(a[i] == b[j]){
c[k++] = a[i++];
j++;
}else{
c[k++] = b[j++];
}
}
while(i<n){
c[k++] = a[i++];
}
while(j<m){
c[k++] = b[j++];
}
int count =k;
for(k=0;k<count;k++){
printf("%d\n",c[k]);
}
return 0;
}