1 / 35

回顾:绪论( 1/2 )

回顾:绪论( 1/2 ). 为什么要学数据结构? 什么是数据结构? 研究计算机操作对象及其关系与操作 相互之间存在一种或多种特定关系的数据元素的集合 从操作对象抽象出来的数学模型, “ 关系 ” 描述数据元素之间的逻辑关系,也称之为逻辑结构 什么是数据类型? 值的集合以及定义在这个值集合上的一组操作 什么是抽象数据类型? 与数据类型的差异???. 回顾:绪论( 2/2 ). 算法?算法性能的度量? 时间 空间. 空间复杂度度量. 存储空间的固定部分 程序指令代码的空间,常数、简单变量、定长成分 ( 如数组元素、结构成分、对象的数据成员等 ) 变量所占空间

chelsey
Download Presentation

回顾:绪论( 1/2 )

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 回顾:绪论(1/2) • 为什么要学数据结构? • 什么是数据结构? • 研究计算机操作对象及其关系与操作 • 相互之间存在一种或多种特定关系的数据元素的集合 • 从操作对象抽象出来的数学模型,“关系”描述数据元素之间的逻辑关系,也称之为逻辑结构 • 什么是数据类型? • 值的集合以及定义在这个值集合上的一组操作 • 什么是抽象数据类型? • 与数据类型的差异???

  2. 回顾:绪论(2/2) • 算法?算法性能的度量? • 时间 • 空间

  3. 空间复杂度度量 • 存储空间的固定部分程序指令代码的空间,常数、简单变量、定长成分(如数组元素、结构成分、对象的数据成员等)变量所占空间 • 可变部分尺寸与实例特性有关的成分变量所占空间、引用变量所占空间、递归栈所用空间、通过new和delete命令动态使用空间 • 空间复杂度 原地工作(额外空间相对输入数据量来说是常数) O(1) 如果我不用任何附加的空间,实现两个数的交换, 怎么做?

  4. 算法时间复杂度分析练习(1/3) • 2100, (3/2)n,(2/3)n, nn ,n0.5 , n! ,2n ,lgn ,nlgn, n(3/2) • 常见的时间复杂度按数量级递增排列,依次为:常数阶0(1)、对数阶0(log2n)、线性阶0(n)、线性对数阶0(nlog2n)、平方阶0(n2)、立方阶0(n3)、k次方阶0(nk)、指数阶0(2n)。先将题中的函数分成如下几类: 常数阶:2100对数阶:lgn K次方阶:n0.5、n(3/2)指数阶 (按指数由小到大排):nlgn、(3/2)n、2n、 n!、 nn注意:(2/3)^n由于底数小于1,所以是一个递减函数,其数量级应小于常数阶。根据以上分析按增长率由小至大的顺序可排列如下:(2/3)n < 2100 < lgn < n0.5 < nlgn < n(3/2) < (3/2)n < 2n < n! < nn

  5. 算法时间复杂度分析练习(2/3) • 给一个实数序列,求子序列的最大和 int MaxSubeqSum(const int A[],int N) { int thisusm,maxsum,i,j,k; for (maxsum=0,i=0;i<N;i++) for(j=i;j<N;j++) { thissum=0; for(k=i;k<=j;k++) thissum+=A[k]; if(thissum>maxsum) maxsum=thissum; } return maxsum; }

  6. 算法时间复杂度分析练习(3/3) • 求菲波拉契数:f0=1,f1=1,fn=fn-1+fn-2 • Long int • Fib(int n) • { if (N<=1) • return 1; • else return Fib(n-1)+Fib(n-2); • } 时间复杂度? 能有更好的算法吗? (时间复杂度低)

  7. 第二章 线性表 • 线性表的类型定义 • 线性表的顺序表示和实现 • 线性表的链式表示 • 线性表的应用

  8. c y z a b 线性结构 线性结构示例:英文字母表 特点: 数据的非空有限集合中, • 存在唯一的一个被称作“第一个”的数据元素; • 存在唯一的一个被称做“最后一个”的数据元素; • 除第一个元素外,其他每一个元素有一个且仅有一个直接前驱。 • 除最后一个元素外,其他每一个元素有一个且仅有一个直接后继。

  9. 线性表的类型定义 定义:线性表(Linear list)是n(0)个数据元素的有限序列,记作(a1, …ai-1, ai , ai+1 , …, an),其中ai是表中数据元素,n是表长度。 i称为ai在线性表中的位序;n=0是为空表。 特点: • 同一线性表中的元素具有相同特性 • 复杂的数据元素可由若干个数据项组成 • 相邻数据元素之间存在序偶关系

  10. 例子 • (1,2,3,4,5,6,7,8,9,10,11) • (2,5,7,4,8,4,3,6,7,44444,3) • (‘ab’,’cd’,’af’,’adsd’) • 字符串 • 以下是错误的例子 • (a,b,c,d),其中a=3,b=5,c=‘a’,d=(1,2) • 自然数

  11. 线性表的ADT定义 ADT List{ 数据对象:D={ai| ai ElemSet,i=1,2,…,n,n  0} 数据关系:R1={< ai-1, ai >| ai-1, aiD,i=2,…n} 基本操作: InitList(&L) DestroyList(&L) ClearList(&L) ListEmpty(L) ListLength(L) GetElem(L,i,&e)

  12. 线性表的ADT定义 LocateElem(L,e,compare( )) PriorElem(L,cur_e,&pre_e) NextElem(L,cur_e,&next_e) ListInsert(&L,i,e) ListDelete(&L,i,&e) ListTraverse(L,visit( )) } ADT List • 在此ADT基础上还可定义更复杂的一些操作

  13. 两个更复杂的操作(1) • 例2-1:集合的并(union)操作 void union(List &La,List Lb){ La_len=ListLength(La); Lb_len=ListLength(Lb); for (i=1;i<=Lb_len;i++){ GetElem(Lb,i,e); if (! LocateElem(La,e,equal)) ListInsert(La,++La_len,e); } } //union 算法2.1

  14. 两个更复杂的操作(2) • 例2-2:两个有序表的归并(merge)操作 void MergeList(List La,List Lb,List & Lc){ //非递减表La,Lb归并为非递减表Lc InitList(Lc); i=j=1;k=0; La_len=ListLength(La); Lb_len=ListLength(Lb); //指针平行移动,一次扫描完成

  15. 两个更复杂的操作(3) while((i<=La_len) && (j <= Lb_len)){ GetElem(La,i,ai);GetElem(Lb,j,bj); if (ai <= bj){ ListInsert(Lc,++k,ai);++i;} else {ListInsert(Lc,++k,bj);++j;} } //若La或Lb还有剩余部分,则直接把这些元素 //依次加到Lc表尾 while (i<=La_len){ //La有剩余 GetElem(La,i++,ai); Listinsert(Lc,++k,ai); }

  16. 两个更复杂的操作(4) while (j<=Lb_len){//Lb有剩余 GetElem(Lb,j++,bji);Listinsert(Lc,++k,bj); } } //union 算法2.2 时间复杂度分析: • 算法的时间复杂度取决于ADT List定义中基本操作的执行时间

  17. 1 2 3 4 5 6 45 89 90 67 40 78 如何表示线性表? • 定义:将线性表中的元素相继存放在一个连续的存储空间中。 • 存储结构:类似一维数组 • 特点:线性表的顺序存储方式——顺序表 • 存取方式:顺序访问, 可以随机存取

  18. 1 2 … i … … … n a1a2 … a i … … … an a a+l … a+(i-1)*l … … … a+(n-1)*lidle 假设每个元素占用 l 个存储地址,则 LOC(a i+1) = LOC( a i )+ l LOC(a i) = LOC(a1)+(i-1)*l 顺序表的存储方式

  19. 创造我们自己的一维数组(1/3) 假设C语言中没有数组,你怎么实现数组? #define LIST_INIT_SIZE 100 //线性表存储空间初始分配量 #define LISTINCREMENT 10 //存储空间分配增量 typedef struct { ElemType *elem; //存储空间基址 int length; //当前元素个数(表长) int listsize; //当前分配存储容量 //以sizeof(ElemType)为单位 } SqList

  20. 创造我们自己的一维数组(2/3) • 初始化 void InitList_sq ( SqList & L ) { L.elem = ( ElemType * ) malloc (LIST_INIT_SIZE * sizeof (ElemType) ); if ( ! L.elem ) exit (OVERFLOW); L.length = 0; L.listsize = LIST_INIT_SIZE ; return OK; }

  21. 创造我们自己的一维数组(3/3) • 按值查找:在顺序表中从头查找结点值等于给定值 x的结点 int LocateElem ( SqList &L, ElemType x,equal ) { int i = 0; while ( i < L.length && L.elem[i] != x ) i++; if ( i < L.length ) return ++i; else return 0; }

  22. 求表的长度 int ListLength ( SqList L ) { return L.length; }

  23. 提取函数:在表中提取第 i 个元素的值 Status GetElem( SqList L, int i ,ElemType &e) { if ( i >= 1 && i <= L.length ){ e= L.elem[i];return (OK);} else{ printf ( “参数i 不合理!\n” ); return ERROR;} }

  24. 1 2 3 4 5 6 7 25 34 57 16 48 09 63 i 插入 x 50 1 2 3 4 5 6 7 8 25 34 57 50 16 48 09 63 50 • 插入元素

  25. 顺序表的插入 Status ListInsert_Sq ( SqList &L, int i, ElemType e ) {//在表L中第 i 个位置前插入新元素 e if (i < 1 || i > L.length+1 |) return ERROR; if (L.length >= L.listsize){//当前存储空间满,增加分配 newbase = (ElemType *) realloc (L.elem, (L.listsize+LISTINCREMENT)*sizeof(ElemType)); if (!newbase) exit(OVERFLOW);//存储分配失败 L.elem = newbase;L.listsize+=LISTINCREMENT; }

  26. q=&(L.elem[i-1]); //q为插入位置 for (p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p; //插入位置及之后的元素右移 } L.elem[i-1]=e; L.length--; }

  27. 性能分析: • 基本操作为“移动”(实质为赋值)操作; 在第i个位置前插入需移动n-i+1个元素 • 假设在任何位置插入元素都是等概率的,则移动次数的期望值(平均次数)Eis为 因此,算法的平均时间复杂度为O(n)

  28. 1 2 3 4 5 6 7 8 16 25 34 57 50 16 48 09 63  删除x 1 2 3 4 5 6 7 25 34 57 50 48 09 63  • 删除元素

  29. 顺序表的删除 Status ListDelete_Sq ( SqList &L,int i, ElemType &e ) { //在表L中删除第i个元素,并用e返回其值 if ((i<1) ||(i>L.length)) return ERROR; p=&(L.elem[i-1]); //p 为被删除元素的位置 e=*p; q=L.elem+L.length-1;//表尾元素位置 for (++p;p<=q;++p) *(p-1)=*p;//元素左移 --L.length; //表长减一 return OK; }

  30. 性能分析: • 基本操作为“移动”(赋值)操作; 删除第i个位置的元素需(向左)移动n-i个元素 • 假设在任何位置删除元素都是等概率的,则移动次数的期望值(平均次数)Edl为 因此,算法的平均时间复杂度也为O(n)!

  31. 集合的“并”运算 • void Union ( SqList &La, SqList Lb ) { • int n = ListLength ( La); • int m = ListLength ( Lb); • for ( int i = 1; i <= m; i++ ) { • GetElem ( Lb, i,e ); //在Lb中取一元素 • if (!LocateElem(La,e,equal)) // La中若没有 • //则向La表尾插入该元素 • ListInsert_Sq(La,++n,e); • } • } • 时间复杂度:O(La.length* Lb.length) • 空间复杂度: O(La.length+ Lb.length) 顺序表的应用(1)

  32. 顺序表的应用(2) • 集合的“减”运算 • void Intersection ( SqList &La, SqList &Lb ) { • int n = ListLength ( La); • int m = ListLength ( Lb); • for ( int i = 1; i <= m; i++ ) { • GetElem ( Lb, i,e ); //在Lb中取一元素 • if (k=LocateElem(La,e,equal)) // La中若有 • //则从La中删除该元素 • ListDelete_Sq(La,k,e); • } • } • 时间复杂度:O(La.length* Lb.length) • 空间复杂度: O(1)

  33. 顺序表的应用(3) • 顺序表的归并(算法2.7) void MergeList_Sq(SqList La,SqList Lb,SqList &Lc) { pa=La.elem;pb=Lb.elem; Lc.listsize=Lc.length=La.length+Lb.length; pc=Lc.elem=(ElemType *) malloc(Lc.listsize * sizeof (ElemType)); if (!Lc.elem) exit(OVERFLOW); pa_last =La.elem+La.length-1; pb_last =Lb.elem+Lb.length-1;

  34. while (pa <=pa_last &&pb<=pb_last){ if (*pa<=*pb) *pc++=*pa++; else *pc++=*pb++; } while (pa<=pa_last) * pc++=*pa++; while (pb<=pb_last) * pc++=*pb++; }//MergeList_Sq • 时间复杂度:O(La.length+Lb.length) • 空间复杂度:O(La.length+Lb.length)

  35. Homework 2 • 思考题: 试分析顺序表的插入与删除操作的最坏时间复杂度。 • 作业: 习题集第18页 题2.21

More Related