设计链表
今天发了几个小时就看了这么一道题目,深深感受到了链表中的临界思维的恶心之处,和一些我以前有一些误区的地方和之前没学习到的知识点,记录一下,狗屎
题目
你可以选择使用单链表或者双链表,设计并实现自己的链表。我使用的是单链表
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
结构体构成
这是一个层次化的写法,SingleNode代表了节点,MyLinkList代表了链表的整体
要注意在MyLinkList中里面的数据是链表的数据,跟节点没有半毛线关系,唯一有点关系的可能就是可以引出节点的dummyHead-虚拟头结点,
dummyHead就是这个链表的入口,Size就是节点的数量
这个Size节点就设置的很好,有了它就不需要每次都循环找出节点的数量。
同时需要明白dummyHead这个东西不算入结点之中,所以叫虚拟头结点,因此它特么没有index
而且头结点,也就是dummyHead.Next 它的索引是0
1 | // 正确的写法 |
1 | // 我一开始的写法 |
边界(狗屎)
首先
要清楚一个事情
如果要查询某个Index下的数据,则节点要遍历到Index节点的本身
可是如果要增加删除某个节点,则节点要遍历到index 节点的前一个结点
因此有了两种写法
1 | // 遍历到index节点的前一个结点 |
1 | // 遍历到Index节点 本身 |
其次
链表的遍历都是用指针的方式,一开始我搞不清楚啥时候新建一个结构体,啥时候新建一个指针,然后我就发现我特么一会指针一会结构体,类型都搞乱了,然后我抄了一遍正确答案,发现了正确定义方法
1 | // 正确 |
再其次
index的是否有效的问题
- index < 0 这个是毋庸置疑的,毕竟索引小于零玩个球球
1 | // index是不能取到 this.Size这个数字的 |
- 我发现正确答案是多不喜欢写 this!=nil 别问我为什么,我也不知道,反正我写了
再其次
重申一下名字
1 | MyLinkedList -- 链表 |
代码呈现
1 | type SingleNode struct { |
1 | // 创建一个链表 |
1 | // 获得第Index个节点的数据 |
1 | // 添加头结点 |
1 | // 添加节点到尾部 |
1 | //添加节点到 Index |
1 | // 删除Index中的数据 |
这道题还可以双向链表做,但是我以及筋疲力尽了,不想写了
双向链表的思路和单向链表的操作是一样的,也没看出多容易
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Echin の 博客!