故事的起源是一道EOJ
1004. 队列的实现 - 数据结构与算法专题题库 - ECNU Online Judgeacm.ecnu.edu.cn当时数据结构学的不好,然后就直接过掉了(所以偷过的懒早晚都是要补回来的)
这题要求用链表实现队列,不带调STL库的啊不然算耍赖!(要是非要耍赖也能AC)
思路是简单的,按照题目要求全局设置两个链表头指针,因为是队列,所以先进先出,插入元素就在末端插入,删除元素就让头指针后移一位,但是由于荒废太久这么简单的东西也还是写了好久,更不用说无数的bug让程序无数次卡死,写这篇文章主要是提醒自己一个最为有趣的bug。
去年学链表的时候因为没有概念,基本上都是照着书敲得代码,这次选择先闭着眼睛靠记忆生敲,果然眼高手低各种问题,先来个结构体——
typedef struct node
{
int data;
struct node* link;
}NODE;
这道题因为要用到size,我们可以将头指针利用起来,将其存到头指针的data中,所以链表长这样:
![39f68a4c4c790af072359f15ed965d13.png](https://i-blog.csdnimg.cn/blog_migrate/05ff4514a629a57e77f0a0512dde0d65.png)
其他的函数定义基本没问题,但是我当时出了个问题,在链表赋值的时候我调用了前面的push方法,但是一开始我就把head值赋了过去,就导致我最终得到的head其实是2倍的head,因为push方法中每次入队会把head->data+1,算个小插曲,直接置零就好。
问题出在pop方法。
我的想法是让第一个有效元素的data直接改成head的data-1,然后让它直接做head:
![8c57ad431ea1267e4ecd39200dd31ca1.png](https://i-blog.csdnimg.cn/blog_migrate/1763857b44a2c998d23c4dea5ebb8120.png)
void pop(NODE *head)
{
if(head->data==0)
{
cout<<"ERRORn";
return;
}
else
{
(head->link->data)++;
head=head->link;
}
}
结果就是程序卡死,我一直没想明白为什么,所以我想了想换了个办法就是让头指针data自减然后直接跳过第一个元素,指针指向第二个:
![42bd65a3d2739853e150f2b211986b28.png](https://i-blog.csdnimg.cn/blog_migrate/43b033588caedf64585eb67130eebb15.png)
void pop(NODE *head)
{
if(head->data==0)
{
cout<<"ERRORn";
return;
}
else
{
head->link=(head->link)->link;
(head->data)--;
/*
(head->link->data)++;
head=head->link;
*/
}
}
这次居然跑通了,想不明白,然后就去找学长了,此处省略......那么先简单回顾C++传参那些事儿:
函数传参三种方式:分别是值传递、指针传递和引用传递。
一、传值
在函数定义括号中的参数是形参,是给函数内专用的局部变量,等同于函数接收到的是实参的副本,形参的值在函数内部如果被改变,对实参没有影响
二、传指针
形参为指向实参地址的指针,当对形参操作时,等同于直接通过地址操作实参
三、传引用
下面这段引自大佬博客,说的很清楚,链接走这里:
浅谈C++三种传参方式 - scyq - 博客园www.cnblogs.com1. 指针从本质上是一个变量,是一个整形变量,存放的是另一个变量的地址。指针在逻辑上是独立的,它可以被改变,甚至能改变它的值(指向其他地址),并且可以取出对应内存中的数据。
2. 引用可以理解为外号,是另一个变量的同义词,它在逻辑上具有 依附性,所以C++也规定引用的在创立的时候就必须被初始化(现有一个变量,然后创建对该变量的引用)。 而且其引用的对象在其整个生命周期中不能被改变,即自始至终只能依附于同一个变量(初始化的时候代表的是谁的别名,就一直是谁的别名,不能变)。
3. 在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
![64aef8ed4b27ac256890b339f20ae440.png](https://i-blog.csdnimg.cn/blog_migrate/95496190a4060cd314dd1443181d6681.png)
![7518092d5047edf06a3f241f322fe148.png](https://i-blog.csdnimg.cn/blog_migrate/7e09a701516498a1b3ee85d21cb5650e.jpeg)
也就是说函数调用前编译器自动创建新的变量,将其赋值成传入的参数,我之前那种传指针的写法,在pop函数里面它实际上是创建了一个新的指针,也指向传进来的head 的队首元素,而我后来对它进行修改改变的只是函数内部的head指向,对全局head没有影响;之所以后来的方法管用是因为全局head和新建head中存着的link,也即队首地址是一样的,也就是它们指向的这个地址是一样的,所以对这个地址操作,让它指向别处,不论是main中的head还是函数里的head都是完全没有问题的。
新旧变量指向的link是相同的,而本身是不同的;所以我让link的这块地址指向别处,对哪个head操作都OK,但是新旧head本身的地址是不一样的,改它莫得用。
最后AC代码,请路过大佬不吝赐教:
#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
int data;
struct node* link;
}NODE;
int Q,x;
string obj,op;
NODE *a,*b;
void pop(NODE *head)
{
if(head->data==0)
{
cout<<"ERRORn";
return;
}
else
{
head->link=(head->link)->link;
(head->data)--;
}
}
void push(NODE *head,int x)
{
NODE *q=(NODE *)malloc(sizeof(NODE));
q->data=x;
q->link=NULL;
NODE *p=head;
while(p->link!=NULL)p=p->link;
p->link=q;
(head->data)++;
}
int _front(NODE *head)
{
return head->link->data;
}
int _size(NODE *head)
{
return head->data;
}
void _clear(NODE *head)
{
head->data=0;
head->link=NULL;
}
bool _empty(NODE *head)
{
return (head->data==0);
}
void _operator(NODE *head1,NODE *head2)// 2赋值给1
{
head1->data=0;
head1->link=NULL;
if(head2->data==0)return;
NODE *p=head2->link;
while(p!=NULL)
{
push(head1,p->data);
p=p->link;
}
}
void _swap(NODE *head1,NODE *head2)
{
NODE *tmp=(NODE *)malloc(sizeof(NODE));
_operator(tmp,b);
_operator(b,a);
_operator(a,tmp);
}
int main()
{
a=(NODE *)malloc(sizeof(NODE));
b=(NODE *)malloc(sizeof(NODE));
a->data=0,a->link=NULL;
b->data=0,b->link=NULL;
cin>>Q;
while(Q--)
{
cin>>op;
if(op=="pop")
{
cin>>obj;
if(obj=="a")pop(a);
else pop(b);
}
else if(op=="push")
{
cin>>obj>>x;
if(obj=="a")push(a,x);
else push(b,x);
}
else if(op=="front")
{
cin>>obj;
if(obj=="a"&&!_empty(a))cout<<_front(a)<<endl;
else if(obj=="b"&&!_empty(b))cout<<_front(b)<<endl;
else cout<<"ERRORn";
}
else if(op=="size")
{
cin>>obj;
if(obj=="a")cout<<_size(a)<<endl;
else cout<<_size(b)<<endl;
}
else if(op=="clear")
{
cin>>obj;
if(obj=="a")_clear(a);
else _clear(b);
}
else if(op=="empty")
{
cin>>obj;
if(obj=="a")cout<<(_empty(a)?"YES":"NO")<<endl;
else cout<<(_empty(b)?"YES":"NO")<<endl;
}
else if(op=="operator=")
{
cin>>obj;
if(obj=="a")_operator(b,a);
else _operator(a,b);
}
else if(op=="swap")_swap(a,b);
}
return 0;
}
ps: 所以当我以为我需要复习数据结构,其实我应该复习的是C++,也就是当我以为我没文化的程度只是欣赏不来文章,而实际上我根本就不识字...
当然学长 @哈哈哈哈哈哈 比我写的好多了,走这里 :)
https://zhuanlan.zhihu.com/p/210271150zhuanlan.zhihu.com