寒假作业Day 11

寒假作业Day 11

一、选择题

在这里插入图片描述

栈满的判断:在链式存储结构中,栈的大小是动态的,只受限于系统分配给程序的内存大小。因此,理论上,链式栈不会因为空间不足而“满”。所以,不需要判断栈满。
栈空的判断:链式栈通常有一个指针指向栈顶元素。如果这个指针是NULL或者指向某个表示空栈的特殊节点,那么我们可以判断栈是空的。因此,需要判断栈空。

在这里插入图片描述

这里画图操作,p->q->…这样的一个样式,让p的后继指针指向原本q指向的地方,再把q free掉其实就可以了

在这里插入图片描述

A:随机存取是顺序表的优点,B很明显也是错误的,结构体里面不仅有元素值,还有指针,不可能花费的存储空间更少;C是对的,顺序表插入删除都需要移动元素,而链表只需要控制指针即可;D:链表的物理顺序是随机摆放的,并不与其逻辑顺序相同

在这里插入图片描述

队列是一种既可以插入,又可以删除,先进先出的线性表

在这里插入图片描述

A. 通常不会出现栈满的情况
解释:
顺序栈是基于数组实现的,它的大小在创建时就已经确定,当栈中元素数量达到数组的最大容量时,就会出现栈满的情况。而链栈是基于链表实现的,链表的大小是动态的,可以随着元素的插入而增长,因此链栈通常不会出现栈满的情况。

其他选项的解释:
B. 通常不会出现栈空的情况 - 无论是顺序栈还是链栈,当栈中没有元素时,都会处于栈空的状态。因此,这个选项不是链栈相对于顺序栈的优势。

C. 插入操作更容易实现 - 无论是顺序栈还是链栈,插入操作(即入栈操作)的复杂度都是O(1),因为都是将新元素放在栈顶。因此,这个选项不是链栈相对于顺序栈的优势。

D. 删除操作更容易实现 - 无论是顺序栈还是链栈,删除操作(即出栈操作)的复杂度也都是O(1),因为都是从栈顶删除元素。因此,这个选项也不是链栈相对于顺序栈的优势。

二、编程题

在这里插入图片描述

// 全局变量,用于在reverse函数递归调用时存储下一个要处理的节点  
struct ListNode * next = NULL;  
 
struct ListNode * reverse(struct ListNode * head, int n)  
{  
    // 如果n为1,说明只需要反转一个节点,此时直接返回该节点  
    if(n == 1)  
    {  
        // 保存当前节点的下一个节点  
        next = head->next;  
        return head;  
    }  
      
    // 递归调用reverse函数,反转head的下一个节点开始的n-1个节点  
    struct ListNode * newHead = NULL;  
    newHead = reverse(head->next, n-1);  
      
    // 将当前节点的下一个节点的next指针指向当前节点,实现反转  
    head->next->next = head;  
      
    // 将当前节点的next指针指向tmp,即原来head的下一个节点的下一个节点  
    head->next = next;  
      
    // 返回新的头节点  
    return newHead;  
}  
  
struct ListNode* reverseBetween(struct ListNode* head, int m, int n ) {  
    // 如果m为1,说明从链表的头节点开始反转,直接调用reverse函数  
    if(m == 1)  
    {  
        return reverse(head, n);  
    }  
      
    // 递归调用reverseBetween函数,处理head的下一个节点开始的子链表  
    struct ListNode * reversePart = NULL;  
    reversePart = reverseBetween(head->next, m-1, n-1);  
      
    // 将当前节点的next指针指向反转后的子链表头节点  
    head->next = reversePart;  
      
    // 返回头节点,此时头节点没有变化  
    return head;  
}

这里我们使用的是递归,所以会比较复杂,我们拿例子来演示一下!

*1->2->3->4->5->NULL
*变成1->4->3->2->5->NULL

*首先我们看reverseBetween函数,如果m==1,我们就直接调用reverse辅助函数,反转链表;但一开始我们的m=2,所以我们会从下面开始:
reversePart=reverseBetween函数递归后返回的结果,并让head->next指向reversePart
*我们递归一次,m=1,n=3,符合条件,走入reverse辅助函数
又因为n=3,所以在其中递归两次,让n=1时才一步一步返回
*此时n=1,head指向的值是4,于是next存储的就是5的位置,返回head,也就是4的位置,并且由于next是全局变量,所以值并没有随着递归的返回而销毁,而是保留(后面的next指针一直没有变过,一直都在5的位置上)
*reverse函数返回到n=2的时候,此时newHead指向4(由于我们会发现在递归中,newHead的值并未发生改变,所以其也一直指向4);之前n=1的时候head指向4,所以此时n=2的head指向的是33的next是4,所以让4的next指向3,并且让3的next指向5(next)
*此刻来到n=3的时候,newHead依旧为4,next依旧为5;此时head指向22的next为3,让3的next指向22的next指向5(next)
*此时reverse辅助函数的递归结束,我们开始看reverseBetween函数,返回了reverse(head,n)(m=1时),此时head指向2,n=3,也就是reverse函数返回给我们的newHead(4);我们再次回到m=2,n=4的时候,这个时候head指向1,于是reversePart指向4,而head->next=4(1->4);而在刚才的reverse辅助函数中,我们让4指向33指向22指向5,即如图的形状,也就是成功完成了转变!

在这里插入图片描述

其实分析原理,我们会发现,首先我们是找到反转的tail,也就是m点,然后就开始进行reverse;在reverse中我们首先找到n点,然后用next记录下n点的next,并用newHead记录下n点的地址;reverse递归完之后,用m点的前一个节点连接n点;而在reverse中,我们将指针逐个反转,把几个特殊节点保留,是十分巧妙的思想

在这里插入图片描述

struct ListNode* removeZeroSumSublists(struct ListNode* head){
    struct ListNode*dummy=(struct ListNode*)malloc(sizeof(struct ListNode));//头节点可能会被消除,要建立一个虚拟头节点
    dummy->next=head;
    struct ListNode*p=dummy;
    struct ListNode*q=p->next;
    int x=0;
    while(p)//其实本质上是用了快慢指针的思想
    {
        x=0;//这里还是要更新的
        q=p->next;
        while(q)
        {
            x-=q->val;//用减号是一个真正的亮点(省去很多麻烦)
            if(x==0)
            {
                p->next=q->next;//删除节点
            }
            q=q->next;
        }
        p=p->next;
    }
    return dummy->next;
}

这里我们还是先举个例子,比如1,2,3,-3,4这个链表

*1->2->3->-3->4
*变成1->2->-4
*首先,创建一个哨兵位,让其next指向head,并让p指向哨兵位,q指向head,并创建一个x来存储val值
*首先套一个p指针的循环,x=0(更新值),q指向p->next
*之后走q的while循环,x减去此时q->val,然后如果x==0,也就是找到了总和值为0的点,就让p->next指向q->next,删除中间节点,并且每次都让q=q->next,这是遍历的必然要求
*在这里,我们会发现,q在指向1的时候,是明显无法让x==0的,所以此时p=p->next,也就是q指向2,这里也不行;只有当q指向3的时候,才可以,而此时p指向2,于是2->next变成了4,即现在的链表为1->2->4,之后p走到3,q走到4,由于后面没有总和值为0的节点,所以都是遍历完就结束了,最后返回哨兵位的next,也就是头节点

其实这个题的原理比上题简单很多,这里我们就是创建了两个快慢指针,并且让其分别遍历(q嵌套在p的循环中,q通过p改变位置从而改变自己的起始位置),用一个x记录其总和值,实在巧妙

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值