您现在的位置是:首页 >技术教程 >代码随想录算法训练营第三天|203.移除链表元素 707.设计链表 206.反转链表网站首页技术教程
代码随想录算法训练营第三天|203.移除链表元素 707.设计链表 206.反转链表
目录
解法一:常规解法
解法二:设置虚拟头结点
LeeCode 203.移除链表元素
解法一:
常规思路:遍历链表寻找符合条件的结点,将该结点的前一个结点的后续指针 指向 这个结点的下一个结点,并删除当前结点。
若需要删除头结点,因为头结点没有上一个结点,所以需要单独考虑这种情况。此时,将头结点向后移动一位,并删除原头结点即可。
class Solution {
public:
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;
}
};
时间复杂度:O(n)
第10行中 while 的条件,如果写成 cur->next != NULL && cur != NULL 会报错(如下,但不理解),不过好在正常人都不会这样写。
Line 10: Char 14: runtime error: member access within null pointer of type 'ListNode' (solution.cpp) SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:29:14
解法二:
为了避免对头结点的特殊处理,可以通过构造虚拟头结点来解决,将其放在头结点之前,最后删除这个虚拟头结点。
class Solution {
public:
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;
}
};
时间复杂度:O(n)
LeeCode 707.设计链表
构造函数来实现一些常见的操作,要求对链表的定义比较熟悉。
需要注意的是本题的index是从0开始。
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++;
}
//将一个值为val的结点插入到链表中下标为index的结点前
//如果index等于链表长度,该结点加在链尾
void addAtIndex(int index, int val) {
if(index > _size) return;//如果index比长度更大,该结点不会插入到链表中
if(index < 0) 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++;
}
//删除第index个结点,若index大于链表长度,直接return
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;
_size--;
}
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);
*/
LeeCode 206.反转链表
解法一:双指针法
思路:改变链表的next指针的指向,直接将链表反转,而不用重新定义一个新的链表。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp;//存储反转后的下一个结点
ListNode* cur = head;
ListNode* pre = NULL;
while(cur){
temp = cur->next;
cur->next = pre;//反转指针
pre = cur;
cur = temp;
}
return pre;
}
};
时间复杂度:O(n)
解法二:递归法
思路:把对指针反向的操作写成递归。(本质和双指针法是一样的)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
return reverse(NULL,head);
}
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
return reverse(cur,temp);
}
};
时间复杂度:O(n)
总结
1.感谢:说实话,本学期刚学过链表,考试和作业也都写过关于定义链表和链表反转的题目,但当时只是应付作业,仅以完成为目的,没深入思考解题方法的内在逻辑,真到动手实践时才发现一堆问题o(╥﹏╥)o。以后还是尽量弄懂,不能光写出来伪代码大体理解思想就放过自己,而是得亲自动手去编译运行,遇到问题、解决问题之后才算真懂。
2.链表的题画图会更容易帮助理解,尤其是多个指针都在移动,但作者能力有限,画的图只能自己看懂,就不放上来了。
3.今天的题目思路比较容易理解,代码实现起来有一定难度,还得继续努力。