文章目录
链表
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
1.单链表
2.双链表(每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点)
3.循环链表
- 定义节点
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) {}
};
//1.通过自己定义的节点构造函数
ListNode* head = new ListNode(5);
//2.使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;
1.设计链表
707.设计链表
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev
以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
- get(index):获取链表中第
index
个节点的值。如果索引无效,则返回-1
。 - addAtHead(val):在链表的第一个元素之前添加一个值为
val
的节点。插入后,新节点将成为链表的第一个节点。 - addAtTail(val):将值为
val
的节点追加到链表的最后一个元素。 - addAtIndex(index,val):在链表中的第
index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。 - deleteAtIndex(index):如果索引
index
有效,则删除链表中的第index
个节点。
设置一个虚拟头结点 比较简单
1.单链表
class MyLinkedList {
public:
struct ListNoded{
int val;
ListNoded* next;
ListNoded() : val(0),next(nullptr){}
ListNoded(int x) : val(x),next(nullptr){}
ListNoded(int x,ListNoded* next) : val(x),next(next){}
};
MyLinkedList() {
head = new ListNoded(); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
size = 0;
}
int get(int index) {
if(index < 0 || index >= size){
return -1;
}
else{
ListNoded* curGetNode = head->next;
for(int i = 0;i < index; i++){
curGetNode = curGetNode->next;
}
return curGetNode->val;
}
}
void addAtHead(int val) {
ListNoded* newHead = new ListNoded(val);
newHead->next = head->next;
head->next = newHead;
size++;
}
void addAtTail(int val) {
ListNoded* newTail = new ListNoded(val);
ListNoded* curAddTailNode = head;
for(;curAddTailNode->next != nullptr;){
curAddTailNode = curAddTailNode->next;
}
curAddTailNode->next = newTail;
size++;
}
void addAtIndex(int index, int val) {
if(index == size){
addAtTail(val);
}
else if(index == 0){
addAtHead(val);
}
else if(index < 0){
addAtHead(0);
}
else if(index > size){
return;
}
else{
ListNoded* newIndex = new ListNoded(val);
ListNoded* curAddIndexNode = head;
for(int i = 0;i < index;i++){
curAddIndexNode = curAddIndexNode->next;
}
newIndex->next = curAddIndexNode->next;
curAddIndexNode->next = newIndex;
size++;
}
}
void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
else{
ListNoded* curDeleteNode = head;
for(int i = 0;i < index;i++){
curDeleteNode = curDeleteNode->next;
}
ListNoded* temp = curDeleteNode->next;
curDeleteNode->next = curDeleteNode->next->next;
delete temp;
size--;
}
}
private:
int size;
ListNoded* head;
};
/**
* 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);
*/
2.双向链表
class MyLinkedList {
public:
struct ListNoded{
int val;
ListNoded* next;
ListNoded* prev;
ListNoded() : val(0),next(nullptr),prev(nullptr){}
ListNoded(int x) : val(x),next(nullptr),prev(nullptr){}
};
MyLinkedList() {
head = new ListNoded(); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
end = new ListNoded();
head->next = end;
end->prev = head;
size = 0;
}
int get(int index) {
if(index < 0 || index >= size){
return -1;
}
else{
ListNoded* curGetNode = head->next;
for(int i = 0;i < index; i++){
curGetNode = curGetNode->next;
}
return curGetNode->val;
}
}
void addAtHead(int val) {
ListNoded* newHead = new ListNoded(val);
newHead->next = head->next;
newHead->prev = head;
head->next->prev = newHead;
head->next = newHead;
size++;
}
void addAtTail(int val) {
ListNoded* newTail = new ListNoded(val);
ListNoded* curAddTailNode = head;
for(;curAddTailNode->next != end;){
curAddTailNode = curAddTailNode->next;
}
newTail->next = curAddTailNode->next;
newTail->prev = curAddTailNode;
curAddTailNode->next->prev = newTail;
curAddTailNode->next = newTail;
size++;
}
void addAtIndex(int index, int val) {
if(index == size){
addAtTail(val);
}
else if(index == 0){
addAtHead(val);
}
else if(index < 0){
addAtHead(0);
}
else if(index > size){
return;
}
else{
ListNoded* newIndex = new ListNoded(val);
ListNoded* curAddIndexNode = head;
for(int i = 0;i < index;i++){
curAddIndexNode = curAddIndexNode->next;
}
newIndex->next = curAddIndexNode->next;
newIndex->prev = curAddIndexNode;
curAddIndexNode->next->prev = newIndex;
curAddIndexNode->next = newIndex;
size++;
}
}
void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
else{
ListNoded* curDeleteNode = head;
for(int i = 0;i < index;i++){
curDeleteNode = curDeleteNode->next;
}
ListNoded* temp = curDeleteNode->next;
curDeleteNode->next->next->prev = curDeleteNode;
curDeleteNode->next = curDeleteNode->next->next;
delete temp;
size--;
}
}
private:
int size;
ListNoded* head;
ListNoded* end;
};
/**
* 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);
*/
2.改了一点点,使prev有用
class MyLinkedList {
public:
struct ListNoded{
int val;
ListNoded* next;
ListNoded* prev;
ListNoded() : val(0),next(nullptr),prev(nullptr){}
ListNoded(int x) : val(x),next(nullptr),prev(nullptr){}
};
MyLinkedList() {
head = new ListNoded(); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
end = new ListNoded();
head->next = end;
end->prev = head;
size = 0;
}
int get(int index) {
if(index < 0 || index >= size){
return -1;
}
else if(index <= (size - 1) / 2){
ListNoded* curGetNode = head->next;
for(int i = 0;i < index; i++){
curGetNode = curGetNode->next;
}
return curGetNode->val;
}
else{
ListNoded* curGetNode = end->prev;
index = size - index - 1;//从后往前查找,查找次数要变
for(int i = 0;i < index; i++){
curGetNode = curGetNode->prev;
}
return curGetNode->val;
}
}
void addAtHead(int val) {
ListNoded* newHead = new ListNoded(val);
newHead->next = head->next;
newHead->prev = head;
head->next->prev = newHead;
head->next = newHead;
size++;
}
void addAtTail(int val) {
ListNoded* newTail = new ListNoded(val);
ListNoded* curAddTailNode = head;
for(;curAddTailNode->next != end;){
curAddTailNode = curAddTailNode->next;
}
newTail->next = curAddTailNode->next;
newTail->prev = curAddTailNode;
curAddTailNode->next->prev = newTail;
curAddTailNode->next = newTail;
size++;
}
void addAtIndex(int index, int val) {
if(index == size){
addAtTail(val);
}
else if(index == 0){
addAtHead(val);
}
else if(index < 0){
addAtHead(0);
}
else if(index > size){
return;
}
else if(index <= (size - 1) / 2){
ListNoded* newIndex = new ListNoded(val);
ListNoded* curAddIndexNode = head;
for(int i = 0;i < index;i++){
curAddIndexNode = curAddIndexNode->next;
}
newIndex->next = curAddIndexNode->next;
newIndex->prev = curAddIndexNode;
curAddIndexNode->next->prev = newIndex;
curAddIndexNode->next = newIndex;
size++;
}
else{
ListNoded* newIndex = new ListNoded(val);
ListNoded* curAddIndexNode = end;
index = size - index;//!!!因为是要插入新节点到index的前面,所以新节点插入位置的前一个位置是index-1,后一个位置是index。所以从左往右遍历时,遍历到index-1的位置;从右往左遍历时,遍历到index(而不是index+1)。注意这点不同。
for(int i = 0;i < index;i++){
curAddIndexNode = curAddIndexNode->prev;
}
newIndex->prev = curAddIndexNode->prev;
newIndex->next = curAddIndexNode;
curAddIndexNode->prev->next = newIndex;
curAddIndexNode->prev = newIndex;
size++;
}
}
void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
else if(index <= (size - 1) / 2){
ListNoded* curDeleteNode = head;
for(int i = 0;i < index;i++){
curDeleteNode = curDeleteNode->next;
}
ListNoded* temp = curDeleteNode->next;
curDeleteNode->next->next->prev = curDeleteNode;
curDeleteNode->next = curDeleteNode->next->next;
delete temp;
size--;
}
else{
ListNoded* curDeleteNode = end;
index = size - index - 1;//指向待删除节点的后一个位置,所以减1
for(int i = 0;i < index;i++){
curDeleteNode = curDeleteNode->prev;
}
ListNoded* temp = curDeleteNode->prev;
curDeleteNode->prev->prev->next = curDeleteNode;
curDeleteNode->prev = curDeleteNode->prev->prev;
delete temp;
size--;
}
}
private:
int size;
ListNoded* head;
ListNoded* end;
};
/**
* 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);
*/
2.移除链表元素
- 记得delete删除节点
203.移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
1.方法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* removeElements(ListNode* head, int val) {
ListNode* deleteNode;
while(head != NULL && head->val == val){
deleteNode = head;
head = head->next;
delete deleteNode;
}
ListNode* current = head;
while(current != NULL && current->next != NULL){
if(current->next->val == val){
deleteNode = current->next;
current->next = current->next->next;
delete deleteNode;
}
else{
current = current->next;
}
}
return head;
}
};
2.方法2:设置一个虚拟头结点在进行移除节点操作:
/**
* 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* removeElements(ListNode* head, int val) {
ListNode* virNode = new ListNode(0,head);
ListNode* currentNode = virNode;
while(currentNode->next != NULL){
if(currentNode->next->val == val){
ListNode* deleteNode = currentNode->next;
currentNode->next = currentNode->next->next;
delete deleteNode;
}
else{
currentNode = currentNode->next;
}
}
head = virNode->next;
delete virNode;
return head;
}
};
3.方法3:**递归 **
/**
* 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* removeElements(ListNode* head, int val) {
if(head == NULL){
return head;
}
else{
head->next = removeElements(head->next,val);
if(head->val == val){
return head->next;
}
else{
return head;
}
}
}
};
3.翻转链表
206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
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* slowNode = NULL;
ListNode* fastNode = head;
ListNode* curNode;
while(fastNode != NULL){
curNode = fastNode;
fastNode = fastNode->next;
curNode->next = slowNode;
slowNode = curNode;
}
return slowNode;
}
};
2.递归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* reverse(ListNode* slowNode,ListNode* fastNode){
if(fastNode == NULL) return slowNode;
ListNode* curNode;
curNode = fastNode;
fastNode = fastNode->next;
curNode->next = slowNode;
slowNode = curNode;
return reverse(slowNode,fastNode);
}
ListNode* reverseList(ListNode* head) {
ListNode* slowNode = NULL;
ListNode* fastNode = head;
return reverse(slowNode,fastNode);
}
};
3.递归2
/**
* 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) {
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode* last = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return last;
}
};
递归方法还需要学习
4.两两交换链表中的节点
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
/**
* 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* swapPairs(ListNode* head) {
ListNode* virNode = new ListNode(0,head);
if(head == nullptr || head->next == nullptr) return head;
ListNode* slowNode = virNode;
ListNode* fastNode = head;
ListNode* curSlow,*curFast;
while(fastNode != NULL && fastNode->next != NULL){//双数fastNode不能等于NULL,单数fastNode->next不能等于NULL
curSlow = slowNode;
curFast = fastNode;
fastNode = fastNode->next->next;
curSlow->next = curFast->next;
curFast->next->next = curFast;
curFast->next = fastNode;
slowNode = curFast;
}
head = virNode->next;
delete virNode;
return head;
}
};
5.删除链表的倒数第N个节点
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
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* removeNthFromEnd(ListNode* head, int n) {
ListNode* virNode = new ListNode(0,head);
ListNode* curNode = virNode;
int index = 0;
if(head == nullptr || head->next == nullptr)return nullptr;
while(curNode->next != nullptr){//统计个数
curNode = curNode->next;
index++;
}
curNode = virNode;
for(int i = 0;i < index - n;i++){//删除
curNode = curNode->next;
}
ListNode* deleteNode = curNode->next;
curNode->next = curNode->next->next;
delete deleteNode;
head = virNode->next;
delete virNode;
return head;
}
};
2.双指针(镜像思想,找到对称过来要删除的点,然后将两个指针同时往后移,使慢指针指向删除点前一位)
/**
* 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* removeNthFromEnd(ListNode* head, int n) {
ListNode* virNode = new ListNode(0,head);
ListNode* slowNode = virNode;
ListNode* fastNode = virNode;
if(head == nullptr || head->next == nullptr)return nullptr;
while(fastNode != nullptr && n){//利用对称思想,找到对称过来要删除的点,然后将两个指针同时往后移,使慢指针指向删除点前一位
fastNode = fastNode->next;
n--;
}
while(fastNode->next != nullptr){
slowNode = slowNode->next;
fastNode = fastNode->next;
}
ListNode* deleteNode = slowNode->next;
slowNode->next = slowNode->next->next;
delete deleteNode;
return virNode->next;
}
};
6.链表相交
- 交点不是数值相等,而是指针相等
面试题 02.07. 链表相交
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交**:**
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构
1.暴力for循环
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
for(ListNode* curA = headA;curA != NULL;curA = curA->next){
for(ListNode* curB = headB;curB != NULL;curB = curB->next){
if(curA == curB)return curA;
}
}
return NULL;
}
};
2.根据代码随想录思路,很巧妙,假设A长于B,长出前面部分去掉,看后面有没有重叠
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int indexA = 0;
int indexB = 0;
while(curA != NULL){
curA = curA->next;
indexA++;
}
while(curB != NULL){
curB = curB->next;
indexB++;
}
curA = headA;
curB = headB;//重新到开头
if(indexA >= indexB) {
int cha = indexA - indexB;
while(cha--){
curA = curA->next;
}
while(curA != NULL || curB != NULL){
if(curA == curB)return curA;
curA = curA->next;
curB = curB->next;
}
}
else{
int cha = indexB - indexA;
while(cha--){
curB = curB->next;
}
while(curA != NULL || curB != NULL){
if(curA == curB)return curA;
curA = curA->next;
curB = curB->next;
}
}
return NULL;
}
};
3.再改进
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int indexA = 0;
int indexB = 0;
while(curA != NULL){
curA = curA->next;
indexA++;
}
while(curB != NULL){
curB = curB->next;
indexB++;
}
if(indexB > indexA){
swap(headA,headB);
swap(indexA,indexB);
}
curA = headA;
curB = headB;//重新到开头
int cha = indexA - indexB;
while(cha--){
curA = curA->next;
}
while(curA != NULL || curB != NULL){
if(curA == curB)return curA;
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
7.环形链表
142. 环形链表 II
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
1.自己思路加提示(看了一句提示,让快指针和慢指针同时进行,但是快指针比慢指针快)
想了很久,这句提示就是解题关键
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* virHead = new ListNode(0,head);
ListNode* slowNode = virHead;
ListNode* fastNode = virHead;
ListNode* findNode = virHead;//查找入口
while(fastNode != NULL && fastNode->next != NULL){
slowNode = slowNode->next;//要快指针比慢指针走得快一些看他们能不能相遇
fastNode = fastNode->next->next;
if(slowNode == fastNode){
while(fastNode != findNode){//一直循环,每次绕到原来跟慢指针一样位置为一圈,查找入口位置
fastNode = fastNode->next;
if(fastNode == slowNode){
findNode = findNode->next;
}
}
return findNode;
}
}
return NULL;
}
};
2.根据代码随想录思路,画图用数学方式解决
具体画图看代码随想录,也可以看leetcode后面详细画图
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* virHead = new ListNode(0,head);
ListNode* slowNode = virHead;
ListNode* fastNode = virHead;
ListNode* findNode = virHead;//查找入口
while(fastNode != NULL && fastNode->next != NULL){
slowNode = slowNode->next;//要快指针比慢指针走得快一些看他们能不能相遇
fastNode = fastNode->next->next;
if(slowNode == fastNode){
while(fastNode != findNode){//根据代码随想录数学公式
fastNode = fastNode->next;
findNode = findNode->next;
}
return findNode;
}
}
return NULL;
}
};