题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
解答链接:代码随想录
这道题比较基本,用链表移除元素。
(1)移除头节点:将头节点移动至下一个元素;或者创造虚拟头节点避免这个问题;
(2)移除中间元素:上一个指向下一个;
1. 链表的结构需要熟悉:
链表头指针为head,这就是链表的调用方式。最后一个指针指向NULL,因此ptr==NULL是结束遍历的条件。链表最小是空链表,也就是head==NULL。
2. 移除元素需要分头节点和其他节点,方法有所不同
3. 移动操作共分为三步:替换(将需要删除的赋给另一个变量);换位(进行搭建新链的操作);移除(将新变量删除,释放内存)
1. 判断条件:判断条件分为两块
(1) 一个是元素是否存在(例如,删除头元素需要看头元素是否存在,head!=NULL,删除中间元素需要看上一个和这个是否存在,cur!=NULL && cur->next!=NULL)
(2) 另一个是元素是否应该被操作(这里是cur->val==val)。
2. 创造节点,需要用new
3. 如果使用虚拟节点,之后返回的时候需要返回head=dummy->next,而不是head。因为这里head也是可以被删除的,所以可能不存在,dummyhead是不会变动的。
ListNode* removeElements(ListNode* head, int val) {
// 删除头节点:将头节点进行替换
// 前提:头节点存在(表不为空),头节点需要被删除
while(head != NULL && head->val == val){
ListNode *temp = head; // 替换
head = head->next; // 换位
delete temp; // 删除
}
// 删除非头节点:从它前一个元素开始,跳过它
// 前提:前一个元素和这个元素存在,这个元素需要被删除
ListNode *cur = head;
while(cur != NULL && cur->next != NULL){
if(cur->next->val == val){
ListNode *temp = cur->next; // 替换
cur->next = cur->next->next; // 换位
delete temp; // 删除
}else{
cur = cur->next; // 挪动
}
}
return head;
}
ListNode* removeElements(ListNode* head, int val) {
ListNode *dummyfirst = new ListNode();
dummyfirst->next = head;
ListNode *cur = dummyfirst;
// 删除非头节点:从它前一个元素开始,跳过它
// 前提:前一个元素和这个元素存在,这个元素需要被删除
while(cur != NULL && cur->next != NULL){
if(cur->next->val == val){
ListNode *temp = cur->next; // 替换
cur->next = cur->next->next; // 换位
delete temp; // 删除
}else{
cur = cur->next; // 挪动
}
}
head = dummyfirst->next;
delete dummyfirst;
return head;
}
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
答案链接:代码随想录
这个题比较简单,但是bug非常多,接下来逐一梳理。
(1)get,addindex,delete需要判断给定的index是否合适。对于get,index属于[0,size),addindex的index属于[0,size],delete的index属于[0,size)。
addindex,delete操作的时候要从index-1开始,因此用于遍历的tempnode = dummyhead,while里面一共循环index次,循环完正好能将tempnode移动至index-1。
get操作的时候要在index上,因此用于遍历的tempnode = dummyhead->next,循环完正好能将tempnode移动至index。
(2)利用while(index--)来代替计数器
(3)在add的时候,new->next = temp->next,如果是加入头节点,temp是dummy。容易想成temp->next->next,这里其实new和temp是并行的关系,之后new加入。
(4)size不要忘记变化
(5)delete后,空的指针地址是随机的,为了避免之后误触,需要赋值为nullptr
(6)最关键的bug:插入元素
new_node->next = temp_node->next
temp_node->next = new_node
第一行写成了 temp_node->next =new_node->next
先联络,再脱钩,这里相当于先脱钩,再联络了,后面的已经跑掉了。联络需要让新的被赋值,被赋值的在前面,顺序一定不能搞乱。
class MyLinkedList {
public:
struct LinkNode{
int val;
LinkNode *next;
LinkNode(int val):val(val),next(nullptr){}
LinkNode():val(0),next(nullptr){}
LinkNode(int v,LinkNode* n):val(v),next(n){}
};
MyLinkedList(){ // 成员函数
dummyhead = new LinkNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
size = 0;
}
int get(int index) {
if(index > (size - 1) || index < 0){
return -1; // 先进行判断
}
LinkNode *tempnode = dummyhead->next;
while(index--){ // 比较好的方式,不用空置count判断
tempnode = tempnode->next;
}
return tempnode->val;
}
void addAtHead(int val) {
LinkNode *newnode = new LinkNode(val);
newnode->next = dummyhead->next; // 不是nextnext
dummyhead->next = newnode;
size++; // 一定要记住size变化
}
void addAtTail(int val) {
LinkNode *newnode = new LinkNode(val);
LinkNode *tempnode = dummyhead; //
while(tempnode->next!=nullptr){
tempnode = tempnode->next;
}
tempnode->next = newnode;
size++;
}
void addAtIndex(int index, int val) {
if(index > size) return;
if(index < 0) index = 0; // 需要注意一下
LinkNode *newnode = new LinkNode(val);
LinkNode *tempnode = dummyhead;
// 插入最好从当前元素的后面插入,而非index之前
while(index--){
tempnode = tempnode->next;
} // tempnode 会指向下标为index-1的节点,因为tempnode是从dummy(index=-1)开始的。如果从head(index=0)开始,就会指向下标为index的节点
newnode->next = tempnode->next;
tempnode->next = newnode;
size++;
}
void deleteAtIndex(int index) {
if(index > (size - 1) || index < 0){
return; // 先进行判断
}
LinkNode *tempnode = dummyhead;
while(index--){
tempnode = tempnode->next;
}
LinkNode *delnode = tempnode->next;
tempnode->next = tempnode->next->next;
delete delnode;
delnode=nullptr;
size--;
}
private:
int size;
LinkNode* 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);
*/
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
解答链接:代码随想录
想到了遍历、逐一操作这件事,但是在具体实现过程中卡在了如何保证脱链但维持信息
方法就是将脱离的,暂时存在一个地方。其实这个过程中有两个脱离,一个是和之前一个节点的(需要指回去),一个是和之后一个节点的(temp = temp->next)。
因此,需要设置三个临时变量,一个记录前一个,一个记录当前的,一个记录后一个。让后一个成为后一个,当前的指向前一个,前一个变成当前的,当前的变成后一个(temp)。
在临时设置变量方面,需要考虑有多少个节点需要断开。
同时,这道题可以用递归的方法进行,但是必要性不高(如同Fib数列),因此先不进行实现。
ListNode* reverseList(ListNode* head) {
ListNode *temp;
ListNode *Cur = head;
ListNode *Pre = NULL;
while(Cur != nullptr){
temp = Cur->next;
Cur->next = Pre;
Pre = Cur;
Cur = temp;
}
return Pre;
}