【数据结构梳理01】数据结构概论

一、算法

算法(Algothrim)是指一个能够为完成某一特定任务的提供运算序列的有穷指令集。

算法应具有以下特性:

①有输入:一个算法必须有0个或多个输入,是算法开始前赋予算法的量。

②有输出:输出的是算法运算结果。

③有穷性:算法必须在有限步内完成任务,不能陷入死循环。这一点与程序不同:程序可以进入死循环,比如操作系统在用户未使用前一直处于“等待”,直到新的事件出现。

④确定性:即算法的每一步无二义性

⑤有效性:算法中的每一步运算足够基本。

二、递归

数学以及程序设计方法中为递归下的定义是:若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。

在以下三种情况下,常常要用到递归的方法:

1.定义是递归的,例如在求解n!或者斐波拉契数列时;

2.数据结构是递归的,例如树,链表等;

3.问题的解法只能是递归:最典型的为Tower of Hanio。

在使用递归的方法解决问题前,我们需要清楚三点:

(1)对于一个较为复杂的问题,如果能够分解成几个相对简单的且解法相同或类似的子问题时,只要解决子问题,那么原问题便可迎刃而解;

(2)当分解后的子问题可以解决时,就停止分解。我们可以说我们获得了原子问题,也就是递归结束条件。例如在求解n!时,当分解到0!=1时我们便可停止分解,0!=1就是这个递归的递归结束条件。

(3)递归定义的函数可以直接编程求解,递归过程直接反应函数的结构。

三、算法性能分析

(一)空间复杂度

空间复杂度涉及的空间类型有:

输入空间: 存储输入数据所需的空间大小;
暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;
输出空间: 算法运行返回时,存储输出数据所需的空间大小;
通常情况下,空间复杂度指在输入数据大小为 N 时,算法运行所使用的「暂存空间」+「输出空间」的总体大小。

例如我们分析以下代码的空间复杂度:

float Sum(float*a, const int n) 
{
float s = 0;
for(int i = 0; i < n; i++)
s += a[i];
return s;
}

问题规模虽然为n,程序中用一个常数n来存放累加项个数,还有一个浮点数存放计算结果;另外对于数组a[]来说,只需存放a的首地址a[0]即可,空间占有量也为常数,故该程序的空间复杂度为O(1).

又比如下程序:

float Rsum (float *a, const int n) 
{
if (n <=0) return 0;
else return (Rsum(a,n-1)+a[n-1]); 
}

这是一个递归函数,很容易看出递归栈的高度可以达到O(n),因此空间复杂度为O(n)。

(二)时间复杂度

程序步(Program Step):在语法或语义上有意义的一段指令序列,而这段序列执行时与实例特性无关。

分析时间复杂度主要是看语句的执行是否与实例特性有关,如果与实例特性无关的语句都可以看成一个程序步,即时间复杂度为O(1)。与实例特性相关的语句要具体问题具体分析,不能笼统而论,否则分析出来误差可能较大。

对于常见的时间复杂度的分析:(以下内容来自Leetcode)

四、算法实际时间获取

两种方法:一是插入计数变量count,计算程序步

                  二是建立一个表,列出程序中各语句的执行步数 。

如果在实际过程中,我们所测算的实例时间太短,那么我们就要采取重复执行取平均的方法来测量其时间,且通常重复次数需要按照不同的实例来取不同的执行次数。

下面是一个利用C++自带计时函数time来计算二分查找法的运行时间的程序:

int BinarySearch(int* a, const int x, const int n)
{//Serach the sorted array a[0],...,a[n-1] for x.
	int left, right;
	left = 0;
	right = n - 1;
	while (left <= right)
	{
		int middle;
		middle = (left + right) / 2;
		if (x < a[middle])
			right = middle - 1;
		else if (x > a[middle])
			left = middle + 1;
		else
			return middle;
	}
	return -1;
}

void ObtainRuntime()
{
	int a[101], n[20];
	const long r[20] = { 10000000,20000000,30000000,40000000,50000000,60000000,
	70000000,80000000,90000000,100000000,110000000,120000000,130000000,
	140000000,150000000,160000000,170000000,180000000,190000000,200000000};
	for (int j = 1; j <= 100; j++)
		a[j] = j;
	for (int j = 0; j < 10; j++)
	{
		n[j] = j;
		n[j + 10] = 10 * (j + 1);
	}
	cout << " n\tTotal time\trun Time" << endl;
	for (int j = 0; j < 20; j++)
	{
		struct timeb start;
		struct timeb stop;
		ftime(&start);
		for (long b = 1; b <= r[j]; b++)
			int k = BinarySearch(a,150,n[j]);
		ftime(&stop);
		long total = (stop.time - start.time) * 100 + (stop.millitm - start.millitm) / 10;
		double runtime = (double)(total) / (double)(r[j]);
		cout << " " << n[j] << "\t" << total << "\t" <<fixed<<setprecision(10) << runtime << endl;
	}
}

int main()
{
	ObtainRuntime();
	return 0;
}

一定要注意time(start)与time(end)的插入位置,否则程序会出错

posted @ 2021-11-29 23:44  天涯海角寻天涯  阅读(223)  评论(1编辑  收藏  举报