⚓️作者简介:即将大四的北京某能源高校非科班学生。
座右铭:“九层之台,起于垒土” 。所以学习技术须脚踏实地。
这里推荐一款刷题、模拟面试神器,可助你斩获大厂offer:点我免费刷题、模拟面试
牛客网是一个集笔面试系统、题库、课程教育、社群交流、招聘内推于一体的招聘类网站,更是一个专注于程序员的学习和成长的平台。
在某次浏览博客的过程中,我偶然点进一个链接,注册了牛客账号。一来到牛客首页,我就被其丰富的功能与良好的社区环境所吸引:
进入题库,更是有最新校招试题与专项练习:
在线编程更是有在线调试功能,可大大提高debug效率:
问答题下还有超多牛客用户交流:
总之,牛客是一个专注于程序员的学习和成长的平台,所以我极力推荐大家注册牛客,坚持刷题,充实自己,大厂offer便指日可待。
链表,别名链式存储结构或单链表,用于存储逻辑关系为 “一对一” 的数据。与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。
下面来刷几个链表题练练手
解题思路
本题为简单题,思路比较清晰:
方法一:
第一次遍历链表,得到链表长度 len,第二次循环,往后走 len - k 步,即可得到倒数第 k 个结点。
C++解题代码:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pHead, int k) {
ListNode *t = pHead;
int len = 0;
while(t){t = t->next; ++len;}
t = pHead;
k = len - k;
while(k && t){t = t->next;--k;}
return t;
}
};
方法二:
维护一个栈或队列,没经过一个元素就压栈或进队,到达尾节点后再将后 k 个元素出栈或将 len - k 个元素出栈,即可得到倒数第 k 个元素。
C++解题代码:
以栈为例:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pHead, int k) {
stack<ListNode*> s;
ListNode* t = pHead;
while(t) {
s.push(t);
t = t->next;
}
while(k - 1 && !s.empty()) {
s.pop();
--k;
}
return s.empty()?nullptr:s.top();
}
};
方法三:
使用双指针,前面指针先走 k 步,然后后面指针从头开始走,前面指针接着走,前面指针到达尾节点后,后面指针即指向了倒数第 k 个结点。
当前面指针走了不到 k 步就到达尾节点后,说明链表长度小于 k,此时无解。
class Solution {
public:
ListNode* FindKthToTail(ListNode* pHead, int k) {
ListNode *first = pHead, *last = pHead;
while(k && first){
first = first->next;
--k;
}
if(!first && k != 0) return nullptr;
while(first){
first = first->next;
last = last->next;
}
return last;
}
};
方法一需要扫描链表 2length - k 次,时间复杂度为 O(n),空间复杂度为 O(1)。方法二的时间复杂度为 O(n),空间复杂度为 O(n)。方法三需要扫描链表 length + k 次,时间复杂度为 O(n),空间复杂度为 O(1)。综上,方法一和方法三的性能较好。
解题思路
利用上题的方法三,维护两个指针,不过这次后面指针是指向要删除结点的前一个结点。此外还要添加一个头结点,方便操作。
C++解题代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *prehead = new ListNode(0);
prehead->next = head;
ListNode *last = prehead, *first = head;
while(n){ first = first->next; --n; }
while(first){ first = first->next; last = last->next; }
ListNode *rednode = last->next;
last->next = rednode->next;
delete(rednode);
head = prehead->next;
delete(prehead);
return head;
}
};
本题也属于简单题,理解上一题的方法三这题就不难做出来,查看更多题解,快来这里吧。
题目:
示例:
解题思路:
维护两个指针。
前面指针 first 指向的结点与后面指针 last 指向的结点具有相同的值,则删除当前结点,first 继续向前走,不同则停下;last 指向 first 指向的结点,first 向前走一步。
继续上面步骤。
C++解题代码
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode *first = head, *last;
while(first){
last = first;
first = first->next;
while(first && first->val == last->val){
first = first->next;
free(last->next);
last->next = first;
}
}
return head;
}
};
解题思路:
ListNode res = new ListNode(0);
//在链表前加一个表头
res.next = head;
C++解题代码
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head) return head;
ListNode *prehead = new ListNode(0);
prehead->next = head;
ListNode *cur = prehead;
while(cur->next && cur->next->next){
if(cur->next->val == cur->next->next->val){
int val = cur->next->val;
while(cur->next && cur->next->val == val)
cur->next = cur->next->next;
}
else
cur = cur->next;
}
return prehead->next;
}
};
我希望通过写博客来结束浑浑噩噩的生活,我更希望通过刷题结束人云亦云的思考。刷题不仅仅是刷题,还是我们与自己内心深处的对话。希望我们可以一起在牛客刷题交流,一起收割大厂offer!