数据结构复习题

数据结构复习题

(一)简答题

1.已知一棵完全二叉树共有n个节点,试求:(要求写出求解过程)

完全二叉树的相关公式
因为完全二叉树中,叶子结点的数量比度为二的结点的数量多1。
若总结点数为999这样的奇数,999/2 = 499.5 ,那么叶子结点就有500个,度为2的节点为499个,度为1的节点为0个。
若总结点数为1500这样的偶数,1500/2 = 750 ,那么叶子节点就有750个,度为2的节点就有749个,度为1的节点为1个
左孩子等于父节点×2,右孩子等于左孩子+1

  • 树的高度;
    [logn]+1(中括号代表取整)
  • 叶子节点数;
    图片源自于网络
  • 单支节点数
    0或者1个。
    假设N0=a个(叶子节点),N1=b个(只有可能为0或1),N2=c个(双分支节点),N0=N2+1,N=N0+N1+N2;
    式中,N1为单支节点数,N为节点总数。
  • 第3层第3个结点的编号,以及它的孩子结点的编号为什么?(默认根结点的编号为1)。
    答:第三层第一个是2的n-1次方,即=4,那么第三个等于6.左孩子为2n=12,右孩子为2n+1=13;

2.在利用链式结构(单链表、双链表、单循环链表及双循环链表)进行数据的访问中,能否从当前节点出发访问到任一节点?为什么?请给出你的分析的理由,描述出单、双链表的结构及对应的循环链表的特点。

答:单链表不行,其他都可以。因为单链表是单向的,每个节点的指针指向的是下一个节点,没有指向前驱的指针而无法访问前驱节点。所以从任意节点出发,只能访问该节点的下一节点。

3.请分析在顺序查找、折半查找、分块查找中,若查找元素时以等概论计算,这些查找方案各自的ASL为多少?给出结论及求解过程。

答:log2的b+1次方-1+(s+1)/2
ASL(关键字的平均比较次数)
】【ASL的定义

  • 对于顺序查找:1/n*(1+2+3+…+n)=(n+1)/2.

  • 对于折半查找:将所要查找的序列,放入一棵树,那么总的查找次数就是每一层的节点数乘以第几层。

  • 第i层的节点个数为2的i-1次方,那么该层的查找次数为i.将i从1求和到n 最后答案为log2的n+1次方-1。

  • 对于分块查找,通过折半查找查找索引表,然后再通过顺序查找。 一共n个元素,每个块s个,那么有n/s块=b。

4.对于n个顶点的无向图和有向图(均为不带权图),当采用邻接矩阵和邻接表表示时,如何求解以下问题:

  1. 图中有多少条边;
    答:①对于邻接矩阵表示的无向图,图的边数等于邻接矩阵数组中为1的元素个数除以2;
    ②对于邻接表表示的无向图,图中的边数等于边结点的个数除以2;
    ③对于邻接矩阵表示的有向图,图中的边数等于邻接矩阵数组中为1的元素个数;
    ④对于邻接表表示的有向图,图中的边数等于边结点的个数;

  2. 任意两个顶点之间i和j是否有边相连;
    答:①对于邻接矩阵g表示的无向图,邻接矩阵数组元素g。Edges[i][j]为1表示它们有边相连。
    ② 如果邻接矩阵g表示的有向图,Edges[i][j]为1表示i到j有边相连。Edges[j][i]表示j到i有边相连。
    ③对于邻接表表示的无向图G,如果G->adjlist[i]的单链表中找到编号为j的边节点,表示它们有边相连;否则为无边相连。
    ④对于邻接表G表示的有向图G,如果G->adjlist[i]的单链表中找到编号为j的边节点,则i与j相连。如果G->adjlist[j]的单链表中找到编号为i的边节点,表示j到i有边。

  3. 任意一个顶点的度是多少。
    ①对于邻接矩阵表示的无向图,顶点i对应的度是第i行元素为1的个数;
    ②对于邻接矩阵表示的有向图,顶点i对应的出度是第i行元素为1的个数,入度是第i列对应的元素为1的个数。
    ③对于邻接表G表示的无向图,顶点i对应的度,是G->adjlist[i]为头节点的单链表中边节点的个数;
    ④对于邻接表G表示有向图,顶点i对应的出度,是G->adjlist[i]为头节点的单链表中边节点的个数。入度需要遍历所有边节点,若G->adjlist[j]为头结点的单链表中存在编号为i的边节点,则顶点i的入度增加1,顶点i的度等于入度和出度之和。

