目录
知识点:
链表:通过指针串联在一起的线性结构,每个节点包含指针域和数据域。
类型: 单链表(每个节点指向下一个节点),双链表(每个节点指向前节点和后节点),循环链表 (链表首尾相连)。
删除和添加:时间复杂度是O(1),但是查找的时间复杂度是O(n)。与数组相反。
代码模板总结:
1、链表的定义及调用方式:
//定义
struct ListNode {
int val; // 节点存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val (x), next(NULL) {} // 节点的构造函数
};
//构造初始化节点
ListNode* head = new ListNode(5);
1、虚拟节点定义
当涉及到链表的删除操作时,我们可以设置一个虚拟节点,让其指向链表的头节点,这样就可以针对整个链表进行相同的删除操作,而不是单独处理头节点。
//设置虚拟节点,虚拟节点指向头节点
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
//查询整个链表
ListNode* cur = dummyHead;
while (cur->next != NULL) {
}
//删除虚拟节点
head = dummyHead->next;
delete dummyHead;
return head;
错误: 用虚拟节点时,开始要将虚拟节点的next指向头节点。结束时,要设置头节点是虚拟节点的next,删除虚拟节点,返回头节点。
2、交换节点
有节点需要预留,有节点需要改变,
保存都是temp(临时) = cur(当前);换节点用cur = temp。
/*保存节点的时候,temp……=cur……*/
ListNode* temp = cur->next;
ListNode* temp1 = cur->next->next;
ListNode* temp2 = cur->next->next->next;
/*换结点的时候,cur……=temp……*/
cur->next = temp1;
cur->next->next = temp;
cur->next->next->next = temp2;
3、查找节点操作
//求链表A的长度
int lenA = 0;
while(curA != NULL) {
lenA++;
curA = curA->next;
}
//查找index处的节点
LinkedNode* cur = dummyNode->next;
while(index--) {
cur = cur->next;
}
//遍历当前链表(可能在末尾添加节点)
LinkedNode* cur = dummyNode;
while(cur->next!=NULL) {
cur = cur->next;
}
cur->next = newNode;
题:
203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com)
注意:1、c++删除节点操作中记得释放内存。
2、设置一个虚拟节点,可以将头节点和其他节点以相同方式移除。
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* temp = cur->next;
cur->next = cur->next->next;
delete temp;
} else {
cur = cur->next;
}
}
//删除虚拟节点
head = dummyHead->next;
delete dummyHead;
return head;
}
};
707. 设计链表 - 力扣(LeetCode) (leetcode-cn.com)
class MyLinkedList {
public:
//定义链表结构体,结构体是大驼峰,结构体尾部有分号
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int x):val(x), next(NULL){}
};
//初始化链表
MyLinkedList() {
//定义一个虚拟节点,不是真正的头节点
dummyNode = new LinkedNode(0);
size = 0;
}
//获取链表中第 index 个节点的值
int get(int index) {
//判断是否超出限制
if(index > size - 1 || index < 0) {
return -1;
}
LinkedNode* cur = dummyNode->next;
//查询列表
while(index--) {
cur = cur->next;
}
return cur->val;
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
//定义一个新节点
LinkedNode* newNode = new LinkedNode(val);
//指向虚拟节点后的头节点
newNode->next = dummyNode->next;
//虚拟节点指向该节点
dummyNode->next = newNode;
//长度++
size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
//定义一个新节点
LinkedNode* newNode = new LinkedNode(val);
//查询当前链表
LinkedNode* cur = dummyNode;
while(cur->next!=NULL) {
cur = cur->next;
}
cur->next = newNode;
//长度++(切记)
size++;
}
/*在链表中的第 index 个节点之前添加值为 val 的节点。
如果 index 等于链表的长度,则该节点将附加到链表的末尾。
如果 index 大于链表长度,则不会插入节点。
如果 index 小于0,则在头部插入节点。*/
void addAtIndex(int index, int val) {
if(index > size) { return;}
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = dummyNode;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
size++;
}
//如果索引 index 有效,则删除链表中的第 index 个节点。
void deleteAtIndex(int index) {
//判断是否有效,可以等于当前长度
if(index >= size || index < 0) return;
//定义虚拟节点
//LinkedNode* dummyNode = newNode* dummyNode;
//当前节点(第一处错误)
LinkedNode* cur = dummyNode;
//查找
while (index--) {
cur = cur->next;
}
//找到该节点
LinkedNode* temp = cur->next;
cur->next = temp->next;
//释放并减一
delete temp;
size--;
}
//打印链表
void printLinkList() {
//定义节点
LinkedNode * cur = dummyNode;
//查找
while (cur->next != NULL) {
cout << cur->next->val <<" ";
cur = cur->next;
}
cout <<endl;
}
//注意还定义了私有成员
private:
int size;
LinkedNode* dummyNode;
};
链表中很多基础比较重要 。
24. 两两交换链表中的节点 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyNode = new ListNode(0);
dummyNode->next = head;
ListNode* cur = dummyNode;
while(cur->next != nullptr && cur->next->next != nullptr) {
/*保存节点的时候,temp……=cur……*/
// 保存临时1,2,3节点
ListNode* temp = cur->next;
ListNode* temp1 = cur->next->next;
ListNode* temp2 = cur->next->next->next;
/*换结点的时候,cur……=temp……*/
//步骤一
cur->next = temp1;
//步骤二
cur->next->next = temp;
//步骤三
cur->next->next->next = temp2;
//后移两位
cur = cur->next->next;
}
return dummyNode->next;
}
};
像这种涉及到很多节点之间交换,需要存下来的,
保存都是temp(临时) = cur(当前);换节点用cur = temp。就会不出错。
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0);
dummyNode->next = head;
ListNode* cur = dummyNode;
ListNode* temp = dummyNode;
int size = 0;
//快指针走到头
while(cur->next != 0) {
size++;
//慢指针比快指针少走n步
if(size > n) {
temp = temp->next;
}
cur = cur->next;
}
ListNode* temp2;
temp2 = temp->next;
temp->next = temp->next->next;
delete temp2;
return dummyNode->next;
}
};
错误:返回头结点的时候不要return head,存在没有链表头节点消失的情况,用虚拟节点。
面试题 02.07. 链表相交 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0, dif = 0;
//求链表A的长度
while(curA != NULL) {
lenA++;
curA = curA->next;
}
//求链表B的长度
while(curB != NULL) {
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
//求长度差,假设最大的是A
if(lenA < lenB) {
swap(lenA, lenB);
swap(curA, curB);
}
dif = lenA - lenB;
//尾部位置对其,即较长的链表向前移动长度差
while(dif--) {
curA = curA->next;
}
//遍历数组
while(curA != NULL) {
if(curA == curB)
return curA;
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
错误:1、判断节点长度的时候,用cur->next会引起超出数组。
2、本题是两个节点相等,即指针相同,不是数值相同。
142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com)
判断是否有环:定义快慢指针,fast走两个节点,slow走一个节点,如果有环一定会在环内相遇。
相遇地点:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
if(fast == slow) {
ListNode* meet = fast;
ListNode* start = head;
while(meet != start) {
meet = meet->next;
start = start->next;
}
return meet;
}
}
return NULL;
}
错误:1、循环时,保证fastNode不是空节点,且fastNode->next也不是空节点。
2、让节点先移动,再判断,否则两个结点都是头节点,直接相等。