203.移除链表元素
重点在认识到虚头节点的方便性。移除元素时要从这个元素的前一个结点p入手,p->next = p->next->next,另外要创建一个temp指针,把这个temp删掉。
返回时注意返回虚拟头节点的next,而不是直接返回head,因为head可能也会被删,虚拟头节点存在的意义就是使得对头节点的删除操作可以变得和其他节点一样。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* myhead = new ListNode(0);
myhead->next = head;
ListNode* p = myhead;
while(p->next){
if(p->next->val==val){
ListNode* t = p->next;
p->next = t->next;
delete t;
}else
p = p->next;
}
return myhead->next;
}
};
707.设计链表
这道题是理解链表的重中之重。
1.定义链表节点结构体
2.初始化链表时要创建虚拟头节点。
3.之后的链表操作基本步骤都是定义cur指向虚拟头结点,然后遍历到正确的位置执行插入或删除。因此要非常注意while的条件,以及步骤之间的执行顺序。
class MyLinkedList {
public:
struct pNode {
int val;
pNode* next;
pNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
pHead = new pNode(0);
size = 0;
}
int get(int index) {
if(index>(size-1)||index<0){
return -1;
}
int i=index;
pNode* cur = pHead->next;
while(i--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
pNode* t = new pNode(val);
t->next = pHead->next;
pHead->next = t;
size++;
}
void addAtTail(int val) {
pNode* tail = new pNode(val);
pNode* cur = pHead;
while(cur->next){
cur = cur->next;
}
cur->next = tail;
size++;
}
void addAtIndex(int index, int val) {
if(index<=size){
pNode* t = new pNode(val);//待插入的节点
pNode* cur = pHead;
while(index--){
cur = cur->next;
}
t->next = cur->next;
cur->next = t;
size++;
}
}
void deleteAtIndex(int index) {
if(index>(size-1)||index<0){
return;
}
pNode* cur = pHead;
while(index--){
cur = cur->next;
}
pNode* t = cur->next;
cur->next = t->next;
delete t;
t=nullptr;
size--;
}
private:
int size = 0;
pNode* pHead;
};
/**
* 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.反转链表
这里采用双指针法。
由于最后一个结点的next是NULL,那么反转前的pre也应该赋初值为NULL,而cur指向head;
我们希望反转方向,让cur->next = pre,但又不能让整个链表断,就需要先用t存储cur->next,然后再让cur->next = pre.
接着让pre和cur都往后移。必须先移pre,不然链表就断了,pre = cur,cur = t,这俩指针都挪到了它后面那个指针的位置。
再看while循环条件,最后结束时,cur指向最后的null,pre指向上图4的位置,也就是新的头结点。也就是while(cur) 和 return pre;
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* cur = head;
while(cur){//循环结束时cur指向NULL,不需要再动
ListNode* t = cur->next;
cur->next = pre;
pre = cur;
cur = t;
}
return pre;
}
};
感想:
感觉链表的操作其实都不难,但是很注重一些while填什么,从哪个位置开始,步骤的先后顺序又是什么,需不需要temp指针临时存储以防断连这样的细节。需要我从目的操作出发,遇到问题思考对应的解决方案,弄清楚执行顺序。