【题目】链表相关算法题
文章目录
- 一. 合并两个有序链表
- 题目解析
- 算法原理
- 代码编写
- 二. 相交链表问题
- 题目解析
- 算法原理
- 代码编写
- 三. 环形链表问题
- 1. 判断是否有环
- 2. 计算环的长度
- 3. 找到环的入口点
- 四. 反转链表
- 方法一:边迭代、边逆置
- 方法二:头插
- 五. 判断链表是否回文
- 题目解析
- 算法原理
- 代码编写
- 六、删除排序链表中的重复元素问题
- 1. 删除排序链表中的重复元素I
- 2. 删除排序链表中的重复元素II
- 七. 随机链表的复制
- 题目解析
- 算法原理
- 代码编写
一. 合并两个有序链表
题目解析
算法原理
同时遍历两个链表,取 val 小的那个节点尾插到新链表中
代码编写
/**
* 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* mergeTwoLists(ListNode* list1, ListNode* list2)
{
// 1、预处理(创建哨兵位头节点)
ListNode* head, *tail;
head = tail = new ListNode();
// 2、遍历并合并两个两个链表
while(list1 != nullptr && list2 != nullptr)
{
if(list1->val < list2->val)
{
tail->next = list1;
list1 = list1->next;
}
else
{
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
// 3、处理未遍历完的那个链表
if(list1 != nullptr) tail->next = list1;
else tail->next = list2;
// 4、返回最终结果
ListNode* ans = head->next;
delete head;
return ans;
}
};
二. 相交链表问题
题目解析
算法原理
代码编写
/**
* 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)
{
// 1、判断两个链表是否相交 && 计算两个链表的长度
int lenA = 1, lenB = 1; //链表长度
ListNode *ltA = headA, *ltB = headB; //链表最后一个节点
while(ltA && ltA->next) ltA = ltA->next, ++lenA; //寻找A最后一个节点 和 统计链表长度
while(ltB && ltB->next) ltB = ltB->next, ++lenB; //寻找B最后一个节点 和 统计链表长度
if(ltA != ltB) return nullptr; //判断是否相交
// 2、寻找初始相交节点
int gap = abs(lenA - lenB); //链表长度差值
ListNode *longList = headA, *shortList = headB; //创建长、短链表指针
if(lenA < lenB) std::swap(longList, shortList);//确认长、短链表指针
while(gap--) longList = longList->next; //长链表先走 gap 步
while(longList != shortList) //两个链表同步走
{
longList = longList->next;
shortList = shortList->next;
}
// 3、返回值
return longList;
}
};
/*
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)
*/
三. 环形链表问题
1. 判断是否有环
解题思路
代码编写
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head)
{
//空链接情况的处理
if(!head)
{
return false;
}
ListNode* fast=head;
ListNode* slow=head;
//fast一次走两步,为了防止空指针的访问要两个条件的判断
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
return true;
}
}
return false;
}
2. 计算环的长度
算法原理
3. 找到环的入口点
算法原理
代码编写
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//先记录fast和slow相遇时的节点
//在定义一个节点从头开始
//二者每次走一步
//最后相遇时的节点就是入环节点
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head)
{
ListNode* slow=head;
ListNode* fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
ListNode* meet=slow;
while(1)
{
if(head==meet)
{
return head;
}
head=head->next;
meet=meet->next;
}
}
}
return NULL;
}
四. 反转链表
方法一:边迭代、边逆置
算法原理
代码编写
// 方法一:边迭代、边逆置
class Solution
{
public:
ListNode* reverseList(ListNode* head)
{
if(!head) return nullptr;
// 1、初始化
ListNode* n1 = nullptr;
ListNode* n2 = head;
ListNode* n3 = head->next;
// 2、逆置
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3) n3 = n3->next;
}
// 3、返回值
return n1;
}
};
方法二:头插
算法原理
代码编写
class Solution
{
public:
ListNode* reverseList(ListNode* head)
{
// 1、初始化
ListNode* cur = head, *newHead = nullptr;
// 2、头插
while(cur)
{
ListNode* next = cur->next;
cur->next = newHead;
newHead = cur;
cur = next;
}
// 3、返回值
return newHead;
}
};
五. 判断链表是否回文
题目解析
算法原理
代码编写
/**
* 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
{
private:
ListNode* ReverseList(ListNode* head)
{
if(!head) return nullptr;
ListNode* n1 = nullptr, *n2 = head, *n3 = head->next;
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3) n3 = n3->next;
}
return n1;
}
public:
bool isPalindrome(ListNode* head)
{
// 1、找到中间节点
ListNode* fast, *slow;
fast = slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
// 2、逆置后半段链表
ListNode* half = ReverseList(slow);
// 3、比较前后部分链表
while(head && half)
{
if(head->val != half->val) return false;
head = head->next;
half = half->next;
}
// 4、返回值
return true;
}
};
六、删除排序链表中的重复元素问题
1. 删除排序链表中的重复元素I
算法原理
代码编写
/**
* 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* deleteDuplicates(ListNode* head)
{
// 0、特殊情况处理(保证链表至少有两个节点才做处理)
if(head == nullptr || head->next == nullptr) return head;
// 1、初始化
ListNode* newHead, *tail;
newHead = tail = head;//新链表第一个节点为 head
head = head->next; //旧链表从第二个节点开始遍历
tail->next = nullptr; //设置新链表中第一个节点的 next 为空
// 2、删除重复元素
while(head)
{
ListNode* cur = head;
head = head->next;
if(cur->val != tail->val)
{
tail->next = cur;
tail = cur;
tail->next = nullptr;
}
}
// 3、返回值
return newHead;
}
};
2. 删除排序链表中的重复元素II
算法原理
代码编写
/**
* 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* deleteDuplicates(ListNode* head)
{
// 0、特殊情况处理(保证链表至少有两个节点才做处理)
if(head == nullptr || head->next == nullptr) return head;
// 1、初始化
ListNode* newHead, *tail;
newHead = tail = head;//新链表第一个节点为 head
head = head->next; //旧链表从第二个节点开始遍历
tail->next = nullptr; //设置新链表中第一个节点的 next 为空
// 2、删除重复元素
while(head)
{
ListNode* cur = head;
head = head->next;
if(cur->val != tail->val)
{
tail->next = cur;
tail = cur;
tail->next = nullptr;
}
}
// 3、返回值
return newHead;
}
};
七. 随机链表的复制
题目解析
算法原理
代码编写
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution
{
public:
Node* copyRandomList(Node* head)
{
// 1、拷贝节点到原节点的后面
Node* cur = head;
while(cur)
{
// 初始化
Node* copy = new Node(cur->val);
Node* next = cur->next;
// 链接
cur->next = copy;
copy->next = next;
// 更新cur
cur = next;
}
// 2、处理拷贝节点的 random 指针
cur = head;
while(cur)
{
Node* copy = cur->next;
if(cur->random)
copy->random = cur->random->next;
// 继续迭代原链表的下一个节点
cur = cur->next->next;
}
// 3、拆解链表
Node* newHead, *tail; //创造一个哨兵位头节点
newHead = tail = new Node(0);
cur = head;
while(cur)
{
// 初始化
Node* copy = cur->next;
Node* next = cur->next->next;
// 尾插拷贝节点到新链表中
tail->next = copy;
tail = copy;
tail->next = nullptr;
// 处理原链表节点
cur->next = next;
cur = next;
}
// 4、返回值
return newHead->next;
}
};