王道考研408 数据结构 2.3 链表练习题

2.3

pre
typedef struct Lnode{
    ElemType data;
    struct Lnode *next;
}LNode,*LinkList;
typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;
}DNode, *DLinklist;
2.3.02

在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一,试编写算法以实现上述操作。

void del_x(LinkList &L, ElemType x)
{
    LNode *p = L->next, *pre = L, *q;//q是临时节点,好像就要用指针
    while(p!=NULL){
        if(p->data == x){
            q = p;
            p = p->next;
            pre->next = p;
            free(q);
        }
        else{
            pre = p;
            p = p->next;
        }//else
    }//while
}
2.3.03

设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值

void print_r(LinkList &L)
{
    if(L->next!=NULL) print_r(L->next);
   	if(L!=NULL) printf("%d",L->data);
}
void reverse(LinkList &L)
{
    if(L!=NULL) print_r(L);
  	return;
}
2.3.05

试编写算法将带头结点的单链表就地逆置,也就是空间复杂度为O(1)

//解法一,拆下来以后用头插法反插
LinkList reverse(LinkList &L)
{
    LNode *p,*r,*pre;
    p = L->next;L->next=NULL;
    while(p->next!=NULL){
        q = p->next;
        p->next = L->next;
        L->next = p;
        p = q;
    }
    return L;
}
//解法二,反向插,最后将位置指向L,两个时间复杂度都是O(n),空间复杂度为O(1)
LinkList reverse(LinkList &L)
{
    LNode *p,*r,*pre;
    p = L->next; r = p->next; p->next = NULL; 
 	while(r!=NULL){
        pre = r->next;
        r->next = p;
        p = r;
        r = pre;
    }   
    L->next = p;
    return L;
}
2.3.08

给定两个单链表,编写算法找出两个链表的公共结点

分析:如果存在公共结点,那两个链表的结尾都是相同的,所以可以先计算两个链表的长度,再减去插值寻找公共结点,时间复杂度为O(length1+length2)

LinkList find_1st(LinkList &L1, LinkList &L2)
{
    int len1 = Length(L1), len2 = Length(L2);
    int dist;
    LinkList p,q;
    if(len1>len2){
        p = L1->next; q = L2->next;
        dist = len1-len2;
    }
    else{
        p = L2->next; q = L1->next;
        dist = len2-len1;
    }
    while(dist--){
        p = p->next;
    }
    while(p!=NULL){
        if(p==q)//真的可以这么写吗?
            return p;
        else{
            p = p->next;
            q = q->next;
        }
    }
    return NULL;
}
2.3.11

将一个带头结点的单链表A分解为两个带头结点的单链表B和C,使得B表中含有原表中序号为奇数的元素,而C表中含有原表中序号为偶数的元素,且C表中顺序反过来,并保持相对顺序

思路:没啥区别,练练链表的手。有个很有意思的地方,就是这个怎么返回两个链表的同时读入一个链表,答案的做法是保留原来输入的链表作为其中一个结果进行输出,另一个用函数返回值进行返回。

LinkList divide(LinkList &A )
{
    LinkList B = (LinkList)malloc(sizeof(LNode));
    B->next = NULL;
    LNode *p=A->next, *q;
    LNode *tmp = A;//指向A的尾结点
    while(p!=NULL){
        tmp->next = p; tmp = p;
        p = p->next;
        if(p!=NULL){
            q = p->next;
            p->next = B->next;
            B->next = p;
            p = q;
        }
    }
    tmp->next = NULL;
    return B;
}
LinkList divide_myself(LinkList &A)
{
    LinkList B = (LinkList)malloc(sizeof(LNode));
    B->next = NULL;
    LNode *pr = A;
    LNode *cr = A->next;
    while(cr!=NULL){
        cr = cr->next;pr = pr->next;
        if(cr!=NULL){
            pre->next = cr->next;
            cr->next = B->next;
            B->next = cr->next;
            cr = pre->next;
        }
    }
    cr->next =NULL;//如果是奇数的情况下进行的定义
	return B;   
}
2.3.16

两个整数序列A=a1,a2,…,am和B=b1,b2,…,bn已经存入两个单链表中,设计一个算法判断B是否是A的连续子序列

