你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性: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
的节点。示例:
输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000
get
、addAtHead
、addAtTail
、addAtIndex
和 deleteAtIndex
的次数不超过 2000
。使用虚拟头结点,这道题目设计链表的五个接口:
获取链表第index个节点的数值:先判断index是否合法,index < 0 || index > (size - 1)
便不合法。定义一个指针,遍历:如果直接操作头结点,头结点值被改了,无法返回头结点。
ListNode* cur = dummyHead->next;
while(index) {
cur = cur->next;
index--;
}
return cur->val;
为何临时指针指针指向dummyHead->next
以及循环如何写,可带入一个节点进行验证。如果index=0,那么相当于获得原始头结点的值,while循环直接跳过之后,return确实合理,那么while循环以及临时指针的指向便没问题。
在链表的最前面插入一个节点:在虚拟头结点和头结点之间插入新节点就好了
newNode->next = dummyHead->next;
dummyHead->next = newNode;
注意以上顺序不可变
在链表的最后面插入一个节点:cur节点必须指向最后一个节点。怎么找尾结点?
while(cur->next != nullptr) {
cur = cur->next; //只要不为空,就一直执行这句话
}
在链表第index个节点前面插入一个节点
如果要在第index个节点前插入,必须保证第index个节点为cur->next
,也就是cur指向第index个节点的前一位。只有知道操作节点的前一个节点,才能进行后续操作。
ListNode* cur = dummyHead;
while(index) {
cur = cur->next;
index--;
}
newNode->next = cur->next;
cur->next = newNode;
size++;
检验对否,可随便代入一个节点便知道。如果index=0,那么while循环不操作等等进行分析。
删除链表的第index个节点
同理,删除第index个节点,必须知道前一个节点的指针。必须保证第index个节点为cur->next
ListNode* cur = dummyHead;
while(index) {
cur = cur->next;
index--;
}
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
size--;
delete命令指示释放了tmp指针原本所指的那部分内存,被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针,如果之后的程序不小心使用了tmp,会指向难以预想的内存空间。
C++:
class MyLinkedList {
public:
struct ListNode {
int val;
ListNode* next;
ListNode(int x): val(x), next(NULL) {}//构造函数
};
MyLinkedList() {
dummyHead = new ListNode(0); // 虚拟头节点
size = 0;// 初始化单链表长度
}
// 获取链表中第 index个节点的值:// 获取到第index个节点数值,如果index是非法数值直接返回-1,
// 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if(index < 0 || index > (size - 1)) { // 如果 index 不合理,返回 -1
return -1;
}
ListNode* cur = dummyHead->next; //创建一个指针 cur,指向虚拟头结点的下一个节点。
//循环遍历链表,移动 cur 指针到第 index 个节点处。
while(index) {
cur = cur->next;
index--;
}
return cur->val;
}
//在链表头部插入新节点
void addAtHead(int val) {
//创建一个新节点
ListNode* newNode = new ListNode(val);
//注意以下两句话的顺序
newNode->next = dummyHead->next;
dummyHead->next = newNode;
// 链表大小加1。
size++;
}
//在链表尾部添加新节点
void addAtTail(int val) {
//创建一个新节点
ListNode* newNode = new ListNode(val);
//创建一个指针 cur,指向虚拟头结点
ListNode* cur = dummyHead;
//循环遍历链表,移动 cur 指针到最后一个节点处
while(cur->next != nullptr) {
cur = cur->next;
}
//将最后一个节点的下一个节点指向新节点。
cur->next = newNode;
size++;
}
//在指定位置插入新节点
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if(index > size) return;//如果 index 大于链表的大小,则直接返回。
if(index < 0) index = 0;//如果 index 小于0,则将其设置为0。
ListNode* newNode = new ListNode(val);//创建一个新节点,并将其值设置为 val。
ListNode* cur = dummyHead;
while(index) {
cur = cur->next;
index--;
}
newNode->next = cur->next;
cur->next = newNode;
size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
//判断 index 是否合法,如果不合法,直接返回。
if(index >= size ||index < 0) {
return;
}
//创建一个指针 cur,指向虚拟头结点。
ListNode* cur = dummyHead;
while(index) {
cur = cur->next;
index--;
}
ListNode* tmp = cur->next; // 创建一个临时节点指向即将删除的节点
cur->next = cur->next->next;// 当前节点指针指向待删除节点的下一个节点
delete tmp;// 释放内存
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp = nullptr;
size--;
}
void printLinkedList() {
ListNode* cur = dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int size;
ListNode* dummyHead;
};
python:单链表
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.dummy_head = ListNode()
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size += 1
def addAtTail(self, val: int) -> None:
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
python:双链表
class ListNode:
def __init__(self, val=0, prev=None, next=None):
self.val = val
self.prev = prev
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
return current.val
def addAtHead(self, val: int) -> None:
new_node = ListNode(val, None, self.head)
if self.head:
self.head.prev = new_node
else:
self.tail = new_node
self.head = new_node
self.size += 1
def addAtTail(self, val: int) -> None:
new_node = ListNode(val, self.tail, None)
if self.tail:
self.tail.next = new_node
else:
self.head = new_node
self.tail = new_node
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
if index == 0:
self.addAtHead(val)
elif index == self.size:
self.addAtTail(val)
else:
if index < self.size // 2:
current = self.head
for i in range(index - 1):
current = current.next
else:
current = self.tail
for i in range(self.size - index):
current = current.prev
new_node = ListNode(val, current, current.next)
current.next.prev = new_node
current.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
if index == 0:
self.head = self.head.next
if self.head:
self.head.prev = None
else:
self.tail = None
elif index == self.size - 1:
self.tail = self.tail.prev
if self.tail:
self.tail.next = None
else:
self.head = None
else:
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1