链表中的指针

中期答辩改在了国庆之后,终于有时间可以看看剑指offer了。在看到单向链表的部分,对指针,尤其是头指针有点疑惑。首先容易理解的是链表的节点是一个结构体,该结构体包含一个数据(一般是int型),还包含一个指向该结构体类型的指针。通过指针的指向一个个遍历,也是通过指针一次次分配内存。这使得链表不同于数组,链表中的内存不是连续的,我们想要访问一个结点只能从头结点开始。其实数组之所以能通过数组下标进行访问,也是因为事先设定了一个基准点。只不过在链表中这个基准点(头指针)很重要。现在就来好好研究一下头指针、头结点的那些事。

 

在书中提供的代码中,无论是在链表末尾插入新的结点,还是在链表中删除具有某值的结点,都有两个共同点。一是函数的第一个参数类型是指向结点数据类型的指针的指针,一是将代码分为两部分,第一部分都是通过头指针是否是空指针判断该链表是否是空链表,或者判断头结点中的数据是否和参数相等。这里提到了两个重要的概念,头指针和头结点。

struct ListNode
{
	int value;
	ListNode * Next;

};

void addTotail(ListNode ** head, int value){//第一个参数是头指针的指针,因为头指针在变
	ListNode* newlistnode = new ListNode();
	newlistnode->value = value;
	newlistnode->Next = NULL;//新建一个节点,数据等于参数,指针为空
	if (*head == NULL){//头指针,指向下一个节点
		*head = newlistnode;//空链表中插入节点,则头指针不再是空指针,第一个形参应改变
	}
	else {
		//链表不空时,递归直到链表尾(空指针)
		ListNode* pNode = *head;
		while (pNode->Next != NULL){
			pNode = pNode->Next;
		}
		pNode->Next = newlistnode;
	}
}

可以看到空指针相对于数组名,是链表的名字,它是指向下一个结点的指针的指针,至于为什么是双重指针,这涉及到函数中参数值的传递。因为在链表中插入我们要讨论该链表是否是空链表,进行不同的操作,而插入这个操作会使得空链表不再空,即使得原来空链表的头指针指向空指针变成了指向一个真实的结构体,我们需要在执行完一次函数后修改头指针这个形参,方便之后再次调用这个函数。

函数中值的传递方式有三种:值传递,地址传递,引用传递。

值传递,只传递了形参的值,因为在函数内部会新建值等于参数的副本(形参),函数中的操作只是对副本进行修改,不会改变实参的原始大小。实参是主函数调用函数时传递的值,形参是函数定义时使用的值。一个很形象的例子就是餐厅中有保鲜膜包好的样品菜,你指给厨师说要菜A,厨师会拿新的食材重新做一份,而不是直接把样品给你。如果真的想要样品中的菜,那么就要借助装样品的盘子了,盘子就相当于地址,这就是地址传递。Java中只有按值传递,这也是java简单的原因。

引用传递。首先搞清楚引用是怎么回事,引用是两个变量名表示同一个东西,不会分配新的内存,可以认为引用是目标变量的一个别名。通过引用修改变量势必引起原始变量发生变化。

下面说一下今天遇到的主角,指针传递。这里更特殊的是使用了双重指针。这里有一句比较容易引起混淆的话:其实一切传递的方式本质都是按值传递。简单来说,双重指针通过传递地址变量和地址指针指向的内容实现了可以通过函数对参数的地址和内容值进行修改。

先看下面一个代码,代码中第一个参数是指向int型的指针,传入a的地址。可以理解为&a是按值传递的,这样在执行函数时会有一个a的地址的拷贝,这个拷贝同样指向a,这样当我们在函数中修改拷贝中的内容,即便这个拷贝最后被释放,还是会改变a的值。

void change(int *_a,int &b){
*_a=b;
}
void main(){
int a=10,b=20;
int *p_a=&a;
change(p_a,b);
printf("%d",*p_a);
}

既然单指针作为形参已经可以实现通过函数修改实参,为什么还要使用双重函数呢?可以注意到我们刚才把a的地址当做按值传递,而函数目标是修改按值传递的地址指向的内容,但是当我们函数的目的是修改地址本身时,又回到了按值传递的老路上,这样是不能实现目的的。借鉴刚才的通过指针改变指针指向的内容,我们再加一层指针,指向变量的地址,即指针的指针,这就有了双重指针。

再说回头指针和头结点。头指针是必要的,头结点更多是为了操作统计和方便,其数据域一般无意义,可以用作监视哨或者存放链表的长度。代码中,只要头指针运用了next操作就自动创建了头结点。再从表示的意义理解一下双重指针ListNode ** pHead,它是指向结构体ListNode的指针的指针,那么*pHead表示的就是指向ListNode的一个指针。

Reference:

  1. 盘子https://blog.csdn.net/ThereIsNoCode/article/details/77996799
  2. 双重指针https://blog.csdn.net/qq_33631303/article/details/78007413
  3. https://blog.csdn.net/xmrforever/article/details/3915665
  4. https://blog.csdn.net/playboy_lei/article/details/51993513
### 回答1: 静态链表指针表示下一个节点在数组的下标位置。由于静态链表是用数组来实现的,因此每个节点都是一个数组元素,指针就是这个元素的下标。这样,通过指针可以直接访问数组的下一个元素,从而实现链表的遍历和操作。 ### 回答2: 静态链表使用数组来表示链表的结构,其每个节点使用一个元素表示。而这个元素指针则表示了该节点在数组的位置信息。通常情况下,指针可以分为两个方向: 1. 下一个节点的指针:通过这个指针,可以找到下一个节点在数组的位置,实现链表的遍历。 2. 前驱节点的指针:通过这个指针,可以找到当前节点的前驱节点在数组的位置,实现链表的逆向遍历。 通过指针,我们可以在静态链表方便地找到相邻节点之间的关系,从而实现链表的各种操作。对于某个节点来说,其在数组的位置就是它的存储地址,通过指针可以直接访问节点的数据和指针信息,而不需要进行线性遍历。 需要注意的是,在静态链表,由于使用数组表示,节点的位置是固定的,不能动态地插入和删除节点。然而,静态链表相对于动态链表来说,具有存储分配简单、空间占用低等优点,适用于对节点插入和删除较少的场景。 ### 回答3: 在静态链表指针表示的是元素在内存的位置或者索引。静态链表实际上是通过数组来实现的,每个元素包含两个部分:数据域和指针域。 指针域用来指向下一个元素的位置或索引。具体来说,它存储了下一个元素在数组的位置或索引值。通过指针域,我们可以在静态链表快速定位和访问下一个元素。 对于静态链表的最后一个元素,其指针域通常指向一个特殊的标记,如-1或者NULL,用来表示链表的末尾。这个标记可以帮助我们确定什么时候到达了链表的尾部。 指针的使用使得静态链表具有了动态链表的特点,可以方便地插入、删除元素,并且在内存的存储也是连续的。每个元素都可以通过指针域链接起来,形成一个完整的链表结构。 总之,指针在静态链表起到了连接各个元素的作用,帮助我们进行元素的访问和操作,使得静态链表的功能更加强大和灵活。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值