一.单链表习题(4)

二.2王道课后题

第四部分

20

image-20210412100934122

我的想法:每次更新完freq的值后,遍历链表中freq的值,最大的排在最前,并且排当前的位置.

由于依次插入的特性,一定是大的在前面,只要向前找就行

找到等于x的结点后,先将他摘下来,然后依次向前查找(因为后面的次序一定小于它),一直到头结点之前,插入到频率大于它的后面(实现值相同,先访问的在前)

这是一道包括了查找,删除,增加,以及初始化等操作的综合题

//typedef struct LinkList{ 
//报错
typedef struct LNode{
    int data;
    int freq; //频率
    struct LNode *prior;
    struct LNode *next;
}LNode,*LinkList;


void insertListForInit(LinkList &l){
    l=new LNode;
    l->prior=NULL;
    l->next=NULL;
    LNode *r=l;//尾指针
    int a;
    cout<<"生成单链表,输入999结束"<<endl;
    scanf("%d",&a);
    while(a!=999){
        LNode *p=new LNode;
        p->data=a;
        p->freq=0;//初始频率0
        p->prior=r;
        r->next=p;
        r=p;
        scanf("%d",&a);
    }
    r->next=NULL;


}
代码
LNode* Locate(LinkList &l,int x){
    LNode *p=l->next;
    //如果有,找到了x的位置,没找到p为空
    while(p!=NULL && p->data!=x){
        p=p->next;
    }
    //如果没找到
    if(!p){
        cout<<"没有x"<<endl;
        exit(0);//我感觉这里就写return NULL;就行

    }
    //找到了继续,之前的返回了,不用写else
    p->freq++;
    //摘出p
    //保留p的前置指针
    LNode *q=p->prior;
    p->next->prior=p->prior;
    p->prior->next=p->next;
    //注意是小于等于,等于是不行的
    //找到插入位置q后
    while(q!=l &&q->freq<=p->freq){      
        q=q->prior;
    }
    //q后面插入q
    q->next->prior=p;
    p->next=q->next;
    q->next=p;
    p->prior=q;
    return p;

}
演示
 LinkList l;
    cout<<"初始化,建议1234"<<endl;
    insertListForInit(l);
    Locate(l,1);
    cout<<"输入1后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
    Locate(l,2);
    Locate(l,2);
    cout<<"两次输入2后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
    Locate(l,3);
    Locate(l,3);
    cout<<"两次输入3后,当前最前面的是"<<l->next->data<<"他的次序是"<<l->next->data<<endl;
结果
初始化,建议1234
生成单链表,输入999结束
1
2
3
4
999
输入1后,当前最前面的是1他的次序是1
两次输入2后,当前最前面的是2他的次序是2
两次输入3后,当前最前面的是3他的次序是3

21

image-20210412100947590

image-20210412101008550

我的想法比较简单,两遍遍历,第一遍知道长度,然后长度-k+1就是倒数第k个结点的位序.

这是我看了书之后的想法

倒数第k个元素,距离最后相差k-1个元素

当p从0走k个位置的时候,q开始出发

p和q差距k-1个元素

当p走到最后的时候,q到了倒数第k个位置

王道书上

p是走到了最后的后一个,此时p与q相差k个元素

image-20210412204520282

image-20210412200311618

代码

王道感觉比较完整

int findCountDownK02(LinkList l,int k){
    int count=0;
    LNode *p=l->next;
    LNode *q=l->next;//p先走,q后走
    while(p!=NULL){
        if(count<k){
            count++;

        }else{
            q=q->next;
        }
        p=p->next;

    }
    if(count<k){
        return 0;
    }
    cout<<"倒数第"<<k<<"个元素是"<<q->data<<endl;
    return 1;
}

int findCountDownK(LinkList l,int k){
    LNode *p=l->next;
    LNode *q=l->next;//p先走,q后走
    int i=1;
    while(p!=NULL && i<k){//p先走
        i++;
        p=p->next;

    }
    if(p==NULL){
        return 0;//没有k个元素
    }
    while(p->next!=NULL){//一起走
        q=q->next;
        p=p->next;
    }
    cout<<"倒数第"<<k<<"个元素是"<<q->data<<endl;
    return 1;


}
演示
 LinkList l;
    initList_H(l);
    insertListByOrder_H(l,1,1);
    insertListByOrder_H(l,2,2);
    insertListByOrder_H(l,3,3);
    insertListByOrder_H(l,4,4);
    insertListByOrder_H(l,5,5);
    insertListByOrder_H(l,6,6);
    printList_H(l);
    cout<<"倒数第3个"<<endl;
    if(findCountDownK(l,1)){
        cout<<"找到了"<<endl;
    }else{
        cout<<"没找到"<<endl;
    }
    
结果
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
单链表的第6个值是:6
倒数第3个
倒数第1个元素是6
找到了

