建议:了解一下链接基础,以及链表和数组的区别?
文章链接:代码随想录
建议:?本题最关键是要理解?虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接:203. 移除链表元素 - 力扣(LeetCode)
文章讲解/视频讲解::代码随想录
建议:?这是一道考察?链表综合操作的题目,不算容易,可以练一练?使用虚拟头结点
文章讲解/视频讲解:代码随想录
文章讲解/视频讲解:代码随想录
c++代码示例如下O(n)
ListNode* removeElements(ListNode* head, int val) {
while (head != NULL && head->val == val) {
ListNode* tmp = head;
head = head->next;
delete tmp;
}
ListNode* cur = head;
while (cur != NULL && cur->next != NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else {
cur = cur->next;
}
}
return head;
}
设置一个虚拟头结点再进行移除节点操作
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
这个例子中ListNode(0)源于链表结构体定义中的构造函数。
ListNode(int x) : val(x),next(NULL) {}
让我们逐一解析这段代码:
ListNode (int x)
: 这是ListNode
类的构造函数的声明。它接受一个整数参数x
。:val(x), next(NULL)
: 这是初始化列表。它用于初始化类的成员变量。在这里,val
成员变量被初始化为x
,而next
成员变量被初始化为NULL
。{ }
: 这是构造函数的主体。在这里它是空的,因为所有的初始化都在初始化列表中完成了。总的来说,当你创建一个新的ListNode
对象并传递一个整数给它时,这个构造函数将确保该节点的值被设置为该整数,并且该节点没有指向任何下一个节点(因为next
被初始化为NULL
)。
注意:如果不定义构造函数使用默认构造函数的话,在初始化的时候不能直接给变量赋值!
c++代码示例如下
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val) :val(val), next(NULL) {}
};
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 != NULL) {
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 >= size || index < 0) {
return;
}
LinkedNode* cur = dummyHead;
while (index--) {
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = NULL;
size--;
}
private:
int size;
LinkedNode* dummyHead;
};
c语言代码示例如下
typedef struct MyLinkedList {
int val;
struct MyLinkedList* next;
}MyLinkedList;
MyLinkedList* myLinkedListCreate() {
MyLinkedList* head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
head->next = NULL;
return head;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList* cur = obj->next;
for (int i = 0; cur != NULL; i++) {
if (i == index) {
return cur->val;
}
else {
cur = cur->next;
}
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList* nhead = (MyLinkedList*)malloc(sizeof(MyLinkedList));
nhead->val = val;
nhead->next = obj->next;
obj->next = nhead;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList* cur = obj;
while (cur->next != NULL) {
cur = cur->next;
}
MyLinkedList* ntail = (MyLinkedList*)malloc(sizeof(MyLinkedList));
ntail->val = val;
ntail->next = NULL;
cur->next = ntail;
}
void myLinkedListAddAtIndex(MyLinkedList* obj,int index,int val) {
if (index == 0) {
myLinkedListAddAtHead(obj, val);
return;
}
MyLinkedList* cur = obj->next;
for (int i = 1; cur != NULL; i++){
if (i == index) {
MyLinkedList* newNode = (MyLinkedList*)malloc(sizeof(MyLinkedList));
newNode->val = val;
newNode->next = cur->next;
cur->next = newNode;
return;
}
else {
cur = cur->next;
}
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if (index == 0) {
MyLinkedList* tmp = obj->next;
if (tmp != NULL) {
obj->next = tmp->next;
free(tmp);
}
return;
}
MyLinkedList* cur = obj->next;
for (int i = 1; cur != NULL && cur->next != NULL; i++) {
if (i == index) {
MyLinkedList* tmp = cur->next;
if (tmp != NULL) {
cur->next = tmp->next;
free(tmp);
}
return;
}
else {
cur = cur->next;
}
}
}
void myLinkedListFree(MyLinkedList* obj) {
while (obj != NULL) {
MyLinkedList* tmp = obj;
obj = obj->next;
free(tmp);
}
}
? ? ? ? 已知头结点,使用双指针法一个节点一个节点的处理:
定义一个cur指针(head),pre指针(NULL),定义一个临时变量tmp把cur->next的内容拿出来,然后把pre指针放进去,就完成了节点的反转。更新pre指针(cur),cur指针(tmp)。循环遍历完链表就完成整体的反转。循环结束时,cur一定指向最后一个节点的->next==NULL,pre一定指向最后一个节点。返回pre
c++代码示例如下O(n)
ListNode* reverseList(ListNode* head) {
ListNode* tmp;
ListNode* cur = head;
ListNode* pre = NULL;
while (cur) {
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
还可以用递归实现,能够迭代就一定可以递归!
ListNode* reverse(ListNode* pre, ListNode* cur) {
if (cur == NULL) {
return pre;
}
ListNode* tmp = cur->next;
cur->next = pre;
return reverse(cur, tmp);
}
这个代码实现逻辑和双指针实现逻辑一模一样,实质上都是从前往后反转指针指向,其实还可以从后往前反转指针。(这个方法还不懂)
ListNode* reverseList(ListNode* head) {
if (head == NULL) return NULL;
if (head->next == NULL) return head;
ListNode* last = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return last;
}