5. 一棵满二叉树,若是操作仅限于进行元素的查找(包括查找孩子结点、双亲结点、兄弟结点),最佳的存储方案是?说明理由。

顺序表存储,用顺序表存储利用率是100%。用顺序表可以方便查到子节点,父节点和兄弟节点。查兄弟节点就将index加一或减一。父节点=index/2,左孩子节点为index *2,右孩子节点为index *2+1.

6.在直接插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、堆排序和基数排序方法中

  1. 不需要进行关键字比较的是哪些?
    答:基数排序
  2. 关键字比较的次数与记录的初始排列次序无关的是哪些?
    答:选择排序
  3. 上面各种排序算法的平均时间效率分别为什么?
    一般取最坏时间复杂度或平均情况为平均时间复杂度。希尔排序的平均时间复杂度是O(n^1.3)
    图片源自网络

7.将一个顺序表中所有的元素实现逆置,算法的空间复杂度尽量保持为O(1),请用文字描述出你的方案。

扫描顺序表中的前半部分,即N/2个元素,然后将这些元素,与后半部分对应的元素交换位置。对于L.data[i]与后面的L.data[L.length-i-1]交换。

8.含有n个结点的3叉树的最小高度是多少?

答:二叉树具有n个结点的m次树的最小高度:图片源自网络

9.已知完全二叉树的第8层有8个节点,则其分支结点数是多少?(请给出求解过程)

答:如果第8层不是最底层,那么第8层的节点数为2的7次方,显然是大于8,所以,第8层是最底层。因为是完全二叉树,所以第7层的叶子节点个数为2的7次方减去8/2.然后求出总的节点区,减去叶子节点数。

10.请描述对于线性表的顺序存储和链式存储结构的优缺点。举例说明在什么样的情况下选用顺序存储,什么情况下选用链式结构存储?

答:①空间的比较:顺序表开辟的是一段连续的空间,然后进行数据的增删改查。而单链表则是一次开辟一个节点的空间,用来存储当前的数据和指向下一个节点的指针。
②时间的比较:访问顺序表中的元素,采用下标进行访问,时间复杂度为O(1),而访问链表中的某一元素,需要遍历整个链表,直到找到元素为止。因此单链表的平均时间复杂度为O(n)。
但是在删除和插入元素时,因为顺序表是连续的,所以需要把被删除或插入的元素后面的元素全部后移或者前移,时间花销很大。而单链表在插入或删除元素时,只需要改变前驱节点的指针就可以了。因此顺序表插入删除元素的平均时间为0(n),而链表的时间复杂度为O(1)。
使用情况:
查询操作使用的比较频繁时,使用顺序表会好一些;在插入、删除操作使用的比较频繁时,使用单链表会好一些。

11.试各举一个实例,简要说明栈和队列在程序设计中所起的作用。

答:栈的特点是后进先出,所以在解决实际问题涉及到后进先出的情况时,可以考虑使用栈。例如:表达式的括号配对问题。设置一个栈,将读到的左括号入栈,每读入一个右括号,判断栈顶是否为左括号,若是,则出栈;否则,表示不匹配。
** 队列的特点是先进后出**。例如:在
电商秒杀的场景中
,许多请求发送到后台系统中,无数线程同时运行。如果最终的结果是要处理每一个请求,那么就将请求进行先后次序排序,输入到队列中,先输入的请求就先处理,凡是需要处理的请求都从队尾进入。

(二)编程题

1.能写出顺序表、单链表、双链表、单循环链表、双循环链表、顺序栈、顺序队、链栈、链队的结构体。

1.顺序表:

Typedef struct {
   Elemtype data[maxsize];
int length;
}Sqlist;

2.单链表:

Typedef struct Lnode{
   Elemtype data;
   Struct Lnode *next;
} Linknode;

3.双链表:

Typedef struct Lnode{
   Elemtype data;
   Struct Lnode *prior;
   Struct Lnode *next;
}Dlinknode;

4.单循环链表:

Typedef struct node{
ListData data;
struct node* link;
}ListNode, *ListLink;

5.顺序栈:

Typedef  struct {
    Elemtype data;
    Int top;
}Sqstack;

6.链栈:

Typedef struct linkNode{
  Elemtype data;
  Struct linknode  * next;
}LinkStnode;

7.顺序队:

