22王道数据结构2.3.7算法题

1、设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。

非递归常规解(带头结点):

//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
    int x;
    LNode *p = L->next;   //p用来遍历单链表
    LNode *q;             //q用来暂时替代p,然后将结点free
    LNode * pre = L;
    printf("请输入删除重复值:");
    scanf("%d", &x);
    while(p != NULL)
    {
        if(p->data == x)
        {
            q = p;
            pre->next = q->next;
            p = p->next;
            free(q);
        }
        else
        {
            pre = p;       //pre要一直为p的前驱
            p = p->next;
        }
    }
}

递归解(带头结点):

//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
    LNode *p = L->next;
    LNode *pre = L;
    LNode *q;
    int x;
    printf("请输入x的值:");
    scanf("%d", &x);
    if(p == NULL)
        return;
    if(p->data == x)
    {
        pre->next = p->next;
        q = p;
        free(q);
        Del_X_3(L);
    }
    else
        Del_X_3(L->next);
}

递归解(不带头结点):

//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
    LNode *p;
    int x;
    printf("请输入x的值:");
    scanf("%d", &x);
    if(L == NULL)
        return;
    if(L->data == x)
    {
        p = L;
        L = L->next;
        free(p);
        Del_X_3(L);
    }
    else
        Del_X_3(L->next);
}

2、在带头结点的单链表中,删除所有值为x的结点,并释放空间。

非递归解(推荐):

//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
    int x;
    LNode *p = L->next;   //p用来遍历单链表
    LNode *q;             //q用来暂时替代p,然后将结点free
    LNode * pre = L;
    printf("请输入删除重复值:");
    scanf("%d", &x);
    while(p != NULL)
    {
        if(p->data == x)
        {
            q = p;
            pre->next = q->next;
            p = p->next;
            free(q);
        }
        else
        {
            pre = p;       //pre要一直为p的前驱
            p = p->next;
        }
    }
}

递归解:

//删除所有值为x的结点(带头结点)
void Del_X_3(LinkList &L)
{
    LNode *p = L->next;
    LNode *pre = L;
    LNode *q;
    int x;
    printf("请输入x的值:");
    scanf("%d", &x);
    if(p == NULL)
        return;
    if(p->data == x)
    {
        pre->next = p->next;
        q = p;
        free(q);
        Del_X_3(L);
    }
    else
        Del_X_3(L->next);
}

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

暴力解1,时间复杂度O(n^2)

//从尾到头输出每个结点的值
void R_Print(LinkList L)
{
    LNode *p = L->next, *r = NULL;   //r用来限制每次遍历的长度
    while(p->next != r)
    {
        p = p->next;
        if(p->next == r)
        {
            printf("%d", p->data);
            r = p;
            p = L->next;
        }
    }
    printf("%d", p->data);  //输出第一个元素(头结点之后第一个结点)
}

暴力解2

void R_Print(LinkList L)
{
    LNode *p = L->next, *r = NULL;
    while(p)
    {
        if(p->next == r)
        {
            printf("%d", p->data);
            r = p;
            p = L->next;
        }
        else
            p = p->next;
    }
}

也可以用栈,这里不写了。

4、在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。

//删除唯一最小值结点(带头结点)
void Delete_Min(LinkList &L)
{
    LNode *p = L->next;   //p用来遍历结点,与q作大小比较
    LNode *q = p;        //q指向当前最小结点
    LNode *pre = L;      //pre始终指向q的前驱结点
    while(p->next != NULL)
    {
        if(q->data > p->next->data)
        {
            pre = p;
            q = p->next;
        }
        p = p->next;
    }
    pre->next = q->next;
    free(q);

}

5、将带头结点的单链表就地逆置,“就地”指辅助空间复杂度为O(1)。

头结点第一个元素结点 看作整体一起断开,再用头插法:

