数据结构与算法学习笔记1.绪论

代码规范

小驼峰命名法命名变量;大驼峰命名法命名类名、属性名等。

代码留白:(整体风格)

void moveZeros(vector<int>& nums){
	int slowIndex = 0;
	for (int fastINdex = 0; fastIndex < nums.size(); fastIndex++){
		if (nums[fastIndex] != 0) {
		nums[slowIndex++] = nums[fastIndex];
		}
	}
	for  (int i= slowIndex; i < nums.size(); i++){
		nums[i] = 0;
	}
}

数据结构与算法

1.绪论

1.1抽象数据类型(ADT)

定义格式:

ADT 抽象数据类型名{

​ Data

​ 数据对象的定义

​ 数据元素之间逻辑关系的定义

​ Operation

​ 操作1

​ 初始条件

​ 操作结果描述

​ 操作2

​ ······

​ 操作n

}ADT 抽象数据类型名

圆抽象数据类型定义(伪代码):
ADT Circle{
	数据对象:D = {r,x,y|r,x,y均为实数}
	数据关系:R = {<r,x,y>|r是半径,<x,y>是圆心坐标}
	基本操作:
	Circle(&C,r,x,y)
			操作结果:构造一个圆。
	double Area(C)
			初始条件:圆已存在
			操作结果:计算面积
	double Circumference(C)
			初始条件:圆已存在
			操作结果:计算周长
	······

}ADT Circle

1.2 算法和算法分析

算法的定义

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

算法的特性:

  • 有穷性、确定性、可行性、输入、输出。

算法设计的要求:

  • 正确性(Correctness) 可读性(Readablility) 健壮性(又称鲁棒性)(Robustness) 高效性(Efficiency)

算法的度量方法

事后统计方法(不科学、不准确)、事前分析估算方法。

算法的效率

  • 时间效率:指的是算法所耗费的时间
  • 空间效率:指的是算法执过程中所耗费的储存空间
1.时间效率度量
算法举例(两个n*n的矩阵相乘):
for(int i=1;i<=n;i++)        //执行n+1次
{
	for(int j=1;j<=n;j++)    //n(n+1)次
	{
		c[i][j]=0;           //n*n次
		for(int k=0;k<n;k++)  //n*n*(n+1)次
		{
			c[i][j]=c[i][j]+a[i][k]*b[k][j];//n*n*n次
		}
	}
}

上述算法时间消耗为:
T ( n ) = 2 n 2 + 3 n 2 + 2 n + 1 T(n)=2n^2+3n^2+2n+1 T(n)=2n2+3n2+2n+1
时间复杂度的渐进表示法主要比较它们的数量级;(时间复杂度,上述算法的时间复杂度为它的时间消耗公式的数量级)如下:
T ( n ) = O ( n 3 ) T(n)=O(n^3) Tn=O(n3)
数量级分类:
在这里插入图片描述

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

  • 常数阶->对数阶->线性阶 >线性对数阶->平方阶->立方阶->K次方阶->指数阶->阶乘阶
2.空间效率度量

例:将一维数组a中的n个数逆序放到原数组中

算法一:原地工作,空间复杂度为常数阶

for(int i=0;i<n/2;i++)
{
	t = a[i];
	a[i] = a[n-i-1];
	a[n-i-1] = t;
}

算法二:空间复杂度为线性阶

for(int i = 0;i<n;i++)
	b[i]=a[n-i-1];
for(int i = 0;i<n;i++)
	a[i] = b[i];
3.扩展:递归算法的时间复杂度分析

面试题:求x的n次方


1)最直观的方法是通过一个for循环求出结果,代码如下:(该算法时间复杂度为O(n))

int function1(int x, int n) {
	int result = 1; //注意任何数的0次方都等于1
	for (int i = 0; i < n; i++) {
		result = result * x;
	}
	return result;
}

2)使用递归算法解决:

该算法每次递归n都做以此减1的操作,那么就是递归了n次,时间复杂度是O(n),每次执行一个乘法操作,而乘法操作的时间复杂度是一个常数项O(1),所以这段代码的时间复杂度是O(n)(O(1))=O(n)

int function2(int x, int n) {
	if (n == 0) {
		return 1;  //return 1,同样是因为任何数的0次方都等于1
	}
	return function2(x, n-1) * x;
	
}

3)二叉树型递归算法:(这段代码的时间复杂度又是多少呢?)

int function3(int x, int n) {
	if (n == 0){
		return 1;
	}
	if (n % 2 == 1) {
		return function3(x, n/2) * function3(x, n/2)*x;
	}
    return function3(x, n/2) * function3(x, n/2);
}

首先看递归了多少次。可以把递归的次数抽象为一颗满二叉树,用一颗满二叉树来表示这个算法,如图:
在这里插入图片描述