Tyedef struct {
   Elemtype data[Maxsize];
   Int front,rear;
}Sqque;

8.链队:

Typedef struct qnode{
       Elemtype data;
       Struct qnode *next;
 }Datanode;

9.双循环链表

双向链表的每个结点需要连接前一个结点和后一个结点,所以需要定义两个指针域,分别指向前一个结点和后一个结点。

typedef struct tagNode
{
    int data;
    struct tagNode *pre,*next;
}Node,*LinkList;

2.写出折半查找的算法,并分析其最好情况、最坏情况及平均情况下的时间复杂度。

typedef int KeyType;//定义关键字类型为int
typedef struct
{
    KeyType key;//关键字项
    InfoType data;//其他数据项,类型为InfoType
}RecType;   //查找元素的类型
int BinSearch(RecType R[], int n, KeyType k)//折半查找算法
{
    int low=0,high=n-1,mid;//当前区间存在元素时循环
    while (low<=high)
    {
        mid=(low+high)/2;
        if (R[mid].key==k)      //查找成功返回
            return mid+1;
        if (R[mid].key>k)       //继续在R[low..mid-1]中查找
            high=mid-1;
        else
            low=mid+1;          //继续在R[mid+1..high]中查找
    }
    return 0;
}

最好时间复杂度:
最坏时间复杂度:
平均时间复度:

3.写出分块查找的算法,并分析其最好情况、最坏情况及平均情况下的时间复杂度。

#include <stdio.h>
#define MAXL 100    //数据表的最大长度
#define MAXI 20     //索引表的最大长度
typedef int KeyType;
typedef char InfoType[10];
typedef struct
{
    KeyType key;                //KeyType为关键字的数据类型
    InfoType data;              //其他数据
} NodeType;
typedef NodeType SeqList[MAXL]; //顺序表类型

typedef struct
{
    KeyType key;            //KeyType为关键字的类型
    int link;               //指向对应块的起始下标
} IdxType;
typedef IdxType IDX[MAXI];  //索引表类型

int IdxSearch(IDX I,int m,SeqList R,int n,KeyType k)
{
    int low=0,high=m-1,mid,i;
    int b=n/m;              //b为每块的记录个数
    while (low<=high)       //在索引表中进行二分查找,找到的位置存放在low中
    {
        mid=(low+high)/2;
        if (I[mid].key>=k)
            high=mid-1;
        else
            low=mid+1;
    }
    //应在索引表的high+1块中,再在线性表中进行顺序查找
    i=I[high+1].link;
    while (i<=I[high+1].link+b-1 && R[i].key!=k) i++;
    if (i<=I[high+1].link+b-1)
        return i+1;
    else
        return 0;
}

int main()
{
    int i,n=25,m=5,j;
    SeqList R;
    IDX I= {{14,0},{34,5},{66,10},{85,15},{100,20}};
    KeyType a[]= {8,14,6,9,10,22,34,18,19,31,40,38,54,66,46,71,78,68,80,85,100,94,88,96,87};
    KeyType x=85;
    for (i=0; i<n; i++)
        R[i].key=a[i];
    j=IdxSearch(I,m,R,n,x);
    if (j!=0)
        printf("%d是第%d个数据\n",x,j);
    else
        printf("未找到%d\n",x);
    return 0;
}

4.写出直接插入排序的算法,并分析其最好情况、最坏情况及平均情况下的时间复杂度。

#include <stdio.h>
#define MaxSize 20
typedef int KeyType;    //定义关键字类型
typedef char InfoType[10];
typedef struct          //记录类型
{
    KeyType key;        //关键字项
    InfoType data;      //其他数据项,类型为InfoType
} RecType;              //排序的记录类型定义

void InsertSort(RecType R[],int n) //对R[0..n-1]按递增有序进行直接插入排序
{
    int i,j;
    RecType tmp;
    for (i=1; i<n; i++)
    {
        tmp=R[i];
        j=i-1;            //从右向左在有序区R[0..i-1]中找R[i]的插入位置
        while (j>=0 && tmp.key<R[j].key)
        {
            R[j+1]=R[j]; //将关键字大于R[i].key的记录后移
            j--;
        }
        R[j+1]=tmp;      //在j+1处插入R[i]
    }
}

