数据结构成神篇1-初学数据结构

目录

一,引子

一,例一:

二,例二:

三,例三:

四,抽象数据类型

二,算法

1,什么是算法?

2.算法复杂度

3,在分析一般算法的效率时,我们经常会关注下面的两种复杂度:

4,渐近表示法:

 三,应用实例:最大子列和问题

 1,穷举所有子列和,从中找出最大值

2,少用一个for循环

3,分而治之

4,在线处理

今天我们开始数据结构的学习,当然,这个有些概念是十分抽象的,只看文章是不一定能懂的,或者说会耗费不少的时间。所以我会持续在B站上面更新讲解视频,都是自己的一些理解和想法。会拿出来和大家一起分享,都是免费的。原创不易,希望大家可以三连支持一下,也希望能给大家带来进步。

博客地址:喜欢吃豆的博客_CSDN博客-C++笔记,C语言笔记,C/C++课外闲谈领域博主https://blog.csdn.net/m0_63309778?spm=1000.2115.3001.5343

喜欢吃豆 的个人主页 - 动态 - 掘金 (juejin.cn)https://juejin.cn/user/251108556018989

仓库地址:

喜欢吃豆 (i-like-beans) - Gitee.comhttps://gitee.com/i-like-beans

B站视频地址:

忘了吃豆的个人空间_哔哩哔哩_bilibilihttps://space.bilibili.com/1032401779?spm_id_from=333.1007.0.0 谢谢大家的支持了。

大家学习这个数据结构之前需要有一本数据结构(第二版C语言)书是最好的。

一,引子

首先,我们先考虑一下,什么是数据结构?我们既然要学习数据结构,那我们就应该知道,数据结构是什么呢?

其实这个问题至今仍然没有一个官方的解答,但是有很多大牛都给出了自己的见解。

有多少人可以看懂?在这里,所有的老师都会选择使用例子来让大家更好的理解。 

一,例一:

书上给的例子是,如何在书架上面摆放图书?

大家可能会有各种各样的想法,因为很少有事情是绝对的,很多事情都有多种解决方法的。

首先,我们来看一看这个问题,这个问题从本质上来说就是不科学的。

准确来说是不够严谨的,我并没有规定书的规模,还有书架的款式。

那如果我们不那么严谨,我们来考虑以下,有什么样的解决方法?

1,随便放,见缝插针。但是找书的时候会很痛苦,你甚至都不能确定你是否有这本书

2,按书名的拼音的首字符来排序,但是如果这样的话我如果要插入新书,就会对后面所有的书本进行移动

3,把书架划分为几块区域,每块区域指定摆放某种类别的图书,在每种类别内,按照书名的拼音字母顺序排放。但是我们并不知道每一类书有多少本,需要多大的一个区域来存放这些书,会造成空间的一个浪费。

所以我们可以知道解决问题方法的效率,跟数据的组织方式有关。

二,例二:

写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1~N的所有整数。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//打印从1~N的全部整数
void PrintN(int N) {
	int i;
	for (i = 1; i <= N; i++) {
		printf("%d\n", i);
	}
}

//递归实现
void PrintN(int N) {
	if (N > 0) {
		PrintN(N - 1);
		printf("%d\n", N);
	}
}

int main() {
	int N;

	scanf("%d", &N);
	PrintN(N);


	return 0;
}

这里我给出了 两个版本,一个是用循环语句实现,还有一个是以递归的形式实现,似乎两种方法都可以完成任务。

我们在测试时可以发现,当N充分大的时候,递归实现居然拒绝工作了,这是为什么呢?

我们就要来考虑递归的内部实现了,我们知道,调用函数时,会开辟一块空间让他存储变量,但是递归中有N个函数的调用,它的函数调用不执行完,之前的函数调用也不会被释放,如果调用的足够多,占有的空间也会足够多,直到将内存空间全部占用后,有可能函数的调用还是没有结束,那么就可能会导致程序崩溃,因为内存中没有足够的空间提供让程序进行计算。

所以我们知道,解决问题方法的效率,跟空间利用效率有关。

