1-1-1:数据结构的定义:
首先用计算机解决一个具体的问题需要经过以下几个步骤:
(1):分析问题,确定数据模型。
(2):设计相应的算法
(3):编写程序,运行并调试程序直至得到正确的结果。
数据是描述客观事物的数,字符以及所有能输入计算机中并被计算机程序处理的符号的集合。例如:,日常生活中使用的各种文字,数字和特定符号都是数据。
通常以数据元素作为数据的基本单位,也就是说数据元素是组成数据的有一定意义的基本单位。
数据对象是性质相同的有限个数据元素的集合,它是数据的一个子集。
数据结构是指所涉及的数据元素的集合以及数据元素之间的关系,由数据元素之间的关系构成结构,因此可把数据结构堪称是带结构的数据元素的集合。
(1):数据元素之间的逻辑关系,即数据的逻辑结构,它是数据结构在用户面前呈现的形式
(2):数据元素及其关系在计算机存储器中的存储方式,即数据的存储结构,也成为数据的物理结构。
(3):施加在该数据上的操作,即数据的运算。
1-1-2 数据的逻辑结构
1:逻辑结构的表示
由于数据的逻辑结构是面向用户的,所欲i可以采用表格,图等用户容易理解的形式表示/
eg;采用表格形式给出一个高等数学成绩表,表中的数据源元素是学生成绩记录,每个数据元素由3个数据项组成。
2:逻辑结构的类型
在现实生活中数据呈现不同类型的逻辑结构,归纳起来数据的逻辑结构主要分为:
(1)集合:结构中数据元素之间除了“同属于一个集合”的关系外没有其他关系,与数学中集合概念相同。
(2):线性结构:若结构是非空的,则有且仅有一个开始元素和终端元素,并且所有元素最多只有一个前驱元素和一个后继元素。
(3):树形结构:若结构是非空的,则有且仅有一个元素是为开始元素,可以有多个终端元素,每个元素有零个或多个后继元素,初开始元素外每个元素有且仅有一个前驱元素。
1-1-3:数据的存储结构
问题求解最终是用计算机求解,在弄清数据的逻辑结构后便可以借助计算机语言实现其存储结构。
数据的逻辑结构是面向用户的,而存储结构是面向计算机的,其基本目标是将数据关系及其逻辑关系存储到计算机的内存中。
所有元素存放在一片地址连续的存储单元中,逻辑上相邻的元素在物理位置上也是相邻的,所有不需要额外空间表示元素之间的逻辑关系。这种存储结构成为顺序存储结构
1:顺序储存结构
2:链式存储结构
3:索引存储结构
4:哈希存储结构
1-1-4 数据的运算
1-1-5 数据结构和数据类型
1:数据类型
2:抽象数据类型
1.2.1 算法和算法评价
1.1 算法的基本概念
算法(Alogorithm)是对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作。因此,算法具有以下五个重要的特性:
(1)有穷性。一个算法必总在执行又穷步之后结束,且每一步都可在有穷时间内完成。
(2)确定性。算法中每条指令必须有确切的含义,对于相同的输入只能得出相同的输出。
(3)可行性。算法中描述的操作都可以通过已经是实现的基本运算执行有限次来实现。
(4)输入。一个算法有零个或多个输入,这些输入曲子某个特定的对象的集合。
(5)输出。一个算法有一个或多个输出,这些输出是与输入有着某种特定关系的量。
设计一个好的算法应达到以下目标:
(1)正确性。算法能够正确地解决求解问题。
(2)可读性。算法应该具有良好的可读性,以帮助人们理解。
(3)健壮性。输入非法数据时,算法能适当地做出反应或进行处理,而不会莫名其妙的输出结果。
(4)效率与低存储量需求。效率是指算法执行的时间,存储量需求是指算法执行过程所需要的的最大存储空间,这两个都与问题的规模有关。
1.2 算法效率的度量
算法效率的度量是通过时间复杂度和空间复杂度来描述的。
时间复杂度
一个语句的频度是指该语句在算法中被重复执行的次数。算法中所有的语句的频度之和记为T(n),它是该算法问题规模n的函数,时间复杂度主要分析T(n)的数量级。算法中基本运算(最深层循环内的语句)的频度与T(n)同数量级,因此通常采用算法中基本运算的频度f(n)来分析算法的时间复杂度。因此,算法的时间复杂度记为:
T(n)=O(f(n))
O的含义是T(n)的数量级,其严格的数学定义是:若T(n)和f(n)是定义在正整数集合上的两个函数,则存在正常数C和n0,使得当n>=n0时,都满足0<=T(n)<=Cf(n).
算法的时间复杂度不仅依赖于问题的规模n,也取决于待输入数据的性质(如输入数据元素的初始状态)。
最坏时间复杂度是指在最坏情况下,算法的时间复杂度。
平均时间复杂度是指所有可能输入实例在等概率出现的情况下,算法的期望运行时间。
最好时间复杂度是指在最好的情况下,算法的时间复杂度。
一般总是考虑在最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。
在分析一个程序的时间复杂性时,有以下两条规则:
(1)加法规则
T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
(2)乘法规则
T(n)=T1(n)*T2(n)=O(f(n))*O(g(n))=O(f(n)*g(n))
常见的渐进时间复杂度为:
O(1)<O(log2n)<O(nlog2n)<O(n^2)<O(n ^3)<O(2 ^n)<O(n!)<O(n ^n)
空间复杂度
算法的空间复杂度S(n)定义为算法所耗费的存储空间,它是问题规模的函数。记为:
S(n)=O(g(n))
一个程序在执行时除需要存储空间来存放本身所用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些为实现计算所需信息的辅助空间。若输入数据所占空间只取决于问题本身,和算法无关,则只需分析除输入和程序之外的额外空间。
算法原地工作是指算法所需的辅助空间为常亮,即O(1).
1-3算法的存储空间分析
1-4数据结构的目标
(1):存储结构的存储能力:如果存储结构的存储能力强,存储的信息多,算法将会方便设计,反之过于简单的存储结构可能要设计一套比较复杂的算法,往往存储能力是与所使用的空间大小成正比的。
(2):存储结构应与所选择的算法相适应:存储结构是实现算法的基础,也会影响算法的设计,其选择要充分考虑算法的各种操作,应与算法的操作相适应。
2-1 线性表
线性表就是数据元素排列像一条线一样的表。
(1)初始化
顺序表:为顺序表分配一个大小确定的数组空间,空表时长度为0;
链表:构造一个空的单链表L,用头指针指向头结点,头结点的指针域置空(L = new LNode; L->next = NULL;)
(2)取值
顺序表:先判断序号值是否合理,若合理,则e = L.elem[ i - 1 ] ;
链表:用指针p指向首元结点,用j做计数器初值赋为1,从首元结点开始依次顺着链域next向下访问
p=L->next; j=1;
while(p&&j<i)
{ p=p->next; ++j; }
e = p->data;
(3)查找(按值查找)
顺序表:从第一个元素起,依次与待查找数比较,时间复杂度为O(n)
for( int i=0; i<n; i++ )
if( L.elem[i]==e) return i+1;
链表:用指针p指向首元结点,从首元结点开始依次顺着链域next向下访问,时间复杂度为O(n)
p=L->next;
while( p && p->data != e ) p=p->next;
(四)插入(在第i个位置插入新的元素e)
顺序表:将第n个至第i个位置依次向后移动一个位置,空出第i个位置,将元素e放入第i个位置,表长加1,时间复杂度为O(n)
for( int j=L.length - 1; j>=i -1; j-- ) L.elem[ j+1]=L.elem[ j ];
L.elem[ i -1 ]=e;
++L.length;
链表:先查找到第i-1个结点,再将值为e的新结点插入到结点a(i-1)和a(i)之间,时间复杂度为O(n)
p = L; j = 0;
while( p && j<i-1 ) { p=p->next; ++j; }
s = new LNode; s->data=e;
s->next = p->next; p->next = s;
(五)删除
顺序表:先判断位置i是否合理,若合理,则将被删除元素之后的元素前移,表长减1,时间复杂度为O(n)
for( j=i; j<=L.length-1; j++ ) L.elem[ j-1]=L.elem[ j ];
--L.length;
链表:先查找到第i-1个结点,临时保存被删除结点的地址以备释放,改变删除结点前驱结点的指针域,释放删除结点的指针域,时间复杂度为O(n)
while( p && j<i-1 ) { p=p->next; ++j; }
q=p-<next; p->next = q->next;