int main()
{
    int i,n=10;
    RecType R[MaxSize];
    KeyType a[]= {9,8,7,6,5,4,3,2,1,0};
    for (i=0; i<n; i++)
        R[i].key=a[i];
    printf("排序前:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    InsertSort(R,n);
    printf("排序后:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    return 0;

5.写出冒泡排序的改进算法,并分析其最好情况、最坏情况及平均情况下的时间复杂度。

#include <stdio.h>
#define MaxSize 20
typedef int KeyType;    //定义关键字类型
typedef char InfoType[10];
typedef struct          //记录类型
{
    KeyType key;        //关键字项
    InfoType data;      //其他数据项,类型为InfoType
} RecType;              //排序的记录类型定义
void BubbleSort1(RecType R[],int n)
{
    int i,j,k,exchange;
    RecType tmp;
    for (i=0; i<n-1; i++)
    {
        exchange=0;
        for (j=n-1; j>i; j--)   //比较,找出最小关键字的记录
            if (R[j].key<R[j-1].key)
            {
                tmp=R[j];  //R[j]与R[j-1]进行交换,将最小关键字记录前移
                R[j]=R[j-1];
                R[j-1]=tmp;
                exchange=1;
            }

        printf("i=%d: ",i);
        for (k=0; k<n; k++)
            printf("%d ",R[k].key);
        printf("\n");

        if (exchange==0)    //中途结束算法
            return;
    }
}
int main()
{
    int i,n=10;
    RecType R[MaxSize];
    KeyType a[]= {0,1,7,2,5,4,3,6,8,9};
    for (i=0; i<n; i++)
        R[i].key=a[i];
    printf("排序前:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    BubbleSort1(R,n);
    printf("排序后:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    return 0;
}

6.写出希尔排序的算法,并分析其最好情况、最坏情况及平均情况下的时间复杂度。

#include <stdio.h>
#define MaxSize 20
typedef int KeyType;    //定义关键字类型
typedef char InfoType[10];
typedef struct          //记录类型
{
    KeyType key;        //关键字项
    InfoType data;      //其他数据项,类型为InfoType
} RecType;              //排序的记录类型定义

void ShellSort(RecType R[],int n)   //希尔排序算法
{
    int i,j,gap;
    RecType tmp;
    gap=n/2;                //增量置初值
    while (gap>0)
    {
        for (i=gap; i<n; i++) //对所有相隔gap位置的所有元素组进行排序
        {
            tmp=R[i];
            j=i-gap;
            while (j>=0 && tmp.key<R[j].key)//对相隔gap位置的元素组进行排序
            {
                R[j+gap]=R[j];
                j=j-gap;
            }
            R[j+gap]=tmp;
            j=j-gap;
        }
        gap=gap/2;  //减小增量
    }
}

int main()
{
    int i,n=11;
    RecType R[MaxSize];
    KeyType a[]= {16,25,12,30,47,11,23,36,9,18,31};
    for (i=0; i<n; i++)
        R[i].key=a[i];
    printf("排序前:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    ShellSort(R,n);
    printf("排序后:");
    for (i=0; i<n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    return 0;
}

(三)选择题常用性质

一、二叉树

1、普通树

①树中结点数等于所有节点度数+1
②度为m的数中第i层至多有m^(i-1)个节点
以上两个可以自己推
③高度为h的m叉树至多((m^h)-1)/(m-1)个节点
④具有n个节点的m叉树的最小高度为:
logm[n(m-1)+1] m是底数,向上取整

2、完全二叉树

叶子节点数=度数为2的节点数+1
有n个节点的完全二叉树的深度为log2(n+1) 向上取整

3、平衡二叉树

它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

二、图

1、无向图、有向图

①若有n个顶点,那么可以n(n-1)/2个无向图
②若有n个顶点,那么可以n(n-1)个有向图
③子图:点包含于原图的点;生成子图:点就是原图的点
在无向图中,所有顶点的度的和是图中边的两倍

2、邻接矩阵(顺序存储)

所谓邻接就是点到点之间有几条边的关系,带权就写权,没有就写无穷
①无向图的边数是上三角或者下三角中非0元素的个数
②无向图的邻接矩阵是对称方阵
③对于点vi,其度数就是第i行的非零元素的个数
④有向图的邻接矩阵中,方向是行到列,也就是说,行是出度,列是入度(不为0的元素有几个,度就是几)

3、邻接表(链式存储)

①有向图的邻接表,在点后面还加了一个入度或者出度,所以有入度邻接表和出度邻接表。

请大家一起纠错,多多批评指正哦!

  • 45
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涟漪!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值