realloc有两种方式,第一种是原地扩容,第二种方式是异地扩容。
要括的时候看后面的空间够不够,够就原地括,这样返回的指针还是原来的指针
如果后面没有足够的空间,会去其他地方找更大的内存,然后再拷贝数据下来,然后释放旧空间,返回新空间地址
注意:原地括代价低(不需要拷贝数组什么的),异地括代价高
顺序表好处:连续的空间只需要知道第一个位置的地址,有顺序,就可以访问所有的数据
链表(物理上不是连续的):有一个指针找到第一个(这种存一个数据的叫节点),第一个节点和第二个节点通过一个指针链接起来,第一个存第二个的地址,到最后存NULL
代价:不连续的话,每存一个数据就要伴随一个指针去把后面存数据的内存块(节点)链接起来。不支持随机访问。
箭头是想象出来的,用形象的方式来表示
如果phead里面存的是空,空指针不能解引用,解引用程序就会出问题
反正都要尾插,我们先开空间,把节点搞出来,然后看phead为不为空,如果为空说明链表里一个节点都没有,那就不找尾,直接让phead指向新节点地址
节点给了phead,plist没有改变
虽然这是传指针,phead是一块空间,把plist的值拷贝给phead,phead的改变就不会影响plist
要改变int就要传int*,要改变int*也要传递int*的地址int**
传一级指针地址用二级指针接收,链表就可以插入数据了。尾插要改变实参所以传地址
打印不传是因为不需要改变实参就不传地址了
总结:想通过指针去改变指向空间的内容,传一级指针,想改变指针的指向,传二级指针
也可以不传二级指针传一级指针,但要使用返回值接收,这样我们就不需要改变plist中存的地址,而是拷贝plist中的地址存放到新开辟的指针中,最后返回开辟的指针
头插
栈比堆小,栈一般只有8M,大概八百多万字节,32位下堆一般有2G
1M=1024KB=1024*1024Byte
头删、尾删都要传二级指针。尾删平时不需要用二级指针,但删到最后还是需要的。把节点删完了,要把plist中存的地址置成空指针
这里的尾删是存在问题的。tail只是这个函数里的局部变量
tail原来是指向最后的,tail=NULL之后:
出了作用域tail也就销毁了。d2后面有一个指针,指向下一个空间的地址。这个指针就是经典的野指针,指针指向非法的空间(比如:被释放了的空间)。所以不仅要释放最后一个节点,还要把最后一个节点的前一个节点中存的地址置空
单链表缺陷:有一个节点可以找下一个节点但找不到它的上一个节点
那就再定义一个指针变量,每次tail往前走之前先赋值给prev。当tail走到尾,prev就是tail的前一个,把prev赋成空。
但删最后一个的时候有问题
以下写法在多个节点的时候是可行的,在删最后一个的时候也存在问题
对空指针解引用,不仅有这个问题plist里面也没置空
所以我们应该分类讨论:
一个节点进if,两个或两个以上节点进else
进行了分类讨论之后,对省一个指针变量的另一种方式也是可行的
想重复查找的话:找到第一个2之后可以从第一个2之后的后一个位置开始找,就不用从头开始了
不仅有找同时也有修改的作用,因为它返回的是下标
如果找第一个节点(pos位置)前一个想插入会发现在pos前找不到
那么判断pos是不是*pphead,是不是第一个节点,是就特殊处理
单链表在pos前插入有点复杂,可以在pos后插,这样就不需要pphead参数,这样实现简单而且效率更高一些。
free之后pos置不置空都可以,pos只是一个形参,pos置空但要删的那个节点不会被置空,因为pos只是一个拷贝,形参的改变不影响实参,所以可以不置空,从好的习惯来说置空了也挺好。
函数进去之后可以看情况加断言。也是一个好习惯,比如打印函数,打印空链表是合理的我们就不加断言。逻辑走代码的时候认为它不可能为空就加断言。
链表不连续,所以不能只free一次,这样会内存泄漏。