数据结构:基本概念

246 阅读3分钟

1 基本概念

1.1 什么是数据结构

数据结构定义存储数据的方式以及定义需要对这些数据进行的操作,算法实现对这些数据的操作。

  1. 例1:如何在书架上摆放图书?(1)新书怎么插入?2)如何找到某本指定的书?(问题分解
  • 方式1:随便放 新书插入:O(1);查找书籍:O(N)
  • 方式2:按书名拼音字母 新书查入:O(logN) +移动书;查找书籍:O(logN)
  • 方式3:书类别+拼音字母 空间如何分配?类别应该分多细? 解决问题方法的效率,跟数据的组织方式有关
  1. 例2:PrintN 顺序打印从1到N的全部正整数 循环VS递归 解决问题方法的效率,跟空间的利用率有关

  2. 例3:多项式求值

  • 让被测函数重复运行充分多次,使得测出的总的时钟打点间隔充分长,最后计算被测函数平均每次运行的时间即可!

  数据结构:数据对象在计算机中的组织方式,数据的逻辑结构(线型结构:1对1;树型结构:1对多;图型结构:多对多),数据的物理存储结构(顺序存储,链式存储);数据对象必定与一系列加在其上的操作相关联;完成这些操作的方法就是算法。

  描述数据结构的方式:抽象数据类型

  • 数据类型:数据对象集;数据集合相关联的操作集
  • 抽象:描述数据类型的方法不依赖于具体实现。与存放数据的机器无关;与数据存储的物理结构无关;与实现操作的算法和编程语言无关。 只关心是什么,不关心如何做到。(接口?)

1.2 什么是算法

  一个有限指令集;接受一些输入(有些情况下不需要输入);产生输出;一定再有限步骤之后终止;每一条指令必须:(1)有充分明确的目标,不可以有歧义;(2)计算机能处理的范围之内;(3)描述不应依赖于任何一种计算机语言及具体的实现手段。 空间复杂度S(n)、时间复杂度T(n) 和输入数据的规模相关(问题的规模)

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;
}

T(n)=C1n2+C2nT(n)=C_1n^2+C_2n 次乘法

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

T(n)=CnT(n)=C*n 次乘法

  • 最坏情况复杂度Tworst(n)T_{worst} (n)、平均复杂度Tavg(n)T_{avg} (n)
  • 复杂度的渐进表示法 O(f(n))O(f(n))——最小上界 Ω(g(n))Ω(g(n))——最大下界
  • T1(n)=O(f1(n))T2(n)=O(f2(n))T_1 (n)=O(f_1 (n)) T_2 (n)=O(f_2 (n))
  • T1(n)+T2(n)=max(O(f1(n)),O(f2(n)))T_1 (n)+T_2 (n)=max( O(f_1 (n)),O(f_2 (n)))
  • T1(n)×T2(n)=O(f1(n)×f2(n))T_1 (n)×T_2 (n)=O(f_1 (n)×f_2 (n))
  1. 一个for循环的时间复杂度等于循环次数乘以循环体代码的复杂度
  2. if-else 结构的复杂度取决于if的条件判断复杂度和两个分支部分的复杂度,总体复杂度取三者中最大。

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

给定N个整数的序列,求其最大子列和

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

using namespace std;

#define N 100

int Array[N];

void GetRand()
{
	//srand(time(0));
	for (int i = 0; i < N; i++)
	{
		Array[i] = rand() % N - N / 2;
	}
}

int MaxSubSeqSum1()
{
	int ThisSum, MaxSum = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = i; j < N; j++)
		{
			ThisSum = 0;
			for (int k = i; k <= j; k++)
			{
				ThisSum += Array[k];
			}
			if (MaxSum < ThisSum)
			{
				MaxSum = ThisSum;
			}
		}
	}
	return MaxSum;
}

int MaxSubSeqSum2()
{
	int ThisSum, MaxSum = 0;
	for (int i = 0; i < N; i++)
	{
		ThisSum = 0;
		for (int j = i; j < N; j++)
		{
			ThisSum += Array[j];
			if (ThisSum > MaxSum)
			{
				MaxSum = ThisSum;
			}
		}
	}
	return MaxSum;
}

int MaxSubSeqSum3(int Left, int Right)
{
	if (Left >= Right)
	{
		return Array[Left];
	}
	int Mid = (Left + Right) / 2;
	int LeftSum = MaxSubSeqSum3(Left, Mid);
	int RightSum = MaxSubSeqSum3(Mid + 1, Right);
	int MaxLeftSumFromMid = 0, Temp = 0;
	for (int i = Mid; i >= Left; i--)
	{
		Temp += Array[i];
		if (MaxLeftSumFromMid < Temp)
		{
			MaxLeftSumFromMid = Temp;
		}
	}
	int MaxRightSumFromMid = 0;
	Temp = 0;
	for (int i = Mid + 1; i <= Right; i++)
	{
		Temp += Array[i];
		if (MaxRightSumFromMid < Temp)
		{
			MaxRightSumFromMid = Temp;
		}
	}
	int MaxSumWithMid = MaxLeftSumFromMid + MaxRightSumFromMid;
	int MaxSum = LeftSum;
	if (MaxSum < RightSum)
	{
		MaxSum = RightSum;
	}
	if (MaxSum < MaxSumWithMid)
	{
		MaxSum = MaxSumWithMid;
	}
	return MaxSum;
}

int MaxSubSeqSum4()
{
	int MaxSum = 0, ThisSum = 0;
	for (int i = 0; i < N; i++)
	{
		ThisSum += Array[i];
		if (MaxSum < ThisSum)
		{
			MaxSum = ThisSum;
		}
		else if (ThisSum < 0)
		{
			ThisSum = 0;
		}
	}
	return MaxSum;
}

int main()
{
	GetRand();
	for (int i = 0; i < N; i++)
	{
		printf("%d ", Array[i]);
	}
	printf("\n");
	printf("MaxSubSeqSum is %d\n", MaxSubSeqSum4());
}
  • MaxSubSeqSum1——T(N)=O(N3)T(N)=O(N^3)
  • MaxSubSeqSum2——T(N)=O(N2)T(N)=O(N^2)
  • MaxSubSeqSum3——T(N)=O(NlogN)T(N)=O(NlogN)
  • MaxSubSeqSum4——T(N)=O(N)T(N)=O(N) 凡是时间复杂度为O(N2)O(N^2)的程序,想办法降到O(NlogN)

递归程序复杂度计算方法(以MaxSubSeqNum3为例)

  • T(N)=2T(N/2)+O(N),T(1)=O(1)T(N)=2T(N/2)+O(N),T(1)=O(1)
  • T(N)=2(T(N/4)+O(N/2))+O(N)T(N)=2(T(N/4)+O(N/2))+O(N)
  • T(N)=2kT(1)+KO(N),N=2kT(N)=2^kT(1)+K*O(N),N=2^k
  • T(N)=O(NlogN)T(N)=O(NlogN)