203.移除链表元素
题目链接:移除链表元素
视频讲解:手把手带你学会操作链表
此题就是熟悉链表的操作,主要学会了使用虚拟头结点的方法,方便对链表进行增、删。
首先要熟悉定义链表的方式:
struct Listnode {
int val; // 节点存储的值
Listnode *next; // 指向下一个节点的指针
Listnode(int x) : val(x), next(NULL) {} // 节点的构造函数
}
C++是有默认的构造函数的,但用默认构造函数初始化时,不能直接给变量赋值。
通过自己定义构造函数初始化节点:
ListNode* head = new ListNode(5);
使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;
直接使用原来的链表来进行移除节点操作
直接使用原来链表操作,需要把头结点单独考虑,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。若要移除头结点,只需把头结点向后移动一位即可,不过C++移动后要自己删除。把head赋给一个临时指针,在进行移位,最后删除临时指针。如果不创建临时指针,head移位后指向的位置变了,不能找到原来的头结点。
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) //用while,可能head一直等于val,可以连续判断
{
ListNode *tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode *cur = head;
while (cur != NULL && cur->next != NULL) // 保证结点不为空,避免操作空指针
{
if (cur->next->val == val)
{
ListNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else //不等于val,cur向后移一位继续判断
{
cur = cur->next;
}
}
return head;
}
};
设置一个虚拟头结点在进行移除节点操作
虚拟头结点就是在head前面再加一个节点,这样就不用单独考虑head结点了,此时head不再是链表的头结点。
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 虚拟头结点指向头结点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;
}
}
return dummyHead->next;
}
};
707.设计链表
题目链接:设计链表
视频讲解:手把手带你学会操作链表
这题做的时间有点长,主要还是对构造函数不够了解。
class MyLinkedList {
public:
// 定义链表节点
struct LinkNode {
int val;
LinkNode* next;
LinkNode(int val) : val(val), next(nullptr) {}
};
// LinkNode* dummyHead = new LinkNode(0);
// int m_size = 0;
MyLinkedList()
{
dummyHead = new LinkNode(0);
m_size = 0;
}
int get(int index) {
if (index < 0 || index > (m_size - 1))
{
return -1;
}
LinkNode* cur = dummyHead->next;
while (index--) // 让cur定位到index的位置,才能取值
{
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) { // 没有改变原来链表的节点的值,不用定义一个cur
LinkNode* newNode = new LinkNode(val);
newNode->next = dummyHead->next;
dummyHead->next = newNode;
m_size++;
}
void addAtTail(int val) {
LinkNode* newNode = new LinkNode(val);
LinkNode* cur = dummyHead;
while (cur->next != NULL) // 自己想用size--判断,有点儿麻烦
{
cur = cur->next;
}
cur->next = newNode; // newNode->next本身就为空
m_size++;
}
void addAtIndex(int index, int val) {
//先判断index是否有效
if (index < 0) index = 0; // index小于0,则在头部插入节点
if (index > m_size) return; // index大于链表长度,返回空
// index等于链表长度,表示在链表尾部插入一个节点,下面可以满足,不用单独讨论
LinkNode* newNode = new LinkNode(val);
LinkNode* cur = dummyHead;
while (index--) // val插入下标为index节点之前,就要让cur->next指向index
{
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode; //这两步不能顺序不能弄反了,如果先给cur->next赋值,再给newNode->next赋值时cur->next的值就变了
m_size++;
}
void deleteAtIndex(int index) {
if (index < 0 || index >= m_size) return;
LinkNode* cur = dummyHead;
while(index--) // 删除index,就要找到index前面的节点
{
cur = cur->next;
}
LinkNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
// delete命令只是释放了tmp原本所指向的那部分内存,delete后tmp地址并非就是空的,而是随机的
// 要把地址置空,不然tmp会成为野指针,之后可能被其他程序使用
tmp = nullptr;
m_size--;
}
private:
int m_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);
*/
206.反转链表
题目链接:反转链表
视频讲解:帮你拿下反转链表
双指针法
主要理解双指针法,每次只改变两个指针之间的指向,让一个箭头反转后,两个指针同时向前移动一位,反转下一个。
初始链表:1→2→3→4→NULL
NULL→1→2→3→4→NULL (链表头节点前面加空指针pre,加粗表示pre和cur的位置,pre始终在cur前一位)
①NULL←1→2→3→4→NULL
②NULL←1←2→3→4→NULL
③NULL←1←2←3→4→NULL
④NULL←1←2→3←4→NULL
⑤NULL←1←2←3←4←NULL (循环的终止条件是cur = NULL,才能保证返回的pre是反转后的头节点)
(又把tmp声明在while循环里导致不能更新)
//时间复杂度:O(n)
//空间复杂度:O(1)
/**
* Definition for singly-linked list.
* 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) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL; //为了让head变成尾节点后head->next指向空
ListNode* cur = head;
ListNode* tmp;
while (cur) //移动到pre是原来尾节点,cur是空的时候结束
{
tmp = cur->next; // 保存cur下一个节点,不然接下来改变cur->next后找不到原来的值了
cur->next = pre; //把两个指针的箭头翻转
pre = cur;
cur = tmp; //反转后两指针都向前移动一位,下一步循环继续翻转
}
return pre; //此时pre是头结点
}
};