第二章 链表part01
强烈要求leetcode官方在链表题目上写清楚有没有头结点,有的题有有的题没,2题debug一直内存泄漏,原来有头结点📕
用时较长,希望下次快一点(o( ̄▽ ̄)ブ)
链表的基础知识
链表的定义:
/**
* 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) {}
* };
*/
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
基本操作复杂度分析:
LeetCode 203.移除链表元素Easy
题目链接:203. 移除链表元素
思路:本题是移除无头结点链表中所有指定val大小的元素,有两种思路(有无头结点都可使用1或2思路)
1是通过创建临时变量temp存储将要删除的元素
2是通过两个指针p,q动态移动,删除指定元素
难点:由于无头结点,需要分开考虑最前面head为val和一般情况下中间值为val两种情况,以1思路为例:处理头指针时,当当前轮次前面head不为空且为val时才能删除head;处理中间值时,head位置已经确定,设current为head,若head后还有值(即current!=NULL&¤t->next!=NULL),方可继续进行判断删除,否则直接不进循环return。
完整C++代码如下:
//无头结点,使用1思路
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//处理头指针为空情况
if(head==NULL)return NULL;
//目的:删除头部所有值为val的节点,确定head位置
while(head!=NULL&&head->val==val){
ListNode *temp=head;//临时变量存储将要删除的head
head=head->next;
delete temp;
}
ListNode* current=head;//此时head位置确定,是否为空不确定,但head上绝不可能为目标val
//有0个或1个值都是直接结束,2个以上进入循环
while(current!=NULL&¤t->next!=NULL){
if(current->next->val==val){
ListNode *temp=current->next;
current->next=current->next->next;
delete temp;
}else{
current=current->next;
}
}
return head;
}
};
//有头结点,使用2思路
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head==NULL)return NULL;
ListNode*p=head;
ListNode*q=p->next;
while(q){
if(q->val==val){
q=q->next;
delete p->next;
p->next=q;
}else{
p=p->next;
q=q->next;
}
}
return head;
}
};
LeetCode 707.设计链表
题目链接:707. 设计链表
思路:一些基本的链表操作
有头结点注意
- 构造函数没什么说的,注意格式
MyLinkedList() : dummy_Head(new LinkedNode(0)), len(0) {}
的学习 - 链表参数有两个,头指针(dummy_Head)指向了头结点(一般头结点的val为无意义,此处设为0)和链表长度(len)
- 由于头结点的存在,在addAtIndex函数和deleteAtIndex函数中可以很舒服的不用考虑首元结点(index为0)没有前驱的情况
注:题目有头结点,默认val为0
完整C++代码如下:
struct LinkedNode
{
int val;
LinkedNode *next;
LinkedNode(int val) : val(val), next(nullptr) {}
};
class MyLinkedList
{
public:
LinkedNode *dummy_Head;
int len;
MyLinkedList() : dummy_Head(new LinkedNode(0)), len(0) {}
int get(int index)
{
if (len < index + 1 || index < 0)
return -1;
LinkedNode *p = dummy_Head->next;
while (index--)
{
p = p->next;
}
return p->val;
}
void addAtHead(int val)
{
LinkedNode *Add_Head = new LinkedNode(val);
Add_Head->next = dummy_Head->next;
dummy_Head->next= Add_Head;
len++;
}
void addAtTail(int val)
{
LinkedNode *Add_Tail = new LinkedNode(val);
LinkedNode *p = dummy_Head;
while (p->next)
{
p = p->next;
}
p->next = Add_Tail;
len++;
}
void addAtIndex(int index, int val)
{
if(index<0||index>len)
return;
LinkedNode *p = dummy_Head;
LinkedNode *Add_Node = new LinkedNode(val);
while (index--)
{
p = p->next;
}
Add_Node->next = p->next;
p->next = Add_Node;
len++;
}
void deleteAtIndex(int index)
{
if (index >= len || index < 0)
return;
LinkedNode *current = dummy_Head;
while (index--)
{
current = current->next;
}
LinkedNode *temp = current->next;
current->next = temp->next;
delete temp;
len--;
temp = nullptr;
}
};
补充:
-
构造函数的作用:
-
给创建的对象建立一个标识符
-
为对象数据成员开辟内存空间;
-
完成对象数据成员的初始化。
-
LeetCode 206.反转链表
题目链接:206. 反转链表
思路:
本题链表没有头结点
-
顺序逆置链表,通过设置一个指针q来遍历链表,每轮让临时变量temp=q,q后移,而temp指向前一个head,完成操作后head后移,从而在head和q的不断后移中完成对链表的逆置,结束时head恰好指向最后一个位置,而q指向nullptr,完成逆置。
- 递归,使用递归法遍历链表,当越过尾节点后终止递归,在回溯时修改各节点的
next
引用指向
- 递归,使用递归法遍历链表,当越过尾节点后终止递归,在回溯时修改各节点的
完整C++代码如下:
//1
//时间复杂度:O(N)
//空间复杂度:O(1)
class Solution
{
public:
ListNode *reverseList(ListNode *head)
{
if (head == nullptr || head->next == nullptr)
return head;
ListNode *q = head->next;
head->next = nullptr;
while(q){
ListNode *temp = q;
q = q->next;
temp->next = head;
head = temp;
}
return head;
}
};
//2
//时间复杂度:O(N)
//空间复杂度:O(N)
class Solution
{
public:
ListNode *reverseList(ListNode *head)
{
return recur(head,nullptr);
}
private:
ListNode* recur(ListNode* cur,ListNode* pre){
if(cur==nullptr)return pre;
ListNode*res=recur(cur->next,cur);//不断递归直到cur->next为空,此时确定cur为最后一个结点(也即res)
cur->next=pre;//回溯过程中不断修改方向,最后pre回到最上层,即开始设立的nullptr
return res;
}
};