LeetCode203. 移除链表元素
思路:想到先设置一个虚拟头结点,指向该链表头结点,再进行删除操作。
C++代码如下(错误!):
/**
* 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* dummyHead = new ListNode(0); //设置一个虚拟头结点,初始化节点为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;
}
cur = cur->next; //更新cur //此处逻辑有问题!!
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
以上是我原来的写法,跑的时候发现执行出错了,报错:Line 20: Char 21: runtime error: member access within null pointer of type 'ListNode' (solution.cpp)。
后面我自己模拟走了一遍,发现按照上面的写法,在判断条件cur->next != NULL中,我只能判断cur->next不为空,我没法判断cur->next->next是否为空,但是倘若进入if(cur->next->val == val)结构体中,会执行cur->next = cur->next->next语句,也就是说此时cur的后继节点变成了cur->next->next,那么我在下面更新cur时就是直接把cur更新到cur->next->next,但是在上面我只判断了cur->next是否为空,并没有判断cur->next->next是否为空,这就会出现cur->next->next为空的情况下被更新为cur,也就是cur变成NULL了。
修改如下(正确!):
/**
* 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* dummyHead = new ListNode(0); // 设置一个虚拟头结点,初始化节点为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; //更新cur
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
LeetCode707. 设计链表
思路:这一题其实主要考察对链表的常见操作,我是设置一个虚拟头结点再进行操作,这样更加方便。
C++代码如下:
class MyLinkedList
{
public:
//设置链表节点的结构体
struct LinkedNode
{
int val;
LinkedNode* next;
LinkedNode(int val) : val(val), next(nullptr)
{
}
};
MyLinkedList() //初始化链表
{
_dummyHead = new LinkedNode(0); //这里定义的头结点 是一个虚拟结点,而不是真正的链表头结点
_size = 0;
}
//获取第index个节点数据,如果index是非法数值直接返回-1。注意index是从0开始的,第0个节点就是头结点!!头节点后面的那结点才是第1个结点。
int get(int index)
{
if((index < 0))
{
return -1;
}
LinkedNode* cur = _dummyHead->next; //这个地方一开始不太理解,为啥没有_dummyHead->next=head这句代码呢?如果不将虚拟头结点指向head,后面怎么移动怎么读取val?再说了,_dummyHead->next不定义的话它指的是啥咱也不知道啊? //后面弄明白了,就在上面定义的结构体的构造函数里,我已经初始化了_dummyHead变量,那么_dummyHead->next就是null了,其实也是有定义了。
while(index--)
{
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) //添加值为val的节点,作为链表的头节点
{
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
void addAtTail(int val) //添加值为val的节点,作为链表的尾节点
{
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != NULL)
{
cur = cur->next;
}
newNode->next = NULL;
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val) //在链表的第index个节点之前添加值为val的节点
{
if(index > _size)
{
return;
}
if(index < 0)
{
index = 0;
}
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while((index--) && (cur->next != NULL))
{
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
//return _dummyHead->next; //没说要返回值
}
void deleteAtIndex(int index)
{
if(index >= _size)
{
return;
}
if(index < 0)
{
return;
}
LinkedNode* cur = _dummyHead;
while(index--)
{
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
_size--;
}
//打印链表
void printLinkedList()
{
LinkedNode* cur = _dummyHead;
while(cur->next != NULL)
{
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);
*/
LeetCode206. 反转链表
思路:一开始想直接反转,但是有些绕,没写出来。后来看了题解,使用双指针法。
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
C++代码如下:
/**
* 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* pre = NULL;
ListNode* cur = head;
while(cur != NULL)
{
ListNode* tmp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
pre = cur; // 更新pre 和 cur指针
cur = tmp;
}
return pre;
}
};
分析:注意在while循环体判断时,判断条件是cur != NULL,我原本写的是cur->next != NULL,然后循环体外写的是 return cur,会报错,后来分析了一下:因为head有可能为NULL,那么cur = head也为NULL了,那就谈不上cur->next != NULL,所以报错。后面改成判断cur != NULL,以及改成return pre 就通过了。
今日总结:
- LeetCode707. 设计链表 要多加练习!写的不够熟练。
- LeetCode206. 反转链表可以拓展思维,可以多用一个指针来辅助,使用双指针法,直接写的话很绕。