数据结构-简答题&算法题2

1、设n表示线性表中的元素个数, E表示存储数据元素所需的存储单元大小,D表示可以在数组中存储线性表的最大元素个数(D≥n),则采用顺序存储方式存储该线性表需要多少存储空间?

答案:

数组长度为D,每个数组元素占用E个存储单元,因此,该线性表采用顺序存储方式所需存储空间为E×D。

2、在什么情况下线性表使用顺序表存储比较好?

答案:

如果线性表很少进行插入和删除操作,但是常常进行查找操作,并且线性表中的最多元素个数已知,或线性表长度变化不大,使用顺序表存储比较好。

3、试以顺序表作存储结构,实现线性表就地置逆。

答案:

线性表的置逆即是将对称元素交换,设线性表的长度为n,则将线性表中第i个元素与第n-i-1个元素相交换。算法只需要一个辅助空间完成两个元素互换,因此空间复杂度为O(1)。注意到顺序表的下标从0开始,算法如下:

void Reverse(SeqList &L){
    int i,temp,n = L.length;
    for(i=0;i<n/2;i++){ /*此处i是数据元素的逻辑序号*/
        temp = L.data[i]; /*交换元素*/
        L.data[i] = L.data[n-i-1];
        L.data[n-i-1] = temp;
    }
}

4、设计一个时间复杂度为O(n)的算法,实现将数组A[n]中所有元素循环左移k个位置。

答案:

可以将这个问题看作是把序列ab转换成序列ba(a代表序列的前k个元素,b代表数组中余下的n-k个元素),先将a置逆得到a^{^{-1}}b,再将b置逆得到a^{-1}b^{-1},最后将a^{-1}b^{-1}置逆得到(a^{-1}b^{-1})^{-1}=ba。注意数组的置逆操作需要限定起始位置和终止位置。

void Converse(char A[],int n,int k){
    Reverse(A,0,k-1); /*将前k个元素置逆*/
    Reverse(A,k,n-1); /*将后n-k个元素置逆*/
    Reverse(A,0,n-1); /*将整个数组置逆*/
}

void Reverse(char A[],int from,int to){ /*将数组A[from]~A[to]置逆*/
    int i,temp;
    for(i=0;i<(to-from+1)/2;i++){
        /*交换元素*/
        temp = A[from+i];
        A[from+i] = A[to-i];
        A[to-i] = temp;
    }
}

5、设n表示线性表中的元素个数, P表示指针所需的存储单元大小,E表示存储数据元素所需的存储单元大小,则使用单链表存储方式存储该线性表需要多少存储空间(不考虑头结点)?

答案:

每个结点所需空间为P+E,线性表中的每个元素对应单链表中的一个结点,单链表中共有n个结点,则共需要n(P+E)个存储单元。

6、用顺序表表示集合,设计算法实现集合的求交集运算。

答案:

本题即是求C=A∩B。扫描表A,对A中的元素A.data[i],若它与表B中某个元素相同,则表示是交集的元素,将其放到表C中。

void Interest(SeqList A,SeqList B,SeqList &C){
    int i,j,k=0;
    for(i=0;i<A.length;i++){
        for(j=0;j<B.length;j++){
            if(A.data[i]==B.data[i]) /*如果是相同元素,则退出循环*/
                break;
        }
        if(j<B.length) /*是相同元素*/
            C.data[k++]=A.data[i];
        C.length=k; /*共有k个相同元素*/
    }
}

7、若频繁地对一个线性表进行插入和删除操作,该线性表采用什么存储结构比较好?

答案:

采用链接存储结构,例如单链表。在链表上进行插入和删除操作不需移动元素,在指针指向合适结点后,只需修改相应指针。最坏情况下,需要从头指针开始查找相应结点,移动指针的平均次数是表长的一半;采用顺序存储结构,插入和删除一个元素需要移动元素的平均个数为表长的一半,通常移动指针比移动元素花费的时间少得多。

8、将值为x的结点插入到不带头结点的单链表L中值为k的结点之前,若找不到值为k的结点,则将x插入到链表的末尾。

答案:

注意题目要求不带头结点,首先需要判断单链表是否为空,如果链表为空,则将值为x的结点作为表的唯一结点插入链表中,这需要修改头指针L;如果链表不为空,则需要查找值为k的结点,由于需要插在结点k之前,需要标记结点k的前驱结点。还要考虑一个边界情况:如果链表第一个结点的值为k,则将值为x的结点作为第一个结点插入链表中,这需要修改头指针。算法如下:

void Insert(Node *L,int x,int k){
    Node *s = NULL,*pre = NULL,*p = NULL;
    s=(Node *)malloc(sizeof(Node));
    s->data = x;
    if(L == NULL || L->data == k){ /*表头插入的特殊情况*/
        s->next = L;
        L = s;
    }
    pre = L; /*pre是辅助工作指针*/
    p = L->next; /*工作指针p初始化*/
    while(p != NULL && p->data != x){
        pre = p;
        p = p->next; /*两个工作指针都要后移*/
    }
    s->next = pre->next;
    pre->next = s; /*插在结点pre的后面*/
}

9、判断非空单链表是否递增有序。

答案:

若单链表的长度为1,则结论显然成立。若单链表的长度大于1,则需判断每个结点的值是否小于其后继结点的值,所以设两个工作指针p和q,p指向当前结点,q始终指向 p的后继(如果后继结点存在),在扫描过程中将p所指结点值和q所指结点值进行比较,然后p和q 同时后移。算法如下:

int Increase(Node *first){
    Node *p = NULL,*q = NULL;
    p = first->next; //指针p指向开始结点
    while (p->next != NULL){ //当p的后继结点存在,进行比较
        q-p->next;
        if (p->data<q->data) p-q;
        else return 0;
    }
    return l; //退出循环说明每个结点的值均小于其后继结点的值
}

10、已知非空线性链表由list指出,设计算法交换p所指结点与其后继结点在链表中的位置(设p所指结点不是链表的最后一个结点)。

答案:

在交换结点p和后继结点的位置时,不仅要考虑到结点内部指针的变化,还应该考虑结点p的前驱结点的指针,所以需要先查找结点p 的前驱结点。交换结点需要修改指针的顺序如图所示。

void Exchange(Node *list, Node *p){
    Node *pre = NULL,*temp = NULL;
    pre = list; //工作指针pre初始化
    while (pre->next != p) //循环直到pre指向p的前驱
        pre = pre->next;
    temp = p->next; //暂存结点p的后继
    pre->next = p->next;
    p->next = temp->next;
    temp->next = p;
}
  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值