​ 当前这棵二叉树就是求x的n次方(为了方便表示n为偶数),当n为16的时候,执行了多少次乘法运算操作呢?这棵树上的每一个节点就代表一次递归并执行了一次相乘操作,所以执行了多少次递归操作,就是看这棵树上有多少个节点。这棵满二叉树的节点数量是
2 3 + 2 2 + 2 1 + 2 0 = 15 2^3+2^2+2^1+2^0=15 23+22+21+20=15
可以发现,这其实就是等比数列的求和公式。(int n = 1 % 2; // n为1,int n = 1/2; //n为0)

如果求x的n次方,即设有m层,就有
2 m + 2 m − 1 + . . . + 2 0 = 2 m + 1 − 1 2^m+2^{m-1}+...+2^0 = 2^{m+1}-1 2m+2m1+...+20=2m+11
个树节点,又(2的m+1次方等于n)
m = l o g 2 n − 1 m = log_2n-1 m=log2n1
得,总节点数为n-1个。

此时,这个递归算法的时间复杂度依然是O(n)。

4)记忆递归算法:

int function4(int x, int n) {
	if (n == 0) {
		return 1;
	}
	int t = function4(x, n/2);//相当于function3,这里是把递归操作抽取出来
	if (n % 2 == 1) {
		return t*t*x;
	}
	return t*t;
}

这里只有一个递归调用,而且每次递归操作的数据规模都除以2,所以这里一共调用了
l o g 2 n log_2n log2n
次,每次递归都是一次乘法操作,所以这个递归算法的时间复杂度为O(logn)。

4.扩展:递归算法的空间复杂度分析
  1. 递归算法的时间复杂度 = 每次递归的时间复杂度 * 递归次数
  2. 递归算法的空间复杂度 = 每次递归的空间复杂度 * 递归深度

例题:求斐波那契数列的性能分析

1.递归算法

int fibonacci(int i) {
	if (i <= 0) return 0;
	if (i == 1) return 1;
	return fibonacci(i-1) + fibonacci(i-2);
}
	测量输入n时候,这段递归求斐波那契数列代码的耗时:
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
using namespace chrono;
int fibonacci(int i) {
       if(i <= 0) return 0;
       if(i == 1) return 1;
       return fibonacci(i - 1) + fibonacci(i - 2);
}
void time_consumption() {
    int n;
    while (cin >> n) {
        milliseconds start_time = duration_cast<milliseconds >(
            system_clock::now().time_since_epoch()
        );

        fibonacci(n);

        milliseconds end_time = duration_cast<milliseconds >(
            system_clock::now().time_since_epoch()
        );
        cout << milliseconds(end_time).count() - milliseconds(start_time).count()
            <<" ms"<< endl;
    }
}
int main()
{
    time_consumption();
    return 0;
}

2.优化递归算法

int fibonacci(int first, int second, int n) {
	if (n <= 0) {
		return 0;
	}
	if (n < 3) {
		return 1;
	}
	else if (n == 3) {
		return first + second;
	}
	else {
		return fibonacci(second, first + second, n-1);
	}
}

测量这段递归求斐波那契数列代码的耗时:

#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
using namespace chrono;
int fibonacci_3(int first, int second, int n) {
    if (n <= 0) {
        return 0;
    }
    if (n < 3) {
        return 1;
    }
    else if (n == 3) {
        return first + second;
    }
    else {
        return fibonacci_3(second, first + second, n - 1);
    }
}

void time_consumption() {
    int n;
    while (cin >> n) {
        milliseconds start_time = duration_cast<milliseconds >(
            system_clock::now().time_since_epoch()
        );

        fibonacci_3(1, 1, n);

        milliseconds end_time = duration_cast<milliseconds >(
            system_clock::now().time_since_epoch()
        );
        cout << milliseconds(end_time).count() - milliseconds(start_time).count()
            <<" ms"<< endl;
    }
}
int main()
{
    time_consumption();
    return 0;
}

3.普通方法

int fibonacci(int n) {
	int a = 0, b = 1;
	while (n--) {
		int c = a + b;
		a = b, b = c;
	}
	return a;
}

各种方法性能分析:

求斐波那契数时间复杂度空间复杂度
递归算法O(2^n)O(n)
优化递归算法O(n)O(n)
普通方法O(n)O(1)

1.3 课后例题

1.1 选择问题:寻找N个元素中的第K个最大值。下面代码令K = N/2 。

#include <iostream>
using namespace std;
#include<vector>
#include<algorithm>

#define ArrayNum 10
#define ResultNum (ArrayNum/2)

int main()
{	
	// 从原始数组中取出ResultNum各元素,然后进行降排序
	vector<int> v;
	int array[ArrayNum] = { 9, 3, 10, 2, 25, 7, 8, 14, 17, 12 };
	cout << "排序前:" << endl;
	for (int i = 0; i < ArrayNum; i++)
	{
		cout<<array[i]<<" ";
		v.push_back(array[i]);
	}
	
	sort(v.begin(), v.end(), greater<int>());//排序算法,降序
	cout << endl;
	cout << "排序后:"<<endl;
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	cout << "第k个最大值为:" << v[ResultNum-1]<<endl;
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值