int child_series(LinkList &A, LinkList &B)
{
    LNode *a = A, *b = B;
    LNode *p = a;//用于存档
    while(a&&b){
        if(a->data == b->data){
            a = a->next;
            b = b->next;
        }
        else{
            b = B;//回到表头
            p = p->next;
            a = p;
        }   
    }//while
    if(b == NULL) return 1;
    else return 0;
}
2.3.20

设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有prior(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq.在链表被启用前,其值均初始化为零,每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头.试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型.

void Locate(LinkList &L, ElemType x)
{
    LNode *s = L;
    LNode *sr = L, tmp;
    while(s!=NULL){
        if(s->data != x) s = s->next;
        else s->freq++;
    }
    if(s==NULL)//not found
        return -1;
    else//change the place
    { 
        while(sr->freq>s->freq&&sr!=NULL) sr = sr->next;
        if(sr!=NULL){//exist prior place
        	s->pred->next = s->next;
            s->next->pred = s->pred;
            sr->pred->next = s;
           	s->pred = sr->pred; 
            s->next = sr;
            sr->pred = s;
        }// end if
    }//end else
}
//也可以考虑从s开始往前遍历,这样的效率更高但是我不想再写一版了。
2.3.22

已知一个带有表头结点的单链表,结点结构为 data link 假设该链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的 算法,查找链表中倒数第k个位置,如果成功则输出该点的data域的值,并返回1,否则返回0

  1. 基本思路

    直接取两个节点,一个在后面k个位置,后面那个到NULL了前面那个刚好再倒数第k个位置。

  2. 代码

int find_reverse_k(int k, LinkList &L)
{
    LNode *pre = L, *lat = L;
    for(int i = 0; i<k; i++){
        lat = lat->next;
        if(lat == NULL) return 0;
    }
   	while(lat!=NULL){
        lat = lat->next;
        pre = pre->next;
    }
   	printf("%d",pre->data);
    return 1;
}
2.3.25

设线性表L=(a1,a2,a3,…,an-2,an-1,an),采用带头节点的单链表保存。设计算法,重新排列L中的节点,得到线性表L’=(a1,an,a2,an-1,a3,an-2,…)。

  1. 思路

    我想的是先建立头结点,然后先用尾插法把后半部分给插上,再正向进行一个隔一个的差。这样的时间复杂度是O(n),空间复杂度是O(1)

  2. (typora的换行真的是一坨沈阳美食家)代码

LinkList reshape(LinkList &L)
{
    LinkList B = (LinkList)malloc(sizeof(LNode));
    LNode *tmp;
    B->next = NULL;
    LNode *mid=L->next, *fin=L->next;
    int length=0;//the length of line
    //first find the mid place
    while(fin!=NULL){
        mid = mid->next;
        fin = fin->next;
        if(fin!=NULL){
            fin = fin->next;
            length+=2;
        }
        else length+=1;
    }
    // mid is deemed to be the later part and without the mid point
  	while(mid!=NULL)
    {
        tmp = mid;
 		mid->next = B->next;
        B->next = mid;
       	mid = tmp->next;
    }
    // after this, it is (an,an-1,...a(n/2))
    tmp = L->next; fin = tmp->next; mid = B;
    for(i = 0; i<(length+1)/2; i++){
        tmp->next = mid->next;
        mid->next = tmp;
        mid = mid->next->next;
        tmp = fin;
        fin = fin->next;
    }
    return B;
}
//当然,也可以像答案那样直接把后半部分进行逆置,然后前后加,也可以得到一样的结果
void change_list(Node *h)
{
    Node *p,*q,*r,*s;
    p = q = h;
    while(q->next != NULL){
        p = p->next;
        q = q->next;
    	if(q->next != NULL) q = q->next;
    }
    q = p->next;//p所指节点为中间节点,q为后半段链表的首节点,我的做法提前next了,所以不一样
    p->next = NULL;
    while(q!=NULL){//将链表后半段逆置
        r = q->next;
        q->next = p->next;
        p->next = q;
        q = r;
    }
    s = h->next;
    q = p->next;
    p->next = NULL;
    while(q!=NULL)
    {
        r = q->next;
        q->next = s->next;
        s->next =q;
        s = q->next;
        q = r;
    }
}
后记

链表有点难,但是很suang口,写到后面还是有点high的

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值