********以下内容均是个人理解,个人语言,仅代表个人观点,希望能对你有所帮助***************
1.对链表的进一步深入理解分析
(1)逻辑结构:想象出来的,并不是真实存在的,例如里面的箭头,指针的指向,phead和pcur都指向链表里面的第一个节点;
(2)对于结点的理解:到底什么是节点,我的理解就是链表里面的一些相同的结构,很多个节点组成了链表;
节点里面包含哪些数据:一个是我们的data数据,还有一个就是next指针,这个指针指向链表里面的下一个节点位置的数据,这2个部分就是链表里面的结点的组成;
(3)物理结构(在内存里面实实在在进行存储的):这个才是内存里面的链表真实存在的形态,phead存储链表里面的第一个节点位置data的地址,第一个节点的next存放第二个节点的data的地址,以此类推,最后的一个节点next指针指向的是空的;
(4)pcur=pcur->next,减号加上箭头就是取出里面的数据(可以理解为解引用);全称就是结构体成员访问操作符(通过名字我们也可以理解是在结构体里面使用);我们首先让pcur和phead指向同一个地址处,也就是第一个节点的位置,cur->data打印的就是1,cur=cur->next,到底应该如何理解?
(我们用1,2,3这个链表为例子)在我看来,cur->next就是下一个结点的地址,也就是第二个节点的地址,赋值给cur,因为cur是一个指针变量,这个时候cur就会指向第二个节点的位置,以此类推,打印完3之后,cur->next指向为空,赋值给cur,cur也是空的,就会跳出循环。
2.链表里面到底为什么会使用到二级指针,你真的明白吗?
(1)我之前是通过调试发现我定义的形参phead,实参plist,在调试的过程中,发现形参phead确实可以改变,但是这个改变不会同步到plist实参,因此我们意识到可能是参数的类型定义有问题
(2)下面我们正面地分析一下,为什么使用二级指针?我列举下面的两个例子:一个要改变的是int类型的变量a,一个是要改变int*类型的变量a;
左边的a本来就不是指针,我们传递的a的地址,相当于就是一个变量的地址,我们使用int*类型的形参进行接收就可以了;
右边的a本来就是指针,我们传递的a的地址,相当于是一个一级指针的地址,我们需要使用int**类型的p进行接收,才可以修改a的数值;
应用到这个里面:我们的plist本来就是一个指针,只不过这个指针类型不是上面的int*,而是一个结构体类型(slnode*类型)的指针,我们想要改变他,就要传递他的地址,使用二级指针进行接收
(3)我们上面是进行的尾部插入数据,原来定义的新的节点是空的,我们要改变里面的值,所以二级指针进行接收,但是打印链表的时候,不会改变节点处的数值,而只是仅仅访问,所以使用一级指针进行接收就可以了。
3.顺序表并不是一无是处,链表也不是无所不能
顺序表:
(1)线性表,我们可以随机访问里面的数据(顺序遍历的数据是有下标的),排列是连续的;
(2)我们增容可能会造成空间的浪费,插入数据的时候,也会需要一个一个地挪动,挪动的时候效率很低下;
链表:
(1)非线性表,我们无法随机的访问里面的数据,因为每个节点之间不是连续排列的,我们只能通过next指针一个一个地向下寻找;
(2)随时进行增容,不会造成空间的浪费,而且插入数据的时候只需要改变链表的结点位置next指针的指向,不需要一个一个地进行挪动,大大提高了效率;
总结:顺序表和链表各有利弊,我们应当辩证地进行看待,对于随机的访问数据,这个在某些算法里面是有要求的,例如二分查找算法,以及优化算法里面的快排,可见链表虽然在某些方面优于顺序表,但是链表也有自己的局限性。