刷完题感觉自己链表的基础很不牢固。。
203.移除链表元素
这个比较简单,和之前删除数组中指定元素有点类似,但链表需要及时将对应的节点删除,而不是单纯的数值覆盖,这个题目的关键在于创建一个虚拟头节点,让头节点与中间节点,尾节点的删除操作完全一致,这样代码具有通用性。
这是我的代码,仅供参考。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* fast = head;
ListNode* slow = new ListNode();
ListNode* result = slow;
while(fast){
if(fast -> val != val){
slow -> next = fast;
slow = slow -> next;
}
fast = fast -> next;
}
slow -> next = NULL;
return result -> next;
}
};
707.设计链表
这个主要是工作量大一点,非常建议用VS来debug,这一题花了我2个小时时间才通过。。。
感觉链表的增删查高度依赖于元素的下标,所以有必要创建一个临时变量来记录,另外,在头部插入,尾部插入,指定下标插入时,建议统一采用下标与链表长度比较的判断条件来实现,因为我没有采用统一的方式踩了雷。。。
这是我的代码,仅供参考。
class MyLinkedList {
public:
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) {}
};
MyLinkedList() { //done
this -> dummy = new ListNode(0);
this -> len = 0;
}
int get(int index) { //done
if(index < 0 || index >= this -> len)
return -1;
ListNode* current = this -> dummy;
int count = -1;
while(count < index){
count++;
current = current -> next;
}
return current -> val;
}
void addAtHead(int val) { //done
ListNode* newhead = new ListNode(val, this -> dummy -> next);
this -> dummy -> next = newhead;
this -> len++;
}
void addAtTail(int val) { //done
ListNode* newtail = new ListNode(val);
ListNode* current = this -> dummy;
while(current -> next){
current = current -> next;
}
current -> next = newtail;
this -> len++;
}
void addAtIndex(int index, int val) { //done
if(index > this -> len)
return ;
int count = -1;
ListNode* current = this -> dummy;
while(count < index - 1){
count++;
current = current -> next;
}
ListNode* newnode = new ListNode(val, current -> next);
current -> next = newnode;
this -> len++;
}
void deleteAtIndex(int index) {
if(index < 0 || index >= this -> len)
return ;
int count = -1;
ListNode* current = this -> dummy;
while(count < index - 1){ //定位到删除节点的前一个节点
current = current -> next;
count++;
}
current -> next = current -> next -> next;
this -> len--;
}
private:
int len; //链表长度
ListNode* dummy; //虚拟头节点
};
/**
* 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);
*/
206.反转链表
这个用暴力点的办法也能做,就是用一个向量,在遍历链表的同时依次将值填入,然后重新遍历链表,从后往前遍历向量,依次将值填入链表就可以。看完视频讲解以后感觉双指针也很简洁,前面的暴力法在于不改变节点的相对顺序,而改变里面的值,而双指针法则是不改变节点里存储的值,改变节点之间的相对顺序,递归有点晦涩难懂,但是理解了双指针的解法以后,递归的代码也很好写。
双指针法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
return reverseNode(NULL, head);
}
ListNode* reverseNode(ListNode* pre, ListNode* current){
if(current == NULL)
return pre;
ListNode* temp = current -> next;
current -> next = pre;
pre = current;
current = temp;
return reverseNode(pre, current);
}
};
递归法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
return reverseNode(NULL, head);
}
ListNode* reverseNode(ListNode* pre, ListNode* current){
if(current == NULL)
return pre;
ListNode* temp = current -> next;
current -> next = pre;
pre = current;
current = temp;
return reverseNode(pre, current);
}
};
基础不牢,地动山摇,这几个题目以后还得反复刷,特别是设计链表这一题,很考验基本功。