22

image-20210412101225379

我一开始想成是字符串匹配问题,想复杂了,这里只是找共同的后缀.

先分别算出两个的长度,尾端对齐,从短的那边开始依次找,相同的即可.

注意要找到公共结点长度的前一个,比较next是否一样

image-20210413110507495

代码
LNode* findCommon02(LinkList a, LinkList b){
    LNode *p=a;
    int lenA=length_H(a);
    LNode *q=b;
    int  lenB=length_H(b);
    //改善 不用if判断,通过for循环条件控制
    //从头结点开始,因为要判断next,走长度差距位
    for(;lenA>lenB;lenA--){
        p=p->next;
    }
    for(;lenA<lenB;lenB--){
        q=q->next;
    }
    //两者同时为空和非空,判断一个即可
    while(p->next!=q->next && p->next!=NULL ){
        p=p->next;
        q=q->next;
    }
    
    if(p!=NULL){
        return p;
    }
    return NULL;

}

我写的丑陋代码,可无视,我想错了,应该是公共结点,看地址

LNode* findCommon(LinkList a, LinkList b){
    LNode *p=a->next;
    int lenA=0;
    LNode *q=b->next;
    int  lenB=0;
    while(p!=NULL){
        lenA++;
        p=p->next;
    }
    while(q!=NULL){
        lenB++;
        q=q->next;
    }
    //改善
    if(lenA>lenB){
        p=a->next;
        q=b->next;
        
        for(int i=0;i<(lenA-lenB);i++){
            p=p->next;
        }
        while(p->data!=q->data && p!=NULL ){
            p=p->next;
            q=q->next;

        }
    }
    if(p!=NULL){
        return p;
    }
    return NULL;

}
void insertNodeNext(LNode *a,LNode *b){
    b->next=a->next;
    a->next=b;
}
演示
 LinkList l;
    initList_H(l);
    insertListByOrder_H(l,1,1);
    insertListByOrder_H(l,2,2);
    insertListByOrder_H(l,3,3);
    //公共结点456
    LNode *h=(LNode *)malloc(sizeof(LNode));
    h->data=4;
    LNode *j=(LNode *)malloc(sizeof(LNode));
    j->data=5;
    LNode *k=(LNode *)malloc(sizeof(LNode));
    k->data=6;
    //找到第3个位置的结点a
    LNode *a=getValueByOrder_H(l,3);
    //在a后面插入公共结点456
    insertNodeNext(a,h);
    insertNodeNext(h,j);
    insertNodeNext(j,k);
    cout<<"单链表1"<<endl;
    printList_H(l);
    
    LinkList l2;
    initList_H(l2);
    insertListByOrder_H(l2,1,7);
    insertListByOrder_H(l2,2,8);
    //找到第2个位置的结点b
    LNode *b=getValueByOrder_H(l2,2);
    //在b后面插入
    insertNodeNext(b,h);
    insertNodeNext(h,j);
    insertNodeNext(j,k);
    cout<<"单链表2"<<endl;
    printList_H(l2);
    if(findCommon02(l,l2)!=NULL){
        cout<<"共同的数字是"<<findCommon(l,l2)->data<<endl;

    }else{
        cout<<"没有"<<endl;
    }   

结果
单链表1
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
单链表的第6个值是:6
单链表2
单链表的第1个值是:7
单链表的第2个值是:8
单链表的第3个值是:4
单链表的第4个值是:5
单链表的第5个值是:6
共同的数字是4

23

image-20210412101249331

我的想法是带标志位(初始0)的遍历删除.时间复杂度o(n),每次遍历到,标志位++,当标志位>1的时候,删除改结点

我的想法不对,不是删除和某个数相同的绝对值,而是所有绝对值相同的都删除

时间复杂度尽可能高,怎么实现??

时间上尽可能高==空间换时间

书上:通过辅助数组,由于长度<=n,需要n+1位

代码
//这题用的指针来表示数组,可以学习一下
void deleteAbsEqual02(LinkList &l,int n){
    //int a[n+1];
    int* a=new int[n+1];
    //初始化为0 
    for(int i=0;i<=n;i++){
        *(a+i)=0;
    }
    LNode *p=l;
    //删除一遍都要保留前指针
    while(p->next!=NULL){
        //三目运算符,也可以用abs(p->next->data)
        int m=p->next->data>0?p->next->data:-p->next->data;
        //完全可以用a[m]
        if(*(a+m)==0){
            *(a+m)=1;
            p=p->next;
        }
        else{
        //删除结点
            LNode *r=p->next;
            p->next=r->next;
            free(r);
        }      
        
    
    }
    //数组是申请的
    delete[] a;
    

}

错误示范

