《数据结构》第一讲基本概念 

1.1什么是数据结构

1.1.1关于数据组织-例:图书摆放

”数据结构是数据对象,以及存在于该对象的实例和组成实例的数据元素之间的各种联系。这些联系可以通过定义相应的函数来给出。“ Sartaj Sahni《数据结构、算法与应用》

”数据结构是ADT(抽象数据类型Abstract Data Type)的物理实现“ Clifford A.Shaffer 《数据结构与算法分析》

”数据结构(data structure)是计算机中存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来最优效率的算法。“   中文维基百科

例1:如何在书架上摆放图书?

数据如何组织和规模有关系。

图书的摆放要使得两个操作方便实现,操作1:新书怎么插入。操作2:怎么找到某本指定的书。

方法1:随便放。操作1:哪里有空放哪,一步到位。操作2:难以实现。

方法2:按照书名的拼音字母顺序排放。操作1:难以实现,比如新进一本《阿Q正传》,则要将之后的书一本一本往后错位,直到前面留出空当为止。操作2:二分查找。

方法3:把书架划分成几块区域,每块区域指定摆放某种类别的书,每种类别内,按照书名的字母拼音顺序排放。操作1:先定类别,二分查找确定位置,移出空位。操作2:先定类别,再二分查找。

基于方法3,问题:空间如何分配?类别应该分多细?

上述例子说明,解决问题方法的效率,跟数据的组织方式有关。

讨论1.1 对中等规模、大规模的图书摆放,你有什么更好的建议?

答:先分大类,大类可以再细分小类,基于合适的数量时,再利用拼音字母顺序摆放。也可以利用计算机建立索引目录,对所有书按类别,按出版时间,按作者等以不同方式进行编号,可以通过不同的检索条件,快速查找到图书。

1.1.2关于空间使用-例:PrintN函数实现

例2:写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数

循环实现:

[cpp]  view plain copy
  1. void PrintN(int N)
  2. {
  3. for(int i = 1;i < N;i++)
  4. printf("%d\n",i);
  5. return;
  6. }

递归实现:

[cpp]  view plain copy
  1. void PrintN(int N)
  2. {
  3. if(N)
  4. {
  5. PrintN(N - 1);
  6. printf("%d\n",N);
  7. }
  8. return;
  9. }

递归实现没有用到临时变量i,看上去更简洁,输入比较小的正整数时,两个版本的结果也类似,但在我的电脑上,当N大于等于65081时,循环实现还是可以正常运行,而递归实现没有打印出任何数,如下图所示。

因为递归程序对空间占用很大,上述程序就是因为递归程序用完了能用的空间,非正常终止了。

上述例子说明,解决问题方法的效率,跟空间的利用效率有关。

1.1.3关于算法效率-例:计算多项式值

例3:写程序计算给定多项式在给定点x处的值

多项式:f(x) = a0 + a1*x + a2*x^2 + ……+ an-1*x^(n-1) + an*x^n

最直接的算法:

[cpp]  view plain copy
  1. double f1(int n,double a[],double x)  //n为阶数,系数放在数组a中,x为要计算的点
  2. {
  3. int i;
  4. double p = a[0];
  5. for(i = 1;i <= n;i++)
  6. p += (a[i] * pow(x,i));
  7. return p;
  8. }

