链表和数组:
链表是一个相对简单的数据结构,与数组不同的是,它并不需要一块连续的内存空间,而是通过指针将一组零散的内存块串联起来使用。
单链表:
我们吧内存块称为链表的结点,为了将所有的结点串起来,每个链表的结点除了存储数据意外,还需要记录链上的下一个结点的地址。这个记录下个结点的指针叫做后继指针next。
我们习惯吧的第一个结点称为头结点,把最后一个结点叫做尾结点。其中,头系欸但用来记录链表的基地址,而尾结点指向一个空地址Null,表示这是链表上的最后一个结点。
单链表的查找、插入,删除操作:
在进行数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度为o(n),而链表插入或者删除数据,由于本身链表的存储空间本身不是连续的,只需要考虑相邻结点的指针改变,其对应的时间复杂度为o(1)。
相对的,链表不能通过下标的随机访问高效访问第K个元素,只能根据指指针一个结点一个结点地依次遍历。其时间复杂度为o(n)。
循环链表和双向链表:
循环链表是一种特殊地单链表,其唯一区别在于循环链表指针指向头结点,适合处理具有环形结的数据,比如约瑟夫问题。
单向链表只有一个方向,结点只有一个后继指针next指向后面的结点,而双向链表多了一个前驱指针prev指向前面的结点。由于双向链表需要额外的空间来存储前驱结点的地址,所以双向链表要比单链表占用更多的内存空间,而且支持在o(1)时间复杂度下找到前驱结点。
关于双向链表的操作:
双向链表和单向链表都支持o(1)的时间复杂度下进行删除操作,但是双向链表会更快一些。这是由于在实际的软件开发中,从链表中删除一个数据一般是这俩种情况:
1.删除结点中“值为某个定值”的结点
2.删除给定指针指向的结点
对于第一种情况,不管是单链表还是双向链表,都要从头开始进行依次遍历对比,直到找到值等于给定值的结点,再通过指针操作进行删除。
尽管单纯的删除时间复杂度是o(1),但是遍历的适合时间复杂度却为o(n)。
对于第二种情况,我们已经找到了需要删除的结点,但是单链表还需要一个指针遍历一次,也可以通过前后指针进行遍历,不管怎么样,一个指针并不能完成删除操作,而双向链表只要一个指针就能完成操作。
此外,对于有序链表,双向链表查找效率也要比单链表效率高一些。
因此,虽然双向链表比较费内存,但是还是比单链表应用更加广泛。如Java中的LinkedHashMap容器。
留意边界条件处理:
链表为空时
链表只包含一个结点时
链表只包含俩个结点时
代码逻辑再处理头节点和尾结点的时候
如何理解指针:
将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中长长了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。