void deleteAbsEqual(LinkList &l,int x){
    LNode *p=l;
    int flag=0;
    while(p->next!=NULL){
        if(abs(p->next->data)==x){
            flag++;
        }
        //删除该结点
        if(flag>1 && abs(p->next->data)==x){
            //别忘了释放空间
            LNode *r=p->next;
            p->next=p->next->next;
            free(r);
        }else{
            p=p->next;
        }
    }
}

演示
LinkList l;
    initList_H(l);
    insertListByOrder_H(l,1,1);
    insertListByOrder_H(l,2,3);
    insertListByOrder_H(l,3,-3);
    insertListByOrder_H(l,4,3);
    insertListByOrder_H(l,5,5);
    insertListByOrder_H(l,6,-5);
    insertListByOrder_H(l,7,3);
    printList_H(l);
    cout<<"删除重复的3的绝对值"<<endl;
    deleteAbsEqual02(l,3);
    printList_H(l);
结果
单链表的第1个值是:1
单链表的第2个值是:3
单链表的第3个值是:-3
单链表的第4个值是:3
单链表的第5个值是:5
单链表的第6个值是:-5
单链表的第7个值是:3
删除重复的3的绝对值
单链表的第1个值是:1
单链表的第2个值是:3

24

image-20210412101312680

若有环,最后一定会回到某个位置,他的next是前面出现过的值.

我的想法:辅助数组,保存所有的地址

比较重要的是,循环那段代码

image-20210413143021521

image-20210413143037469

代码
LNode* findCircle(LinkList l){
    LNode *fast=l;
    LNode *slow=l;
    //fast!=NULL slow!=NUll也不行
    //有可能空指针
    //这个循环 很有讲究
    while(slow!=NULL && fast->next!=NULL){
        fast=fast->next->next;//走两步
        slow=slow->next;
        if(slow==fast) break; //相遇

    }
    while(slow==NULL ||fast->next==NULL) return NULL;
    LNode *p1=l;    //从头走
    LNode *p2=fast; //相遇点
    while(p1!=p2){
        p1=p1->next;
        p2=p2->next;
    }
    return p1;
演示
LinkList l;
    initList_H(l);
    insertListByOrder_H(l,1,1);
    insertListByOrder_H(l,2,2);
    insertListByOrder_H(l,3,3);
    LNode *h=(LNode *)malloc(sizeof(LNode));
    h->data=4;
    LNode *j=(LNode *)malloc(sizeof(LNode));
    j->data=5;
    //找到第3个位置的结点a
    LNode *a=getValueByOrder_H(l,3);
    //在a后面插入公共结点45
    insertNodeNext(a,h);
    insertNodeNext(h,j);
    LNode *b=getValueByOrder_H(l,5);
    b->next=a;
    //构建了一个12345---|
    //          ^-----|
    //12345->3的环
    //打印死循环
    //printList_H(l);
    cout<<"环的起点的值是"<<findCircle(l)->data<<endl;
结果
环的起点的值是3

25

image-20210412101328777

想不到啥

空间复杂度1–>不能用数组,或者新建一个链表

书上:先遍历找到中间元素(两个指针),后半部分逆置(a1 a2 a3 … an an-1 an-2…)

依次从头和中间选择,插入

找中间元素的思想,也很重要

image-20210413153926581

代码
void change(LinkList &l){
    LNode *mid=l,*end=l;

    //找中间位置 mid指向中间 end指向最后
    while(end->next!=NULL){
        mid=mid->next;
        end=end->next;
        if(end->next!=NULL){
            end=end->next;
        }
    }
    LNode *p=mid->next; // p为中间结点next为后半段起始
    //逆置后半段 头插法,先让p->next=NULL
    mid->next=NULL;
    LNode *r;
    while(p!=NULL){
        r=p->next; //不断链
        p->next=mid->next;
        mid->next=p;
        p=r;  
    }
    //依次插入 
    LNode *s=l->next; //第一个结点
    LNode *q=mid->next;//后半段开始结点
    mid->next=NULL;//作为尾结点

    while(q!=NULL){
        r=q->next; //不断链
        q->next=s->next;
        s->next=q;
        s=q->next;
        q=r;
    }
    
}
演示
    LinkList l;
    initList_H(l);
    insertListByOrder_H(l,1,1);
    insertListByOrder_H(l,2,2);
    insertListByOrder_H(l,3,3);
    insertListByOrder_H(l,4,4);
    insertListByOrder_H(l,5,5);
    printList_H(l);
    cout<<"变换"<<endl;
    change(l);
    printList_H(l);
}
结果
单链表的第1个值是:1
单链表的第2个值是:2
单链表的第3个值是:3
单链表的第4个值是:4
单链表的第5个值是:5
变换
单链表的第1个值是:1
单链表的第2个值是:5
单链表的第3个值是:2
单链表的第4个值是:4
单链表的第5个值是:3
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值