三,例三:

 最直接的方法就是暴力解法,根据多项式的标准表达式通过循环累计求和来实现这个函数。

#include<stdio.h>
#include<math.h>

//暴力
double f(int n, double a[], double x) {
	int i;
	double p = a[0];
	for (i = 1; i <= n; i++) {
		p += a[i] * pow(x, i);
	}
	return p;
}

//秦九韶解法
double f(int n, double a[], double x) {
	int i;
	double p = a[n];
	for (i = n; i > 0; i++) {
		p = a[i - 1] + x * p;
	}
	return p;
}

这就是秦九韶公式。

我们看上面的两个函数看起来都差不多的样子,但实际上呢?

这里就需要我们学习一个clock工具了。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>

clock_t start, stop;//clock_t是clock函数返回值的类型
double duration;//记录被测函数运行时间,以秒为单位

int main() {

	//不在测试范围内的准备工作写在clock函数调用之前

	start = clock();//开始计时
	MyFunction();//这里写入想测的函数
	stop = clock();//停止计时
	duration = ((double)(stop - start))/CLK_TCK;//计算运行时间

	//CLK_TCK是机器时钟每秒所走的时钟打点数
	//在某些IDE下也可能叫做CLOCKS_PER_SEC
	
	//其他不在测试范围的处理写在后面。

	return 0;
}

 所以我们知道,解决问题方法的效率,跟算法的巧妙程度有关。

通过上面的三个例子,我们知道了,解决问题方法的效率与其相关的因素,那这些因素的定义与什么有关呢?、

好,我们现在正式开始数据结构的一个整体大概的介绍。

什么是数据结构?

1,数据对象在计算机中的组织方式,

1)逻辑结构(线性or非线性)

2)物理存储结构(数组还是链表)

2,数据对象必定与一系列加在其上的操作相关联

3,完成这些操作所用的方法就是算法

我们讨论数据对象的各种不同的组织方式,是为了得到处理这些数据对象的最高效的算法(依需求而定)。所以我们在讨论数据结构这个概念时,关心的不仅仅是数据对象本身以及他们在计算机中的组织方式,还要关心与他们相关联的一个操作集,以及实现这些操作的最高效的算法。

四,抽象数据类型

抽象数据类型是一种对“数据类型”的描述,这种描述是抽象的

数据类型:数据对象集,与数据相关的操作集

抽象:我们描述数据类型是不依赖于具体实现的,即数据对象集和操作集的描述与存放数据的机器无关,与数据存储的物理结构无关,与实现操作的算法和编程语言无关。

总的来说,只描述数据对象集和相关操作集“是什么”并不涉及“如何做到”的问题。

我们还要重点讲一讲抽象,抽象是计算机求解问题的基本方式和重要手段,它使得一种设计可以应用于多种场景。而且可以通过抽象屏蔽掉底层的细节,使得设计更加简单,理解更加方便。

二,算法

算法的设计是一门艺术,解决同一个问题,一般有多种算法,但漂亮的算法与其他算法相比往往有天壤之别。

1,什么是算法?

1)一个有限的指令集

2)接受一些输入(也可以不接受)

3)产出输出(不然没有意义)

4)一定在有限步骤之后终止

5)每一条指令必须有充分明确的目标,不可以有歧义。必须在计算机能处理的范围之内。且描述应不依赖于任何一种计算机语言以及具体实现手段。

2.算法复杂度

什么是好的算法?

比较算法优劣的指标:

1)空间复杂度S(n):根据算法写成的程序在执行时占用存储单元的长度

若过高,会导致内存超出限制,造成程序的非正常中断。

2)时间复杂度T(n):根据算法写成的程序在执行时耗费的时间的长度。

3,在分析一般算法的效率时,我们经常会关注下面的两种复杂度:

对最坏情况复杂度的分析会更容易,因为很多时候平均并不是一件简单的事。 

4,渐近表示法:

