当前位置:网站首页>牛客网刷题(一)
牛客网刷题(一)
2022-07-31 15:59:00 【std i hurt o love】
重排链表
解法一:暴力
观察排序后的序列我们可以发现,第一个节点后接最后一个节点,第二个节点后接倒数第二个节点,以此类推。故我们可以每次都找到最后一个节点并把它接在当前节点的后面
我们用一个指针p指向头节点,每次找到链表的最后一个节点并将它接在p所指节点的后面,然后p再指向下一个节点,直到p后面的节点数小于两个时停止
class Solution {
public:
void reorderList(ListNode *head) {
ListNode* p = head;
while(p){
if(p->next && p->next->next){
// 若p后面至少有两个节点
ListNode *pre = p->next, *cur = p->next->next;
while(cur->next){
// 找到最后一个节点cur, pre指向倒数第二个节点
cur = cur->next;
pre = pre->next;
}
// 将cur接在p后面
pre->next = nullptr;
cur->next = p->next;
p->next = cur;
p = p->next->next;
}
else break;
}
}
};
时间复杂度:O(n^2), n为节点数,每次找最后一个的时间复杂度为O(n),故总时间复杂度为O(n^2)
空间复杂度:O(1)
解法二:(线性表)
方法一的缺陷在于链表不能通过下标访问元素,故每次找到最后一个节点都很花费时间,因此我们想到可以用线性表来存储节点指针,然后再重新连接链表即可
实现细节:我们先将每个节点指针放入数组中,然后使用双指针算法每次取出前后对应的两个节点并让前一个节点指向后一个节点,然后用一个指针pre表示上一部分的尾节点指针,若pre不为空,说明前面有一段链表,让pre指向取出的前一个节点即可,再令pre为取出的后一个节点,以此类推,最后记得让pre指向空,当然前提是pre自身不为空
class Solution {
public:
void reorderList(ListNode *head) {
vector<ListNode*> vec; // 创建线性表
ListNode* cur = head;
while(cur){
// 将节点指针放入线性表
vec.emplace_back(cur);
cur = cur->next;
}
ListNode* pre = nullptr;
int i = 0, j = vec.size() - 1;
while(i <= j){
// 使用双指针算法重连链表
vec[i]->next = vec[j];
if(pre) pre->next = vec[i];
pre = vec[j];
i++, j--;
}
if(pre) pre->next = nullptr; // 尾节点指向空指针
}
};
时间复杂度:O(n), n为链表节点数,双指针算法的时间复杂度为O(n)
空间复杂度:O(n), 线性表使用的空间
解法三:(快慢指针、链表翻转、链表合并)
我们可以先通过快慢指针找到链表中点,然后将链表一分为二,再将第二段链表翻转,最后将两段链表交叉合并为一条即可
这种做法涉及到了快慢指针求链表中点,链表的原地翻转和链表的交叉合并,综合性很强,考察了我们指针使用的熟练度和基本功
为了方便理解,我用动图来帮助大家弄懂基本的实现原理,关于链表原地翻转的算法,我将在最后用一张动图来演示
class Solution {
public:
void reorderList(ListNode* head) {
if(!head) return;
ListNode* mid = GetMid(head); // 得到链表的中点
ListNode *p1 = head, *p2 = mid->next;
mid->next = nullptr; // 断开链表
p2 = ReverseList(p2); // 翻转后一段链表
MergeList(p1, p2); // 交叉合并两条链表
}
ListNode* GetMid(ListNode* head) {
// 返回链表中点
ListNode *slow = head, *fast = head;
while(fast->next && fast->next->next){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* ReverseList(ListNode* head) {
// 链表的原地翻转
if(!head) return head;
ListNode *pre = head, *cur = head->next;
while(cur){
head->next = cur->next;
cur->next = pre;
pre = cur;
cur = head->next;
}
return pre;
}
void MergeList(ListNode *l1, ListNode *l2) {
// 交叉合并两条链表
ListNode *tmp1, *tmp2;
while(l1 && l2){
tmp1 = l1->next;
tmp2 = l2->next;
l1->next = l2;
l2->next = tmp1;
l1 = tmp1;
l2 = tmp2;
}
}
};
时间复杂度:O(n),快慢指针,链表合并,翻转链表的时间复杂度都是O(n)的
空间复杂度:O(1)
边栏推荐
- 字符指针赋值[通俗易懂]
- Deployment application life cycle and Pod health check
- org.apache.jasperException(could not initialize class org)
- [TypeScript] In-depth study of TypeScript type operations
- [MySQL] Mysql paradigm and the role of foreign keys
- .NET 20th Anniversary Interview - Zhang Shanyou: How .NET technology empowers and changes the world
- button控件的使用
- MySQL基础篇【单行函数】
- After Grafana is installed, the web opens and reports an error
- "Autumn Recruitment Series" MySQL Interview Core 25 Questions (with answers)
猜你喜欢
随机推荐
SHELL内外置命令
做事软件开发-法的重要性所在以及合理结论的认识
使用 Postman 工具高效管理和测试 SAP ABAP OData 服务的试读版
复制延迟案例(1)-最终一致性
腾讯云部署----DevOps
C语言”三子棋“升级版(模式选择+AI下棋)
Summary of the implementation method of string inversion "recommended collection"
6. 使用 Postman 工具高效管理和测试 SAP ABAP OData 服务
苹果官网样式调整 结账时产品图片“巨大化”
[TypeScript] In-depth study of TypeScript type operations
Kubernetes principle analysis and practical application manual, too complete
【7.28】代码源 - 【Fence Painting】【合适数对(数据加强版)】
2020 WeChat applet decompilation tutorial (can applet decompile source code be used)
Snake Project (Simple)
Linux check redis version (check mongodb version)
小程序:matlab解微分方程「建议收藏」
Why don't you make a confession during the graduation season?
Deployment application life cycle and Pod health check
border控件的使用
利用PHP开发具有注册、登陆、文件上传、发布动态功能的网站