多项式另一种写法( 秦九韶算法):f(X) = a0 + x*(a1 + x*(…+x*(an-1 + x*(an))…)

[cpp]  view plain copy
  1. double f2(int n,double a[],double x) //n为阶数,系数放在数组a中,x为要计算的点
  2. {
  3. int i;
  4. double p = a[n];
  5. for(int i = n;i > 0;i--)
  6. p = a[i-1] + x * p;
  7. return p;
  8. }

我们开始比较两种函数的运行时间:

C语言提供了clock():捕捉从程序开始运行到clock()被调用时所耗费的时间。这个时间单位是clock tick,即”时钟打点“。常数CLK_TCK:机器时钟每秒所走的时钟打点数。

常用的计算函数运行时间的模板:

[cpp]  view plain copy
  1. #include <stdio.h>
  2. #include <time.h>
  3. clock_t start,stop; //clock_t 是clock函数返回的变量类型
  4. double duration;    //记录被测函数运行时间,以秒为单位
  5. int main(void)
  6. {
  7. //不在测试范围内的准备工作写在clock()调用之前
  8. start = clock(); //开始计时
  9. MyFunction();//把被测函数加在这里
  10. stop = clock();//停止计时
  11. duration = ((double) (stop - start)) / CLK_TCK; //计算运行时间
  12. //其他不在测试范围的处理写在后面,例如输出duration的值
  13. return 0;
  14. }

下面给定具体多项式 计算在给定点x=1.1处的值f(1.1)

[cpp]  view plain copy
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <math.h>
  4. clock_t start,stop;
  5. double duration;
  6. #define MAXN 10 //多项式最大项数,即多项式阶数+1
  7. double f1(int n,double a[],double x);
  8. double f2(int n,double a[],double x);
  9. int main(void)
  10. {
  11. int i;
  12. double a[MAXN];//存储多项式的系数
  13. for(int i = 0;i < MAXN;i++)
  14. a[i] = (double) i;
  15. start = clock();
  16. f1(MAXN - 1,a,1.1);
  17. stop = clock();
  18. duration = ((double) (stop - start)) / CLK_TCK;
  19. printf("ticks1 = %lf\n",(double) (stop - start));
  20. printf("duration1 = %lf\n",duration);
  21. start = clock();
  22. f2(MAXN - 1,a,1.1);
  23. stop = clock();
  24. duration = ((double) (stop - start)) / CLK_TCK;
  25. printf("ticks2 = %lf\n",(double) (stop - start));
  26. printf("duration2 = %lf\n",duration);
  27. return 0;
  28. }
  29. double f1(int n,double a[],double x)  //n为阶数,系数放在数组a中,x为要计算的点
  30. {
  31. int i;
  32. double p = a[0];
  33. for(i = 1;i <= n;i++)
  34. p += (a[i] * pow(x,i));
  35. return p;
  36. }
  37. double f2(int n,double a[],double x) //n为阶数,系数放在数组a中,x为要计算的点
  38. {
  39. int i;
  40. double p = a[n];
  41. for(int i = n;i > 0;i--)
  42. p = a[i-1] + x * p;
  43. return p;
  44. }

但是光是这样因为两个函数运行速度都太快,所以会得到下图的结果:

所以我们需要让被测函数重复运行多次,使得测出的总的时钟打点间隔充分长,最后计算被测函数平均每次运行的时间。

[cpp]  view plain copy
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <math.h>
  4. clock_t start,stop;
  5. double duration;
  6. #define MAXN 10 //多项式最大项数,即多项式阶数+1
  7. #define MAXK 1e7 //被测函数最大重复调用次数
  8. double f1(int n,double a[],double x);
  9. double f2(int n,double a[],double x);
  10. int main(void)
  11. {
  12. int i;
  13. double a[MAXN];//存储多项式的系数
  14. for(int i = 0;i < MAXN;i++)
  15. a[i] = (double) i;
  16. start = clock();
  17. for(int i = 0;i < MAXK;i++)//重复调用函数以获得充分多的时钟打点数
  18. f1(MAXN - 1,a,1.1);
  19. stop = clock();
  20. duration = ((double) (stop - start)) / CLK_TCK / MAXK;  //计算函数单次运行时间
  21. printf("ticks1 = %lf\n",(double) (stop - start));
  22. printf("duration1 = %lf\n",duration);
  23. start = clock();
  24. for(int i = 0;i < MAXK;i++)//重复调用函数以获得充分多的时钟打点数
  25. f2(MAXN - 1,a,1.1);
  26. stop = clock();
  27. duration = ((double) (stop - start)) / CLK_TCK / MAXK;
  28. printf("ticks2 = %lf\n",(double) (stop - start));
  29. printf("duration2 = %lf\n",duration);
  30. return 0;
  31. }
  32. double f1(int n,double a[],double x)  //n为阶数,系数放在数组a中,x为要计算的点
  33. {
  34. int i;
  35. double p = a[0];
  36. for(i = 1;i <= n;i++)
  37. p += (a[i] * pow(x,i));
  38. return p;
  39. }
  40. double f2(int n,double a[],double x) //n为阶数,系数放在数组a中,x为要计算的点
  41. {
  42. int i;
  43. double p = a[n];
  44. for(int i = n;i > 0;i--)
  45. p = a[i-1] + x * p;
  46. return p;
  47. }

下面是运行结果
上述例子说明,解决问题方法的效率,跟算法的巧妙程度有关

讨论1.3再试一个多项式



1.1.4抽象数据类型

数据结构是数据对象在计算机中的组织方式,数据对象与一系列加在其上的操作相关联,完成这些操作的方法就是算法。

数据对象的逻辑结构:

以例1摆放图书作例,图书按拼音顺序一本接着一本,一个编号对应一本书,即线性结构。

先把图书分类,某个类一个编号,一个编号对应多本书,一对多的逻辑结构,即树。

如果统计一本书有几个人买,买这本书的人还买了什么书,一本书对应很多人,一个人对应很多书,多对多的一个关系网,关系网对应着图。

数据对象的物理存储结构:

这些逻辑结构在机器内存的放法。例如是用数组来存放还是用链表来存放。

抽象数据类型(Abstract Data Type):

数据类型:数据对象集, 数据集合相关联的操作集。

抽象:描述数据类型的方法不依赖于具体实现。与存放数据的机器无关,与数据存储的物理结构无关,与实现操作的算法和编程语言均无关。只描述数据对象集和相关操作集”是什么“,并不涉及”如何做到“的问题。

例4:”矩阵“的抽象数据类型定义

类型名称:矩阵

数据对象集:一个M*N的矩阵A(M*N)=(aij)(i=1,……,M;j=1,……,N)由M*N个三元组<a,i,j>构成,其中a是矩阵元素的值,i是元素所在行号,j是元素所在列号。

抽象数据类型描述时不关心a是int还是float,对元素值进行操作时也不关注元素类型。矩阵如何存储,是用一维数组、二维数组还是十字链表我们也不用关注。函数的具体实现、用什么语言也不用管。这就是抽象。

讨论1.4抽象有什么好处?

答:把复杂问题简单化、系统化,可以解决类似的一系列问题,不考虑具体的实现,从全局来看待问题。

1.2什么是算法

1.2.1算法的定义

算法(algorithm):一个有限指令集;接受一些输入(有些情况不需要输入);产生输出;一定在有限步骤后终止;每一条指令必须有充分明确的目标,不可以有歧义,在计算机能处理的范围内,描述应不依赖于任何一种计算机语言以及具体的实现手段。

例1:选择排序算法的伪码描述

void SelectionSort(int List[],int N)

{//将N个整数List[0]…List[N-1]进行非递减排序

for(i = 0; i < N;i++)

{

MinPosition = ScanForMin(List,i,N-1);

//从List[i]到List[N-1]中找最小元,并将其位置赋给MinPosition;

Swap(List[i],List[MinPosition]);

//将未排序部分的最小元换到有序部分的最后位置;

}

}

这段伪码描述比较抽象,并不知道List是数组还是链表,Swap是函数实现还是宏实现,这些具体实现我们在描述算法时是不关心。

1.2.2什么是好的算法

空间复杂度S(n)——根据算法写成的程序在执行时占用存储单元的长度。这个长度往往与输入数据的规模有关。空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断。

时间复杂度T(n)——根据算法写成的程序在执行时耗费时间的长度。这个长度往往也与输入数据的规模有关。时间复杂度过高的低效算法可能导致我们在有生之年都等不到运行结果。

比如之前1.1.2的例2写PrintN函数的递归版本,每次调用PrintN,都会占用一块空间,因为是递归调用,要一直调用到PrintN(0)才会开始释放之前的空间,所以N过大可能就会导致内存超限,使程序非正常中断。如下图所示,其占用空间的大小随着N的增大而线性增长。

而之前的循环版本占用的空间不论N如何变化,是不变的。

再回顾之前1.1.3的例3,因为机器运算加减法的速度比乘除法的速度快很多,程序的时间复杂度一般看函数做了多少次乘除法。例3第一个版本,每次循环需要做i次的乘法,乘法的总次数为(1+2+……n)=(n^2+n)/2,而例3第二个版本每次循环只做了一次乘法,总共n次乘法。所以第一个版本的时间复杂度T(n)=C1*n^2 + C2*n,第二个版本的时间复杂度T(n) = C * n。所以当n充分大时,n^2的值远远大于n,所以当n很大时,第二个版本会比第一个版本运行快很多。

在分析一般算法的效率时,我们经常关注下面两种复杂度,最坏情况复杂度Tworst(n),平均复杂度Tavg(n),Tavg(n) <= Tworst(n)。我们平时比较喜欢分析最坏情况复杂度。

讨论1.5分析“二分法”

查找算法中的“二分法”是这样定义的:

给定N个从小到大排好序的整数序列List[],以及某待查找整数X,我们的目标是找到X在List中的下标。即若有List[i]=X,则返回i;否则返回-1表示没有找到。

二分法是先找到序列的中点List[M],与X进行比较,若相等则返回中点下标;否则,若List[M]>X,则在左边的子系列中查找X;若List[M]<X,则在右边的子系列中查找X。

试写出算法的伪码描述,并分析最坏、最好情况下的时间、空间复杂度。

我的二分法代码:

[cpp]  view plain copy
  1. int binarySearch(double List[],double x,int N)
  2. {
  3. int low,mid,high;
  4. low = 0;
  5. high = N - 1;
  6. while(low <= high)
  7. {
  8. mid = (low + high) / 2;
  9. if (x < List[mid])
  10. high = mid - 1;
  11. else if(x > List[mid])
  12. low = mid + 1;
  13. else
  14. return mid;
  15. }
  16. return -1;
  17. }

最坏时间复杂度T(n) = C * log2(n),最好时间复杂度 T(n) = C

空间复杂度S(n) = C

1.2.3复杂度的渐进表示

复杂度的渐进表示法:T(n) = O(f(n)) ,表示存在常数C>0,n0>0,使得当n>= n0时有T(n) <= C * f(n),即为上界。T(n) = Ω(g(n)),表示存在常数C>0,n0>0,使得当n>=n0时有T(n) >= C *g(n),即为下界。T(n) = θ(h(n))表示同时有T(n) = O(h(n)) 和T(n) = Ω(h(n))。我们应该要找能找到的最小的上界和最大的下界。下图是一些不同复杂度的函数:

用图形直观表示:

作为一个专业的程序员,设计算法时看到复杂度为n^2的,最好能想办法把它降为nlogn,效率会巨大提高。

复杂度分析小窍门:

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

1.3.1应用实例-算法1&2


算法1:算出所有的子列和,从中找出最大的一个:

[cpp]  view plain copy
  1. int MaxSubseqSum1(int A[],int N)
  2. {
  3. int ThisSum,MaxSum = 0;
  4. int i,j,l;
  5. for(i = 0;i < N;i++)   //i是子列左端位置
  6. {
  7. for(j = i;j < N;j++) //j是子列右端位置
  8. {
  9. ThisSum = 0;     //ThisSum是从A[i]到A[j]的子列和
  10. for(k = i;k < j;k++)
  11. ThisSum += A[k];
  12. if(ThisSum > MaxSum)  //如果刚得到的这个子列和更大
  13. MaxSum = ThisSum;   //则更新结果
  14. }//j循环结束
  15. }//i循环结束
  16. return MaxSum;
  17. }

时间复杂度T(n) = O(n^3),时间复杂度过高,有着大量的重复计算。

算法2:

[cpp]  view plain copy
  1. int MaxSubseqSum2(int A[],int N)
  2. {
  3. int ThisSum,MaxSum = 0;
  4. int i,j;
  5. for(i = 0;i < N;i++) //i是子列左端位置
  6. {
  7. ThisSum = 0;    //ThisSum是从A[i]到A[j]的子列和
  8. for(j = i;j < N;j++) //j是子列右端位置
  9. {
  10. ThisSum += A[j];    //对于相同的i不同的j,只要在j-1次循环的基础上累加1项即可
  11. if(ThisSum > MaxSum) //如果刚得到的这个子列和更大
  12. MaxSum = ThisSum;   //则更新结果
  13. }   //j循环结束
  14. }   //i循环结束
  15. return MaxSum;
  16. }

时间复杂度T(n) = O(n^2),最好能把时间复杂度改进成O(nlogn)

1.3.2应用实例-算法3

算法3:分而治之

分而治之的思想:把复杂的问题切分成小的块,分头去解决它们,最后将结果合并。

应用在最大子列和问题上,即将存储数据的数组看成左右两部分,分别求出两部分的最大子列和以及贯穿中间线的最大子列和,三个部分中的最大部分即我们要求的最大子列和。

[cpp]  view plain copy
  1. int Max3( int A, int B, int C )
  2. { /* 返回3个整数中的最大值 */
  3. return A > B ? A > C ? A : C : B > C ? B : C;
  4. }
  5. int DivideAndConquer( int List[], int left, int right )
  6. { /* 分治法求List[left]到List[right]的最大子列和 */
  7. int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
  8. int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
  9. int LeftBorderSum, RightBorderSum;
  10. int center, i;
  11. if( left == right )  /* 递归的终止条件,子列只有1个数字 */
  12. if( List[left] > 0 )  return List[left];
  13. else return 0;
  14. /* 下面是"分"的过程 */
  15. center = ( left + right ) / 2; /* 找到中分点 */
  16. /* 递归求得两边子列的最大和 */
  17. MaxLeftSum = DivideAndConquer( List, left, center );
  18. MaxRightSum = DivideAndConquer( List, center+1, right );
  19. /* 下面求跨分界线的最大子列和 */
  20. MaxLeftBorderSum = 0; LeftBorderSum = 0;
  21. for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */
  22. LeftBorderSum += List[i];
  23. if( LeftBorderSum > MaxLeftBorderSum )
  24. MaxLeftBorderSum = LeftBorderSum;
  25. } /* 左边扫描结束 */
  26. MaxRightBorderSum = 0; RightBorderSum = 0;
  27. for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
  28. RightBorderSum += List[i];
  29. if( RightBorderSum > MaxRightBorderSum )
  30. MaxRightBorderSum = RightBorderSum;
  31. } /* 右边扫描结束 */
  32. /* 下面返回"治"的结果 */
  33. return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
  34. }
  35. int MaxSubseqSum3( int List[], int N )
  36. { /* 保持与前2种算法相同的函数接口 */
  37. return DivideAndConquer( List, 0, N-1 );
  38. }

