203.移除链表元素
链表这块我不是很熟悉,大一下学C大程的时候摆烂了一学期……摆过的烂最后还是要还QAQ。但是链表这块在仔细研究了卡哥的题解以及知识点讲解后我发现画图可以很好得理清思路,写起来思路非常清晰不会出现问题。下面是我通过画图解析的虚拟头结点法(能统一处理肯定还是统一处理更好嘛)
这里提一嘴我也是第一次接触的概念——构造函数;
这里就拿卡哥给的这个例子吧,我个人的理解是构造函数让我们可以有多种初始化的方式,而且表达形式简洁不需要单独拎出来初始化(不知道理解得对不对哈)
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
我们先画出加入虚拟头结点的状态
然后我们根据条件,如果要判断链表中数值部分是否等于val,那么就要让当前结点的next指针所指向的结点的val部分和val进行比较,翻译过来就是
temp->next->val==val这个判定条件了。
那么如何进行删除操作呢?在链表中无非就是让next指针绕过下一个结点,直接指向下下个结点,翻译一下就是temp->next=temp->next->next
最后需要考虑的是什么时候我们发现所有链表已经遍历结束了呢?假如当前处于temp,那么我们根据之前的判定每次做判定都是temp->next->val,也就是说如果temp目前是最后一个结点,那么说明不需要继续查找了,最后一个结点最大的特征便是next指针指向了NULL,那么判定条件也就是temp->next!=NULL
下面是我的代码,初始版本并没有考虑到释放内存这件事,后来学习了题解之后意识到了,所以添加进去了,tem这个变量就是用来存储即将释放的地址的,因此并没有加入实际运算及操作
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummynode=new ListNode(0);
dummynode->next=head;
ListNode* temp=dummynode;
while (temp->next!= NULL)
{
if (temp->next->val==val)
{
ListNode* tem=temp->next;
temp->next=temp->next->next;
delete tem;
}
else{
temp=temp->next;
}
}
return dummynode->next;
delete dummynode;
}
};
707.设计链表
debug到心态快崩了……主要是每个函数之间都会相互影响,可能在这个地方的报错,往往是你其他地方出现的谬误所导致。基本思路方面我还是作图进行直观理解,效果很不错
我自己本身程序的问题其实出在两个地方,一个是在addAtIndex中多写了一个next,这个是思路上出现的错误,这种错误可以通过图表理解和检验排查。还有一个致命的问题就是我在判定了特殊情况后未能及时return结束函数进程,反而是继续了后续的判定。而正是因为这么小的两个错误,让我debug了估计有一个半小时。。。导致我在凌晨两三点中身心俱疲,最后猛然醒悟还是靠的题解一段一段替换我的代码来找出问题所在,leetcode上面无法追踪数据让debug难度直线上升QAQ
最后上代码(实在太累了,内存释放什么的没有补充,以后再完善)
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
_dummy=new LinkedNode(0);
_size=0;
}
int get(int index) {
int i;
LinkedNode* temp=new LinkedNode(0);
temp=_dummy;
if (index>=_size || index<0) return -1;
for (i=1;i<=index+1;i++)
{
temp=temp->next;
}
return temp->val;
delete temp;
}
void addAtHead(int val) {
LinkedNode* temp=new LinkedNode(val);
temp->next=_dummy->next;
_dummy->next=temp;
_size++;
}
void addAtTail(int val) {
LinkedNode* temp=new LinkedNode(0);
LinkedNode* tail=new LinkedNode(val);
temp=_dummy;
while (temp->next!=nullptr)
{
temp=temp->next;
}
temp->next=tail;
_size++;
}
void addAtIndex(int index, int val) {
int i;
LinkedNode* temp=new LinkedNode(0);
LinkedNode* add=new LinkedNode(val);
if (index==_size)
{
addAtTail(val);
return;
}
if (index>_size || index<0) return;
temp=_dummy;
for (i=1;i<=index;i++)
{
temp=temp->next;
}
add->next=temp->next;
temp->next=add;
_size++;
}
void deleteAtIndex(int index) {
int i;
LinkedNode* temp=new LinkedNode(0);
if (index>=_size || index<0)
{
return;
}
temp=_dummy;
for (i=1;i<=index;i++)
{
temp=temp->next;
}
temp->next=temp->next->next;
_size--;
}
private:
int _size;
LinkedNode* _dummy;
};
206. 反转链表
经过第二题的折磨,这题可以说是宝宝巴士了。经典双指针法,一个读取,一个存储,直接把链表的箭头转向即可,注意初始化即可
大概图解如下
下面是代码
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* left=new ListNode(0);
ListNode* right=new ListNode(0);
ListNode* temp=new ListNode(0);
left=NULL;
right=head;
while (right!=NULL)
{
temp=right->next;
right->next=left;
left=right;
right=temp;
}
return left;
}
};