链表:由一串节点node组成,node里有一个data数据域和一个next指针域(存放指向下一个节点的指针),第一个节点为head,这是单链表;
双链表:node里还有一个prev,指向上一个节点的next,因此双链表可以向前查询也可以向后查询。
循环链表:就是单链表的头尾连接起来了
删除节点只需要将前一个节点的next指向后一个节点就行,但最好自己手动释放删除节点的内存。
力扣
我都想不通为啥我照抄的代码随想录的代码还是AC不了,服了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while (val == head->val && head !=NULL)
{
ListNode* p = head;
head = head->next;
delete p;
}
ListNode* p = head;
while (p->next != NULL && p!=NULL)
{
if (p->next->val == val)
{
ListNode* d = p->next;
p->next = p->next->next;
delete d;
}
else
{p = p->next;
}
}
return head;
}
};
但是直接copy上面的又过了......无语无语!
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val)
{
//删除头节点,不能是if
while (head != nullptr && head->val == val)
{
head = head->next;
}
ListNode* cur_node = head;
while( cur_node != nullptr && cur_node->next != nullptr )
{
if (cur_node->next->val == val)
{
cur_node->next = cur_node->next->next;
}
else
{
cur_node = cur_node->next;
}
}
return head;
}
};
在删除头节点的while循环处,不能为if的原因是,后面的if判断的都是当前节点的下一个节点值是否=val,如果用if只判断一次,那遇到前几个节点值都为val的情况,始终会留下一个节点值为val的头节点不会被判断到。
如果想判断当前节点值,则需要增加一个哑节点,作为头节点前一个节点:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val)
{
//删除头节点
ListNode dummy(0);
dummy.next = head;
ListNode* cur_node = head;
ListNode* prev_node = &dummy;
while (cur_node != nullptr)
{
if (cur_node->val == val)
{
prev_node->next = cur_node->next;
delete cur_node;
cur_node = prev_node->next;
}
else
{
prev_node = cur_node;
cur_node = cur_node->next;
}
}
return dummy.next;
}
};
代码随想录的解:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) { // 注意这里不是if
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;
}
};
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向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;
}
};
力扣
在构建链表时,记得先定义节点结构体:
struct ListNode{
ListNode* next;
int val;
}
同样的,链表里面还有private数据成员,虚拟节点和链表长度:
private:
ListNode* dummy_node;
int size;
在写增删改查的函数时,注意遍历节点cur_node到底是=dummy_node还是=dummy_node->next;
删除节点时,记得定义一个临时节点存储要删除的节点,然后delete释放内存,再将指针置空:
ListNode* tmp = cur_node->next;
delete tmp;
tmp = nullptr;
如果是节点型指针,构造时要么为空,要么=new ListNode,访问成员时要用->:
ListNode* cur_node = new ListNode(0);
cur_node->next;
class MyLinkedList {
public:
struct ListNode {
int val;
ListNode* next;
ListNode(int val):val(val),next(nullptr) {}
};
MyLinkedList() {
dummy_node = new ListNode(0);
size = 0;
}
int get(int index) {
if (index > size-1 || index < 0) {
return -1;
}
ListNode* cur_node = dummy_node->next;
while (index){ //每找一次就减一次index,直到index为0表示已经找到当前index对应的节点
cur_node = cur_node->next;
index--;
}
return cur_node->val;
}
void addAtHead(int val) {
ListNode* add_node = new ListNode(val);
add_node->next = dummy_node->next;
dummy_node->next = add_node;
size++;
}
void addAtTail(int val) {
ListNode* cur_node = dummy_node; //注意这里cur=虚拟节点而不是头节点
while (cur_node->next != nullptr) {
cur_node = cur_node->next;
}
ListNode* add_node = new ListNode(val);
cur_node->next = add_node;
size++;
}
void addAtIndex(int index, int val) {
if (index > size) {
return;
}
else if (index == size) {
addAtTail(val);
}
else {
ListNode* cur_node = dummy_node;
ListNode* add_node = new ListNode(val);
while (index) {
cur_node = cur_node->next;
index--;
}
add_node->next = cur_node->next;
cur_node->next = add_node;
size++;
}
}
void deleteAtIndex(int index) {
if (index > size-1 || index < 0) {
return;
}
ListNode* cur_node = dummy_node;
while (index){
cur_node = cur_node->next;
index--;
}
ListNode* tmp = cur_node->next;
cur_node->next = cur_node->next->next;
delete tmp; //记得清内存
tmp = nullptr; //避免成为野指针
size--;
}
private:
ListNode* dummy_node;
int size;
};
/**
* 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);
*/
struct ListNodes {
ListNodes* next;
int val;
ListNodes():val(0), next(nullptr){};
ListNodes(int x):val(x), next(nullptr){};
};
class MyLinkedList {
public:
MyLinkedList() {
dummyhead = new ListNodes();
length = 0;
}
int get(int index) {
if (index < 0 || index >= length) return -1;
ListNodes* cur = dummyhead->next;
while (index--) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
ListNodes* newHead = new ListNodes(val);
newHead->next = dummyhead->next;
dummyhead->next = newHead;
length++;
}
void addAtTail(int val) {
ListNodes* newTail = new ListNodes(val);
ListNodes* cur = dummyhead;
while (cur->next != nullptr) cur = cur->next;
cur->next = newTail;
length++;
}
void addAtIndex(int index, int val) {
if (index < 0 || index > length) return;
if (index == length) {
addAtTail(val);
return;
}
else {
ListNodes* cur = dummyhead;
while (index--) {
cur = cur->next;
}
ListNodes* temp = new ListNodes(val);
temp->next = cur->next;
cur->next = temp;
length++;
}
}
void deleteAtIndex(int index) {
if (index < 0 || index >= length) return;
else {
ListNodes* cur = dummyhead;
while (index--) {
cur = cur->next;
}
ListNodes* toDelete = cur->next;
cur->next = cur->next->next;
delete toDelete;
length--;
}
}
private:
ListNodes* dummyhead;
int length;
};
力扣
这道题自己没有想出来,但是看了代码随想录的解释,瞬间觉得太牛了
首先明确的是只需要修改节点的指向,并且一次只能修改一个节点的指向。
如图所示,因为修改的是遍历节点cur到next的指向,所以需要将原本的cur->next记录为temp,在将cur->next改为指向prev后,cur不能再用cur=cur->next的方式遍历(因为next的地址已经更改),但是可以通过赋值的方式,即cur=temp,如此和剩下的链表衔接起来,在挪动cur之前,因先将prev赋值为当前的cur,再完成下一个节点的指向修改。
其中需要注意1、prev的初始化,可以联想为初始化的prev最终会变为尾节点的next,而尾节点的next都是null,因此需要将prev设为Null,即ListNode* prev=NULL。
2、最后返回值prev,因为每次循环的顺序为先改cur->next,然后temp++,cur++,prev++,当最后一条指向修改完毕后,根据执行顺序temp、cur、prev都还要往右挪一个,此时只有prev才指向修改完的头节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev_node = nullptr;//因为最后的尾节点要指向null
ListNode* temp;
ListNode* cur_node = head; //遍历节点
while (cur_node != nullptr) {
temp = cur_node->next; //保存原本下一个节点
cur_node->next = prev_node; //改变当前节点的next指向
prev_node = cur_node; //prev++
cur_node = temp; //cur++
}
return prev_node;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* preNode = nullptr; //最后尾节点必须指向null
ListNode* curNode = head;
ListNode* tempNode;
while (curNode != nullptr) {
tempNode = curNode->next; //先记录马上要断掉的next
curNode->next = preNode; //紧跟着更新next
preNode = curNode; //然后更新pre
curNode = tempNode; //最后cur继续遍历
}
return preNode; //最后cur遍历到了原本尾节点的next(即null),那么pre就变成了翻转后的head
}
};