讨论1.6算法3的空间复杂度是多少?

具体来说,这个问题分两部分:

  1. 由于递归而产生的空间复杂度是多少?

  2. 算法的整体空间复杂度一共是多少?

答:1.递归1次,左边2/n,右边2/n;……递归k次,左边1,右边1,即2^k = n,k = log2(n),S(n) = O(logn)

2.数组存储所需的空间C1*n和递归所需的空间C2*log2(n) = O(n)

1.3.3应用实例-算法4

算法4:在线处理

int MaxSubseqSum4(int A[],int N)
{int ThisSum,MaxSum;int i;ThisSum = MaxSum = 0;for(i = 0;i < N;i++){ThisSum += A[i];  //向右累加if(ThisSum > MaxSum)MaxSum = ThisSum; //发现更大和则更新当前结果else if(ThisSum < 0)   //如果当前子列和为负ThisSum = 0;//则不可能使后面的部分和增大,抛弃之}return MaxSum;
}

时间复杂度T(N) = O(N),已经是可以想象的最快最好的算法了。但别人理解起来可能会有困难。在线处理指的是每输入一个数据就进行即时处理,在任何一个地方中止输入,算法都能正确给出当前的解。

讨论1.7 晒运行结果

在PTA上发布的编程题“最大子列和问题”给了非常宽松的时间上限,让大家可以至少把算法2、3、4分别尝试一下。

算法2运行结果:

算法3运行结果:

算法4运行结果:

课后作业

01-复杂度2 Maximum Subsequence Sum   (25分)

Given a sequence of K integers { N​1​​, N​2​​, ..., N​K​​ }. A continuous subsequence is defined to be { N​i​​, N​i+1​​, ..., N​j​​ } where 1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (≤10000). The second line contains K numbers, separated by a space.

Output Specification:

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:

10
-10 1 2 3 4 -5 -23 3 7 -21

Sample Output:

10 1 4

我的代码:

#include <stdio.h>int main(void)
{int thisSum,maxSum;int leftNumber,rightNumber,temporaryLeftNumber;int k;scanf("%d",&k);  //读取整数个数 int array[k];for(int i = 0;i < k;i++)    //读取k个整数存入数组 scanf("%d",&array[i]);thisSum = maxSum = 0;    //将thisSum 和 maxSum 设为0 leftNumber = array[0];       //为了处理全部小于0时输出首尾数字的情况,先将数组头尾的数字赋给记录子列左右数字的变量 rightNumber = array[k-1];temporaryLeftNumber = array[0]; //记录左边数字的暂定值 for(int i = 0;i < k;i++){thisSum += array[i]; //向右累加if(thisSum > maxSum){leftNumber = temporaryLeftNumber;maxSum = thisSum;  //发现更大和则更新当前结果rightNumber = array[i];          //如果有更大和,那么不断更新右边的数字 }if(thisSum < 0) //如果当前子列和为负{thisSum = 0;       //则不可能使后面的部分和增大,抛弃之 if(i + 1 < k)temporaryLeftNumber = array[i + 1];}if(array[i] == 0 && maxSum == 0 ) //如果A[i]为0的且最大子列和也为0,则将左右数字都设为0 {leftNumber = 0;rightNumber = 0;}}printf("%d %d %d",maxSum,leftNumber,rightNumber);  //输出答案 } 


陈越、何钦铭《数据结构》第一讲基本概念 笔记相关推荐

  1. 数据结构-第一讲 基本概念-学习笔记(MOOC 浙江大学 陈越 何钦铭)

    目录 第一讲 基本概念 1.1 什么是数据结构 1.1.1 关于数据组织 - 例:图书摆放 1.1.2 关于空间使用 - 例:PrintN函数实现 1.1.3 关于算法效率 - 例:计算多项式值 计算 ...

  2. [数据结构] 第一讲 基本概念

    目录 一.概念 二.分类 一.概念 数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符合集合. 数据元素:是组成数据的.有一定意义的基本单位,在计算机中通常 ...

  3. 【2020-MOOC-浙江大学-陈越、何钦铭-数据结构】春期中考试(附每一个题目的详细解析)

    文章目录 〇.前言 一.判断题 二.选择题 三.程序填空题 总结 〇.前言 这周开始了[MOOC-浙江大学-陈越.何钦铭-数据结构]的期中考试,感觉有点难,可能是我才学了一个月不到的原因??? 一.判 ...

  4. 陈越《数据结构》第一讲 基本概念

    陈越<数据结构>第一讲 基本概念 1什么是数据结构 1.1 引子 例子:如何在书架上摆放图书? 随便放: 按照书名的拼音字母顺序排放: 把书架划分成几块区域,每块区域指定摆放某种类别的图书 ...

  5. 【2020-MOOC-浙江大学-陈越、何钦铭-数据结构】树和堆(第五周的笔记和编程作业)

    文章目录 〇.前言 一.堆 二.堆的基本操作 三.哈夫曼树 四.集合及运算 五.堆中的路径 六.课后题 1.05-树7 堆中的路径 (25分) 2.05-树8 File Transfer (25分) ...

  6. 中国大学MOOC-陈越、何钦铭-数据结构-2022春期中考试

    中国大学MOOC-陈越.何钦铭-数据结构-2022春期中考试(2) 判断题 1-1 所谓"循环队列"是指用单向循环链表或者循环数组表示的队列. (2分) 1-2 用邻接表法存储图, ...

  7. 中国大学MOOC-陈越、何钦铭-数据结构-2019夏期末考试(含答案)

    中国大学MOOC-陈越.何钦铭-数据结构-2019夏期末考试(含答案) 判断题 1-1   对N个不同的数据采用冒泡排序进行从大到小的排序,当元素基本有序时交换元素次数肯定最多. (2分) 1-2   ...

  8. 中国大学MOOC-陈越、何钦铭-数据结构-2022春期中考试(2)

    中国大学MOOC-陈越.何钦铭-数据结构-2022春期中考试(1) 判断题 1-1 将1.2.3.4.5.6顺序插入初始为空的AVL树中,当完成这6个元素的插入后,该AVL树的先序遍历结果是:4.2. ...

  9. 中国大学MOOC-陈越、何钦铭-数据结构-2019秋期末考试

    [PTA]中国大学MOOC-陈越.何钦铭-数据结构-2019秋期末考试 一.判断题: 1-1 对N个不同的数据采用冒泡排序进行从大到小的排序,当元素基本有序时交换元素次数肯定最多. [F] 1-2 2 ...

最新文章

  1. Windows核心编程 第四章 进程(下)
  2. 成功解决RuntimeError: filter weights array has incorrect shape.
  3. Docker最全教程——从理论到实战(三)
  4. 解决报错 javax.persistence.TransactionRequiredException: Executing an update/delete query
  5. java gbk汉字 10进制,用java程序将GBK字符转成UTF-8编码格式
  6. Servlet destroy()回收机制
  7. Unity 中的特殊文件夹
  8. Bailian2690 首字母大写【字符串】
  9. 图(一):图的邻接表表示
  10. OSPF 单区域配置实例学习记录
  11. 恢复为TrustedInstaller权限
  12. chrome浏览器书签同步_如何将Google Chrome浏览器的书签与手机同步
  13. class_view_decorate
  14. Visual Studio远程调试程序
  15. 酷派大神F1移动版刷机教程卡刷教程
  16. worldpress php部署,用XAMPP搭建本地主机(附worldpress的安装)
  17. 服务器文件夹怎么找回来,文件过期了怎么恢复(教你一招找回微信过期文件)...
  18. 解决win10蓝牙搜索到小爱音箱无法连接问题
  19. dotnet 配置 Gitlab 的 Runner 做 CI 自动构建
  20. 朋也bbs开源学习(一)

热门文章

  1. 如何屏蔽百度热搜榜?(关闭百度搜索热点、关闭百度风云榜实时热点)(Adblock Plus插件)
  2. 使用UML进行业务分析(二)——“生产订单管理”实例介绍
  3. 【架构设计】单点登录SSO设计与实现
  4. 手机(Android)刷NetHunter安装指南,无需ssh执行kali命令, NetHunter支持的无线网卡列表!
  5. SpringBoot Web项目Mock测试
  6. kubernetes 中安装 heapster 问题
  7. python冒号切片_Python切片知识解析
  8. Density Peak
  9. 【学习方法】手机越戒越上瘾?三招让你戒掉手机玩命学习!
  10. 【HDR图像处理】HDR图像,HDRI技术的一些基础概念 | GPT对话记录