• [c++指针教程]用简单链表练习指针


    P1160 队列安排 AKA 指针模板题

    链表模板题,本题目下(list)的教学已经很多了,我来讲点更加基础的( ightarrow)

    基础指针(结构体内)

    写在前面:本教程假设读者基本了解指针的定义以及使用,能会用指针写a+b就行

    写主席树,平衡树时我们会发现数组套数组的写法十分难受

    ...
    int sub=nd[nd[y].ls].sum-nd[nd[x].ls].sum;
    ...
    ch[old_root][opr]=ch[x][opr^1];
    f[ch[old_root][opr]]=old_root;
    ...
    

    我愿称其为"后 置 性 定 语 从 句"

    这种样子写起来很不舒服,实际上,指针才是最适合写这种数据结构的"法宝",

    本题是极其简单的链表的模板题,正好方便我们练习用指针处理数据结构.

    结构体指针的声明

    我们首先需要知道结构体本身的一些性质:

    在结构体(node)中:

    struct node{
    	int number;
    	node *pre,*next;
    	node(int Number=0):number(Number){pre=next=NULL;}
    	...
    };
    

    我们知道前两行是定义了结构体内的(int)变量和两个(node)类型的指针,所要指向的便是该元素的前一个元素(pre)和后一个元素(next)(这里就是一个简单的定义,不用管)

    第三行则是一个简单的"构造函数",格式如下(同样以结构体(node)为例,故函数名为(node)):

    struct node{
    	int a,b,c;
    	node(int A=一个数,int B=另一个数,int C=又一个数):a(A),b(B),c(C){...}
    };
    

    意思是把结构体内的变量(a,b,c)与函数传入的参数(A,B,C)一一对应赋值,再执行函数内的操作

    若是以结构体(node)定义了结构体变量,但是并没用函数对该变量进行赋值,只是单单定义了该变量,则该变量内对应变量就是我们自己定义的默认值,也就是自己在函数传参时初始化的那许多"一个数"

    同时我们需要知道,这个构造函数返回的是一个(node)类型的结构体变量,也就是说,这个函数写出来就可以当做变量使了,同时我们知道:

    int *a=new int;
    

    这个操作可以使得新定义的指针得到了一个新的(int)地址,进一步的,我们去了解这样一种操作:

    node *root=new node(a,b,c);
    

    这种操作的结果是让(root)指针指向一个内容为(a,b,c)的结构体,这个结构体正是刚刚我们用在(new)后面的那个函数造出来的,我们可以用这种操作完成新变量的指针连接

    对于已经存在的变量,它们的指针连接就更加简单,因为我们知道指针之间可以直接赋值,即:

    node *a,*b;
    ...//中间的一些步骤,使他们分别指向不同的目标
    a=b;//这样他们就指向了同一个目标
    

    这样一来我们就能够直接使指针指向我们所希望它指向的目标了.

    以上是结构体指针的声明与基本赋值(连接)

    结构体内的特殊指针操作

    一般结构体变量内元素的调用都需要形如"(nd.a)或者(b)或者(c)"的格式,加入现有一指针(rt)指向一个内容为(a,b,c)的结构体变量,那么我们在用指针调用时就需要这样写:

    rt->a;
    

    对于结构体内的函数(成员函数),需要这样写:

    rt->function();
    

    就是由点变成了箭头,不要认为两种形式都可以,必须对应起来,不然会CE

    另外,在结构体内写成员函数的时候,我们可以省去变量名等前缀,

    很人性化的是,在结构体中,(this)表示当前结构体的地址,这样就方便了我们在结构体内捯饬指针的时候无法处理"自己"的问题,

    当然,没有人想要在结构体里用(this)来访问自己的元素,因为能访问(this)的时候可以直接调用该结构体内元素值,(this)只是用来让别人指向自己的

    来看下结构体内成员函数有什么变化:

    //结构体node[a,b,*c]外,结构体变量名nd,指针c指向其他node结构体:
    inline void insert(){
    	nd.a=...;
        nd.b=...;
        nd.c->a=...;
        nd.c->b=...;
    }
    //结构体内:
    struct node{
    	int a,b;
      	node *c;
        inline void insert(){
    		a=...;
            b=...;
            c->a=...;
            c->b=...;
        }
    }nd(或者*nd);
    
    

    需要注意的是,要达到第一个函数的效果,第二个函数必须搭配着这样写:

    //nd是结构体变量的情况下:
    nd.insert();
    //nd是指针的的情况下:
    nd->insert();
    
    

    这样写意味着我们需要把已有的结构体变量(或指针)作为对象进行操作,也就是我们可以指定函数操作的对象,从而去指定我们用成员函数调用谁体内的元素.

    但是我们知道,指针有种说法叫做"空指针",即指向空地址,对空指针指向的地址进行任何形式的访问都会RE,因此我们在进行相关的指针连接的时候需要进行指针的判断

    也许这样看着写起来很麻烦,读起来很难受,但是实际上这是很符合中国人语言习惯的写法,这一点写的数据结构多了,就自然会明白,就自然会感叹指针的妙处.

    指针的链表应用

    链表,用结构体伪代码表示的话,大概需要以下变量:

    struct node{
    	int 链表元素所包含的数据,可以是多个;
    	int 该元素前一个元素的下标,该元素后一个元素的下标,没有就是0;
    	或者
    	node *前一个元素的地址,*后一个元素的地址
    	...构造函数,成员函数等 
    };
    
    

    下面我们就指针写法做详细讨论

    链表本身并不复杂,毕竟操作就只有元素之间的插入,删除,牵扯元素最多的怕就是链表的遍历,此时只要记录好链表的第一个元素的地址,跟着指针的指引,按照指针遍历就好了;

    • 对于插入操作

    对于"前插"与"后插"进行区分,下面给出前插的代码及详解,注意这里是结构体内成员函数:

    inline void insert0(ci u){ //基于某一元素的前插,现在我们的位置就在输入数据给出的元素那里
    	node *now=new node(u); //既然是插入,就要新建元素
    	if(pre)                //判断一下,如果当前元素是第一,那么访问pre空指针就会RE
            pre->next=now;     //原先前面元素的后一个元素改为新插入的元素
    	now->pre=pre;          //新插入的元素的前一个元素为原先前面的元素
    	now->next=this;        //新插入的元素后一个元素是当前元素
        pre=now;               //新的元素是我们的前一个元素
    }
    
    

    这是基本前插的思想,对于后插请独立思考得出结论怂什么,还有后面的总代码呢

    • 对于删除操作

    单靠上面的条件比较恶心,题目所给出的,是被删去同学的序号,此时我们新建一个指针数组(nd[]),指向对应编号的元素,(nd[i])指向的是(i)号同学,

    我们这样一种指针的添加可以理解为为链表提供了直接的指向,比如样例输出数据中的:

    2 4 1
    
    

    实际上就是

    元素[2,前一个空,下一个是元素4的地址]
    ->元素[4,前一个是元素2的地址,后一个是元素1的地址]
    ->元素[1,前一个是元素4的地址,后一个空]
    //另外的*nd数组,可以直接通过*nd数组找到对应元素的位置进而访问对应元素
    nd[1]=元素1的地址,nd[2]=元素2的地址, nd[3]=早删了,nd[4]=元素4的地址
    
    

    其中我们利用(nd[4]->pre)调用的指针,实际上就是(nd[2])里存放的指针

    由此,我们可以方便地访问到数据给出的元素,

    inline void remove(){
    	if(pre) pre->next=next; //如果有前一个元素的话就指向后面的元素
    	if(next) next->pre=pre; //类比上面一条
    	nd[number]=NULL;        //number是元素值,元素i里存的number是i
    }
    
    
    • 对于细节

    我们还需要一个指针(^*rt)来保存链表的第一个元素,这样才能输出整个链表

    我们在结构体之前这样写:

    struct node *nd[N],*rt;
    struct node{
    	int number;
    	node *pre,*next;
    	...
    };
    
    

    这样我们就可以在结构体中的成员函数中用这些指针了

    对于本题目,我们注意到它的操作是默认(1)已经在队列中了,所以我们需要在主函数中先建立元素(1),并且把第一个元素设定为(1)

    nd[1]=new node(1); rt=nd[1];
    
    

    另外的插入删除操作及一些改动会在完整代码中给出注释,

    完整代码:

    #include<iostream>
    #include<cstdio>
    #define ci const int &
    using namespace std;
    const int N=100005;
    int n,m;
    struct node *nd[N],*rt;
    struct node{
    	int number;
    	node *pre,*next;
    	node(ci Number=0):number(Number){pre=next=NULL;}
    	inline void insert0(ci u){
    		node *now=new node(u); nd[u]=now; //把对应的nd指针赋值
            if(rt==this) rt=now;              //前插时如果在第一个元素之前插入则新元素为第一
    		if(pre) pre->next=now;
    		now->pre=pre;
    		now->next=this; pre=now;
    	}
    	inline void insert1(ci u){
    		node *now=new node(u); nd[u]=now; //同理
    		if(next) next->pre=now;
    		now->next=next;
    		now->pre=this; next=now;
    	}
    	inline void remove(){
    		if(rt==this) rt=next;             //如果不巧删到了第一,就把第一后面的设成第一
    		if(pre) pre->next=next;
    		if(next) next->pre=pre;
    		nd[number]=NULL;
    	}
    };
    int main(){
    	scanf("%d",&n);
    	nd[1]=new node(1); rt=nd[1];
    	for(int i=2;i<=n;++i){
    		int k,p; scanf("%d%d",&k,&p);     //下面将按照符号进行相应插入操作
    		if(!p) nd[k]->insert0(i);
    		else nd[k]->insert1(i);
    	}scanf("%d",&m);
    	for(int i=1;i<=m;++i){
    		int x; scanf("%d",&x);
    		if(nd[x]) nd[x]->remove();        //把对应的元素删去
    	}
    	while(rt){                            //当前指针不为空则遍历没有结束
    		printf("%d ",rt->number);         //输出
    		rt=rt->next;                      //传递
    	}return 0;
    }
    
    

    希望我的讲解能够给读者带来些什么,总之感谢你的阅读

  • 相关阅读:
    loj1201(最大独立集)
    hdu4185+poj3020(最大匹配+最小边覆盖)
    【Leetcode】3Sum Closest
    【Leetcode】3Sum
    【Leetcode】Two Sum
    【Leetcode】Longest Consecutive Sequence
    【Leetcode】Median of Two Sorted Arrays
    【Leetcode】Search in Rotated Sorted Array II
    【Leetcode】Search in Rotated Sorted Array
    【Leetcode】Remove Duplicates from Sorted Array II
  • 原文地址:https://www.cnblogs.com/648-233/p/13236755.html
Copyright © 2020-2023  润新知