//就地逆置(带头结点)
void Reverse(LinkList &L)
{
    LNode *p = L->next;  //p指向头结点后一个结点
    LNode *r = p->next;  //r指向p结点后一个结点,用来当断链后的第一个结点
    LNode *q;            //暂时替代r
    p->next = NULL;      //将p结点之后断开
    while(r)
    {
        q = r;            //用q结点暂存r结点来使用头插法
        r = r->next;      //趁未断链时将r向后移动
        q->next = L->next; 
        L->next = q;
    }
}

将头结点断开,再用头插法:

//就地逆置(带头结点)
void Reverse(LinkList &L)
{
    LNode *p = L->next;  //p指向头结点后一个结点
    LNode *r;
    L->next = NULL;
    while(p)
    {
       r = p->next;
       p->next = L->next;  //p结点指向头结点的后继
       L->next = p;
       p = r;
    }
}

6、带头结点的单链表L,设计一个算法使其元素递增有序。

算法和上一题相似,算法很巧妙,建议大家把这题好好琢磨一下。

//递增排序(带头结点)
void Sort(LinkList &L)
{
    LNode *p = L->next;    //用来指向插入元素
    LNode *r = p->next;
    LNode *pre;
    p->next = NULL;
    p = r;
    while(p)
    {
        r = p->next;
        pre = L;          //pre每次都要从首个元素结点与p结点作比较
        while(pre->next != NULL && pre->next->data < p->data)
            pre = pre->next;     //判断p插入的位置,并使pre指向插入位置的前驱结点
        p->next = pre->next;
        pre->next = p;
        p = r;
    }
}

7、在带头结点的无序单链表中,编写一个函数,删除所有介于给定的两个值之间的元素。

//删除结点元素值在(s,t)内的结点
void RangeDelete(LinkList &L)
{
    int s, t;
    LNode *p = L->next, *q;
    LNode *pre = L;
    printf("请输入(s,t)的值(空格隔开):");
    scanf("%d %d", &s, &t);
    while(p)
    {
        q = p;          //q用来暂存p结点
        p = p->next;    //趁p还未改变时将p往后移动
        if(q->data < t && q->data > s)
        {
            pre->next = q->next;
            free(q);
        }
        else
            pre = q;       //不满足条件则将pre往后移
    }
}

改进(更直观)

