第一次写链表,很多操作都不太熟,浪费了很多时间,不过看了carl哥的讲解后,对这些问题也都搞明白了,今天头晕脑花的,再接再励吧
对于移除链表,有两种解法,对应于自己是否去加一个虚拟的头节点
如果不加头节点,就需要对第一个元素进行判断是否等于目标值target
例如,head的值也是1,target也是1
此时就需要让head等于head->next
并且删除内存
而此时的head还是1,还需要再次进行相同操作
因此我们用while循环进行判断
while(head&&head->val==val)
{
ListNode* p =head;
head = head->next;
delete p;
}
而对于非head元素p,
如果p->val = target
我们仅需要让前一个元素的next指向该元素的下一个元素
并删除该内存即可
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while(head&&head->val==val)
{
ListNode* p =head;
head = head->next;
delete p;
}
ListNode*p = head;
while(p&&p->next)
{
if(p->next->val ==val)
{
ListNode* temp = p->next;
p->next=temp->next;
delete temp;
}
else
p = p->next;
}
return head;
}
};
假如我们添加了虚拟头节点,对于任意节点,我们都可以对其进行形如上述非头节点的操作
无需再判断是否是头节点
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;
}
};
设计链表真是一个大工程,耗时两个多小时,对链表debug真的太难熬了,大家可以像我一样,一个函数一个函数写,每写完一个函数进行一次运行,如果不是解答错误 就要看代码,就说明代码有了bug,如果是解答错误,就继续往下写,写完下一个函数,看看运行的案例有没有增加,可以判断这个函数错了没,最后也要全局debug。
接下来就要介绍这个题目了
首先,我们要用带虚拟头节点的单链表进行此次大工程,因为有虚拟头节点的存在,函数也会简便很多
很显然,我们要定义一个节点,要包含它的值和指针,为了简便,笔者也定义了它的初始化
struct linkednode{
int val;
linkednode* next;
linkednode(int val):val(val),next(nullptr){}
};
我们定义它的构造函数,定义一个虚拟节点,用来引出链表
public:
MyLinkedList() {
virhead = new linkednode(0);
size = 0;
}
linkednode *virhead;
int size;
对于get函数,
如果index范围越界,我们使其返回-1
我们可以让其进入循环,while(index–),当index为零时,遍历到我们想要的位置
因此此时,定义一个指针temp,temp初始应当指向虚拟头节点virtual
int get(int index) {
if(index>(size-1)||index<0)
return -1;
linkednode* p = virhead->next;
while(index)
{
p=p->next;
index--;
}
return p->val;
}
对于addAtHead函数,比较简单,我们直接开辟一个新节点,其值等于val,
让该节点next指向虚拟头节点的next,让虚拟头节点指向该节点,即可
void addAtHead(int val) {
linkednode *p = new linkednode(val);
p->next = virhead->next;
virhead->next = p;
size++;
}
对于 addAtTail函数,我们也可以先遍历该函数,先使指针指向尾部,再使尾指针指向一个新开辟节点,即可
void addAtTail(int val) {
linkednode *p = new linkednode(val);
linkednode *temp = virhead;
while(temp->next != nullptr)
{
temp = temp->next;
}
temp->next = p;
size++;
}
对于addAtIndex函数,我们先判断index是否越界,不越界,我们遍历到指定位置,将节点插入
void addAtIndex(int index, int val) {
if(index>size)
return ;
if(index<0)index = 0;
linkednode *p = new linkednode(val);
linkednode *temp = virhead;
while(index--)
{
temp = temp->next;
}
p->next = temp->next;
temp->next = p;
size++;
}
对于函数deleteAtIndex,先判断是否越界,在遍历到指定位置,删除即可
void deleteAtIndex(int index) {
if(index>=size||index<0)
return ;
linkednode *temp = virhead;
while(index--)
{
temp = temp->next;
}
linkednode *temp0 = temp->next;
temp->next = temp->next->next;
size--;
delete(temp0);
}
值得注意的是,对于每一个操作,我们可能需要对链表大小进行操作,即增加或减小或不变,只有实时改变链表大小,我们才能在操作之前的判断中,得到合适的结果
class MyLinkedList {
public:
struct linkednode{
int val;
linkednode* next;
linkednode(int val):val(val),next(nullptr){}
};
MyLinkedList() {
virhead = new linkednode(0);
size = 0;
}
int get(int index) {
if(index>(size-1)||index<0)
return -1;
linkednode* p = virhead->next;
while(index)
{
p=p->next;
index--;
}
return p->val;
}
void addAtHead(int val) {
linkednode *p = new linkednode(val);
p->next = virhead->next;
virhead->next = p;
size++;
}
void addAtTail(int val) {
linkednode *p = new linkednode(val);
linkednode *temp = virhead;
while(temp->next != nullptr)
{
temp = temp->next;
}
temp->next = p;
size++;
}
void addAtIndex(int index, int val) {
if(index>size)
return ;
if(index<0)index = 0;
linkednode *p = new linkednode(val);
linkednode *temp = virhead;
while(index--)
{
temp = temp->next;
}
p->next = temp->next;
temp->next = p;
size++;
}
void deleteAtIndex(int index) {
if(index>=size||index<0)
return ;
linkednode *temp = virhead;
while(index--)
{
temp = temp->next;
}
linkednode *temp0 = temp->next;
temp->next = temp->next->next;
size--;
delete(temp0);
}
int size = 0;
linkednode* virhead;
};
反转链表,顾名思义,让其指针反过来就行,因此,我们记录下反转的指针
我们定义两个指针pre和cur,分别记作,前一个指针和当前运转的指针,起始时,令cur指向header,pre则为header的前一项,即为空,引入临时变量,使其记录cur的下一项,如图所示反转即可。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *cur = head;
while(cur!=nullptr)
{
ListNode *temp = cur->next;
cur->next = pre;
pre = cur;
head = pre;
cur = temp;
}
return head;
}
};