当n足够大的时候,一些常数或者说一些低阶次的变量的影响就不会那么大了,所以我们一般用渐近表示法。

 可能大家对不同的空间复杂度和时间复杂度没有一个很明确的认识,给大家看看这三个表就知道了。

 

 告诉大家一些对给定算法做渐进分析时的小窍门:

 三,应用实例:最大子列和问题

我们这里将会让大家更加清楚的认识到算法效率的差别,加深大家对算法的理解

 1,穷举所有子列和,从中找出最大值

int MaxSubseqsuml(int list[],int N) {
	int i, j, k;
	int ThisSum, MaxSum = 0;
	for (i = 0; i < N; i++) {//i是子列左端的位置
		for (j = i; j < N; j++) {//j是子列右端的位置
			ThisSum = 0;//ThisSum是i到j的子列和
			for (k = i; k <= j; k++) {
				ThisSum += list[k];
			}
			if (ThisSum > MaxSum)
				MaxSum = ThisSum;
		}
	}

	return MaxSum;
}

2,少用一个for循环

int MaxSubseqsuml(int list[], int N) {
	int i, j, k;
	int ThisSum, MaxSum = 0;
	for (i = 0; i < N; i++) {//i是子列左端的位置
		for (j = i; j < N; j++) {//j是子列右端的位置
			ThisSum += list[j];
			
			if (ThisSum > MaxSum)
				MaxSum = ThisSum;
		}
	}

	return MaxSum;
}

3,分而治之

顾名思义,分而治之的基本思路就是将原问题拆分为若干个小问题,分别解决后在将结果和而治之

,用递归实现十分方便。

#define _CRT_SECURE_NO_WARNINGS 1

int Max3(int a, int b, int c) {
	return a > b ? a > c ? a : c : b > c ? b : c;
}

int DivideAndConquer(int List[], int left, int right) {
	//分治法求List[left]到List[right]的最小子列和
	int MaxLeftSum, MaxRightSum;//存放左右子问题的解
	int MaxLeftBorderSum, MaxRightBoderSum;//存放跨分界线的结果
	int LeftBorderSum, RightBorderSum;
	int center, i;

	if (left == right) {//递归的结束条件,子列只有一个数字
		if (List[left] > 0)
			return List[left];
		else return 0;
	}
//下面是分的过程
	//找到中分点
	center = (left + right) / 2;
	//递归求两边子列的最大和 
	MaxLeftSum = DivideAndConquer(List, left, center);
	MaxRightBoderSum = DivideAndConquer(List, center + 1, right);

	//下面求跨分界线最大子列和
	MaxLeftBorderSum = 0;
	LeftBorderSum = 0;

	for (i = center; i >= left; i--) {
		LeftBorderSum += List[i];
		if (LeftBorderSum > MaxLeftBorderSum)
			MaxLeftBorderSum = LeftBorderSum;
	}
	//左边扫描结束

	MaxRightBoderSum = 0;
	RightBorderSum = 0;
	for (i = center + 1; i <= right; i++) {
		RightBorderSum += List[i];
		if (RightBorderSum > MaxRightBoderSum)
			MaxRightBoderSum = RightBorderSum;
	}
	//右边扫描结束

	//最后进行比较
	return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBoderSum);

}

int MaxSubseqsum3(int List[],int N) {
	return DivideAndConquer(List, 0, N - 1);
}

4,在线处理

“在线”的意思是指每输入一个数据就进行即时处理,得到的结果是对于当前已经读入的所有数据都成立的解,即在任何地方中止输入,算法都能正确给出正确的解。

int MaxSubseqSum4(int List[], int N) {
	int i;
	int ThisSum, MaxSum;

	ThisSum = MaxSum = 0;
	for (i = 0; i < N; i++)
	{
		ThisSum += List[i];
		if (ThisSum > MaxSum)
			MaxSum = ThisSum;
		else if (ThisSum < 0)
			ThisSum = 0;

	}

	return MaxSum;
}

这种算法并不要求存储序列中的数据,只需要一个一个读入,同时一个一个处理即可,处理过的数据没必要存储,可以说是最快的算法了。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢吃豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值