707. 设计链表
这题卡哥的讲解视频特别好,涵盖了很多细节。
自己跟着卡哥代码敲了一遍。
单链表:
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode *next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index) {
if (index > (_size - 1)|| index < 0)
{
return -1;
}
LinkedNode *cur = _dummyHead->next;
while (index--)
{
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode *newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
++_size;
}
void addAtTail(int val) {
LinkedNode *newNode = new LinkedNode(val);
LinkedNode *cur = _dummyHead;
while (cur->next != nullptr)
{
cur = cur->next;
}
cur->next = newNode;
++_size;
}
void addAtIndex(int index, int val) {
if (index > _size) return;
if (index < 0) index = 0;
LinkedNode *newNode = new LinkedNode(val);
LinkedNode *cur = _dummyHead;
while (index--)
{
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
++_size;
}
void deleteAtIndex(int index) {
if (index < 0 || index >= _size)
{
return;
}
LinkedNode *cur = _dummyHead;
while(index--)
{
cur = cur->next;
}
LinkedNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp=nullptr;
--_size;
}
void printLinkedList()
{
LinkedNode *cur = _dummyHead;
while (cur->next != nullptr)
{
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode *_dummyHead;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
附上自己看卡哥视频有所感的点:






卡哥也给出了双向链表的代码。
问了下ChatGPT哨兵节点:
在这段代码中,哨兵节点(sentinelNode)的 prev 和 next 指针指向的是它自身,形成一个循环结构。具体来说:
-
sentinelNode->next:哨兵节点的 next 指针指向链表的第一个有效节点(即头节点)。但是,最开始这个节点是指向哨兵节点本身,意味着链表为空时,哨兵节点的 next 指向它自己。 -
sentinelNode->prev:哨兵节点的 prev 指针指向链表的最后一个有效节点(即尾节点)。同样,当链表为空时,哨兵节点的 prev 也指向它自己。
这样做的目的是为了简化链表的边界条件,避免在处理链表的头部和尾部时需要额外的判断。
举个例子:
-
空链表时:
-
sentinelNode->next指向哨兵节点自身。 -
sentinelNode->prev指向哨兵节点自身。
这时,链表没有任何有效节点,头部和尾部都由哨兵节点控制。
-
-
向链表头部添加一个新节点:
假设我们添加一个值为 10 的节点:
-
创建一个新的节点 newNode,它的 next 指向哨兵节点的原 next,即空链表时指向哨兵节点本身。
-
新节点的 prev 指向哨兵节点。
-
然后更新哨兵节点的 next 指向新节点,sentinelNode->next = newNode。
此时,链表变成:[sentinelNode] <-> [newNode] <-> [sentinelNode]。
-
-
向链表尾部添加一个新节点:
如果我们在尾部插入一个值为 20 的节点:
-
创建一个新的节点 newNode,它的 prev 指向哨兵节点的原 prev,即空链表时指向哨兵节点本身。
-
新节点的 next 指向哨兵节点。
-
然后更新哨兵节点的 prev 指向新节点,sentinelNode->prev = newNode。
此时,链表变成:[sentinelNode] <-> [newNode] <-> [sentinelNode]。
-
-
总结:
在空链表中,哨兵节点的 prev 和 next 都指向它自己,形成一个闭环。当链表非空时,哨兵节点的 prev 和 next 分别指向链表的尾节点和头节点,但仍然保持循环结构。
附上一张自己画的图:


浙公网安备 33010602011771号