void Reverse(LinkList &L)
{
    LNode *p = L->next;
    LNode *q;
    LNode *pre = L;
    while(p)
    {
        if(p->data < t && p->data >s)
        {
            q = p;
            p = p->next;
            pre->next = q->next;
            free(q);
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
}

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

虽然有点长,但是挺简单的。

LinkList Search_1st_Common(LinkList L1, LinkList L2)
{
    int num = 0;
    int len1 = Length(L1), len2 = Length(L2);  //计算两个表的表长
    LNode *longlist, *shortlist;           //指向两个链表
    if(len1 >= len2)
    {
        longlist = L1->next;
        shortlist = L2->next;
        dist = len1 - len2;               //表长之差
    }
    else
    {
        longlist = L2->next;
        shortlist = L1->next;
        dist = len2 - len1;
    }
    while(dist--)
        longlist = longlist->next;
    while(longlist)
    {
        if(shortlist->data == longlist->data)
        {
            printf("%d", shortlist->data);
            num++;
        }
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    if(num == 0)
        printf("无公共结点!");
}

9、给定一个带头结点的单链表,设head为头指针,结构结点为(data,next),data为整型元素,next为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间(不允许使用数组作为辅助空间)。

可以先对单链表排序,再输出,也可以直接暴力解:每次遍历出一个最小值并输出,再接着遍历,直到全部输出完成。

//递增输出并释放空间
void Min_Delete(LinkList &L)
{
    while(L->next != NULL)    //当元素结点还存在时一直循环
    {
        LNode *p = L->next;
        LNode *pre = L;     //指向q的前驱结点
        LNode *q = p;       //指向最小值的结点
        while(p->next != NULL)
        {
            if(q->data > p->next->data)
            {
                q = p->next;
                pre = p;
            }
            p = p->next;
        }
        printf("%d ",q->data);
        pre->next = q->next;
        free(q);        //每次循环找到最小值结点并释放
    }
}

10、将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A中含有原表中序号为奇数的元素,B中含有原表中序号为偶数的元素,且保持相对位置不变。

这是我的暴力解,有点过于繁琐,我本来想用while里面装两次分配结点,中间用if隔开,这样完成一次就是偶数,不需要判断次数,但是不小心搞复杂了。大家可能看不太懂,在草稿纸上跟一边就明白了,道理和前几题差不多,我把整个源码都放上来了,接口可以自己调整一下。

#include <iostream>

using namespace std;

typedef struct LNode{              //定义单链表结点类型
    int data;                       //数据域:每个结点存放一个数据元素
    struct LNode *next;             //指针域:指针指向下一个结点
}LNode, *LinkList;

//拆为A、B两表,A中含有原表中序号为奇数的元素,B为偶数
LinkList DisCreate_1(LinkList &A)
{
    int num = 0;   //用来记录当前发现了几个结点
    LinkList B;
    B = (LinkList)malloc(sizeof(LNode));
    B->next =NULL;

    LNode *p = A->next;
    num++;        //发现第一个结点
    LNode *pre = A;    //pre指向p的前驱结点
    LNode *q, *r = B;  //r用来指向B链表的尾部
    while(p->next)
    {
        q = p;
        p = p->next;
        num++;    //又发现结点
        pre->next = p;
        r->next = q;
        q->next = NULL;
        r = q;
        if(p->next)
        {
            pre = p;
            p= p->next;
            num++;  //又发现结点
        }
     }
     if(num % 2 == 1)
     {
        r->next = p;       //最后一个结点位序为偶数时不需要执行这两句
        pre->next =NULL;
     }
     while(B->next)
     {
         printf("%d", B->next->data);
         B = B->next;
     }
     printf("\n");
     while(A->next)
     {
         printf("%d",A->next->data);
         A = A->next;
     }
     return B;
}


int main()
{
    LinkList A;              //声明一个指向单链表的指针
    int x;
    A = (LinkList)malloc(sizeof(LNode)); //建立头结点
    A->next = NULL;
    LNode *s,*r = A;
    printf("请输入插入的值:\n");                     //建立表尾指针
    scanf("%d", &x);
    while(x != 9999)
    {
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    r->next = NULL;          //表尾指针后继置空


    DisCreate_1(A);
    return 0;
}

标准答案的确比我的简单,直接判断这一次识别的结点的奇偶性,而且不进行断链操作:

//拆为A、B两表,A中含有原表中序号为奇数的元素,B为偶数
LinkList DisCreate_1(LinkList &A)
{
    int i = 0;   //用来记录A中结点的序号
    LinkList B;
    B = (LinkList)malloc(sizeof(LNode));
    B->next =NULL;
    LNode *ra = A, *rb = B;    //分别指向A表和B表的尾结点
    LNode *p = A->next;
    A->next = NULL;     //将A断开,相当于置新
    while(p)
    {
        i++;
        if(i % 2 == 0)
        {
            rb->next = p;
            rb = p;
        }
        else
        {
            ra->next = p;
            ra = p;
        }
        p = p->next;
    }
    ra->next = NULL;
    rb->next = NULL;
    return B;
}

11、设C={a1,b1,a2,b2,……,an,bn}为线性表,采用带头结点的hc单链表存放,设计一个就地算法,将其拆为A={a1,a2,……,an}和B{bn,……,b2,b1}。

暴力解:这题很简单,按照上题的两种方法将C拆为A和B,之后再将B逆置即可,奉上源码:

#include <iostream>

using namespace std;

typedef struct LNode{              //定义单链表结点类型
    int data;                       //数据域:每个结点存放一个数据元素
    struct LNode *next;             //指针域:指针指向下一个结点
}LNode, *LinkList;


LinkList DisCreate_2(LinkList &C)
{
    int i = 0;
    LinkList B, A;
    B = (LinkList)malloc(sizeof(LNode));
    A = (LinkList)malloc(sizeof(LNode));
    B->next = NULL;
    A->next = NULL;
    LNode *ra = A, *rb = B, *p = C->next;
    while(p)
    {
        i++;
        if(i % 2 == 1)
        {
            ra->next = p;
            ra = p;
        }
        if(i % 2 == 0)
        {
            rb->next = p;
            rb = p;
        }
        p = p->next;
    }
    ra->next = NULL;
    rb->next = NULL;

    //接下来对B链表进行逆置
    LNode *q = B->next->next;
    B->next->next = NULL;
    LNode *r;
    while(q)
    {
        r=q;
        q = q->next;
        r->next = B->next;
        B->next = r;
        p = r;
    }
    
    /*//也可以这样逆置
    void Reverse(LinkList &L)
    {
        LNode *p = L->next;  //p指向头结点后一个结点
        LNode *r;
        L->next = NULL;
        while(p)
        {
           r = p->next;
           p->next = L->next;  //p结点指向头结点的后继
           L->next = p;
           p = r;
        }
    }    */

    //下面进行输出
    while(A->next)
    {
        printf("%d", A->next->data);
        A = A->next;
    }
    printf("\n");
    while(B->next)
    {
        printf("%d", B->next->data);
        B = B->next;
    }


}


int main()
{
    LinkList C;              //声明一个指向单链表的指针
    int x;
    C = (LinkList)malloc(sizeof(LNode)); //建立头结点
    C->next = NULL;
    LNode *s,*r = C;
    printf("请输入插入的值:\n");                     //建立表尾指针
    scanf("%d", &x);
    while(x != 9999)
    {
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    r->next = NULL;          //表尾指针后继置空


    DisCreate_2(C);
    return 0;
}

标准答案:将属于B链表的结点头插法建表,不想写了。

12、在一个递增有序的单链表中,有数值相同的元素存在。设计算法去掉相同的元素(保留一个)。

刚开始因为收到顺序表里类似题的影响,所以思维局限了,想的是记录第一个相等值的点和最后一个相等值的后驱点,然后链接,但是很麻烦。

所以改变思路,可以遍历链表,当该结点值等于后驱结点时,将后驱结点删除,不等时后移。

LinkList Del_Same(LinkList &L)
{
    LNode *p = L->next;
    LNode *q;
    while(p->next)
    {
        if(p->data == p->next->data)   //当后一个结点值与前一个相等时,将后一个删除
        {
            q = p->next;
            p->next = p->next->next;
            free(q);   //也可以不释放q,因为链已经改变
        }
        else
            p = p->next;
    }
}

13、假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。编写算法将这两个单链表归并为一个按元素递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。

第一次程序出了bug,打印完Lc中排好序的元素后,一直在重复打印最后一个结点的值,最后发现时因为最后一个结点的next指向了自己

LinkList MergeList(LinkList &La, LinkList &Lb)
{
    LinkList Lc;
    LNode *qa, *qb;     //pb和pa的前置指针
    LNode *pa = La->next, *pb = Lb->next, *pc;
    Lc = pc = La;
    _____________
    while(pa && pb)
    {
        if(pa->data < pb->data)    //若pa结点值小于pb结点,则对pa结点进行头插法
        {
            qa = pa;        //用qa保存pa的前置结点
            pa = pa->next;   //将pa指针向后移
            qa->next = Lc->next;
            Lc->next = qa;
            pc = qa;              //pc始终指向Lc->next
        }
        else if(pa->data > pb->data)
        {
            qb = pb;        //用qb保存pb的前置结点
            pb = pb->next;  //将pb指针向后移
            qb->next = Lc->next;
            Lc->next = qb;
            pc = qb;
        }
        else         //相等时取La中的元素,并删除Lb中的结点
        {
            qa = pa;
            pa = pa->next;
            qa->next = Lc->next;
            Lc->next = qa;
            pc = qa;
            qb = pb;
            pb = pb->next;
            free(qb);
        }
    }
    while(pa)
    {
        qa = pa;
        pa = pa->next;   
        qa->next = Lc->next;
        Lc->next = qa;
        pc = qa;
    }
    while(pb)
    {
        qb = pb;
        pb = pb->next;
        qb->next = Lc->next;
        Lc->next = qb;
        pc = qb;
    }
    free(Lb);
    while(pc)
    {
        printf("%d", pc->data);
        pc = pc->next;
    }
}

于是进行了修改:在下划线处增加一条Lc->next = NULL;即可

14、设A和B是两个带头结点的单链表,其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表C,要求不破坏A、B的结点。

void Get_Common(LinkList &La, LinkList &Lb)
{
    LinkList Lc = La;
    LNode *pa = La->next, *pb = Lb->next;
    LNode *s, *r = La;  //*s用来申请新的结点空间,*r用来使用尾插法
    while(pa && pb)
    {
       if(pa->data < pb->data)
            pa = pa->next;
        else if(pa->data > pb->data)
            pb = pb->next;
        else
        {
            s = (LinkList)malloc(sizeof(LNode));
            s->data = pa->data;
            r->next = s;
            r = s;
            pa = pa->next;
            pb = pb->next;
        }
    }
    r->next = NULL;
    while(Lc->next)
    {
        printf("%d", Lc->next->data);
        Lc = Lc->next;
    }
}

15、已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存于A链表中。

进行比较,把较小的结点释放,因为后续肯定不会有与之相等的结点。

void Union(LinkList &La, LinkList &Lb)
{
    LNode *pa = La->next, *pb = Lb->next;
    LNode *r = La;     //*s用来申请新的结点空间,*r用来使用尾插法
    LNode *u;          //用来释放结点
    while(pa && pb)
    {
       if(pa->data < pb->data)
       {
           u = pa;
           pa = pa->next;
           free(u);
       }
        else if(pa->data > pb->data)
        {
            u = pb;
            pb = pb->next;
            free(u);
        }
        else
        {
            r->next = pa;
            r = pa;
            pa = pa->next;
            u = pb;
            pb = pb->next;
            free(u);
        }
    }
    while(pa)           //A未遍历完,B已遍历完时释放A中的所有剩余结点
    {
        u = pa;
        pa = pa->next;
        free(u);
    }
    while(pa)          //B未遍历完,A已遍历完时释放A中的所有剩余结点
    {
        u = pb;
        pb = pb->next;
        free(u);
    }
    r->next = NULL;
    while(La->next)
    {
        printf("%d", La->next->data);
        La = La->next;
    }
}

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

#include <iostream>

using namespace std;

typedef struct LNode{              //定义单链表结点类型
    int data;                       //数据域:每个结点存放一个数据元素
    struct LNode *next;             //指针域:指针指向下一个结点
}LNode, *LinkList;


LinkList List_TailInsert(LinkList &L)
{
    int x;
    L = (LinkList)malloc(sizeof(LNode)); //建立头结点
    L->next = NULL;
    LNode *s,*r = L;
    printf("请输入插入的值:\n");                     //建立表尾指针
    scanf("%d", &x);
    while(x != 9999)
    {
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    r->next = NULL;          //表尾指针后继置空
    return L;               //返回头结点L
}


int Pattern(LinkList &La, LinkList &Lb)
{
    LNode *pa = La->next, *pb = Lb->next, *q = La->next; //q指向pa的前置结点
    while(pb && pa)      //若括号里只有pb,则匹配失败时为死循环
    {
        if(pb->data == pa->data) //A链和B链当前元素值相同则比较下一个元素
        {
            pb = pb->next;
            pa = pa->next;
        }
        else
        {
            q = q->next;
            pa = q;            //从A上次开始进行比较的结点的下一个结点开始比较
            pb = Lb->next;     //B从头开始匹配
        }
    }
    if(pb == NULL)
        return 1;
    return 0;
}


int main()
{
    int flag;
    LinkList La;              //声明一个指向单链表的指针
    LinkList Lb;
    List_TailInsert(La);
    List_TailInsert(Lb);
    if(flag = Pattern(La, Lb))
        printf("B是A的子序列!");
    else
        printf("B不是A的子序列!");
    return 0;
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值