【牛客网面试必刷TOP101】链表篇(一)
- 前言
- 刷题网站
- 刷题!
- BM1 反转链表
- 思路一:用栈解决
- 思路二:双链表求解
- 思路三:递归解决
- 总结
- BM2 链表内指定区间反转
- 思路一:头插法迭代
- 思路二:递归
- BM3 链表中的节点每k个一组翻转
- 思路:递归
- 总结
前言
链表是数据结构中重要的一个章节,他的重要性也不言而喻
面试都会遇到这类的题目,以下是链表的常考的题目。
刷题网站
牛客网:https://www.nowcoder.com/activity/oj
刷题!
BM1 反转链表
https://www.nowcoder.com/share/jump/6243324481684200142743
思路一:用栈解决
链表的反转是老生常谈的一个问题了,同时也是面试中常考的一道题。最简单的一种方式就是使用栈,因为栈是先进后出的。实现原理就是把链表节点一个个入栈,当全部入栈完之后再一个个出栈,出栈的时候在把出栈的结点串成一个新的链表。原理如下:
public class Solution {
public ListNode ReverseList(ListNode head) {
Stack<ListNode> stack= new Stack<>();
//把链表节点全部摘掉放到栈中
while (head != null) {
stack.push(head);
head = head.next;
}
if (stack.isEmpty())
return null;
ListNode node = stack.pop();
ListNode dummy = node;
//栈中的结点全部出栈,然后重新连成一个新的链表
while (!stack.isEmpty()) {
ListNode tempNode = stack.pop();
node.next = tempNode;
node = node.next;
}
//最后一个结点就是反转前的头结点,一定要让他的next
//等于空,否则会构成环
node.next = null;
return dummy;
}
}
思路二:双链表求解
双链表求解是把原链表的结点一个个摘掉,每次摘掉的链表都让他成为新的链表的头结点,然后更新新链表。
public ListNode ReverseList(ListNode head) {
ListNode newhead=null;
while(head!=null){
ListNode tmp=head.next;
head.next=newhead;
newhead=head;
head=tmp;
}
return newhead;
}
思路三:递归解决
public ListNode ReverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode next=head.next;
ListNode reverse=ReverseList(next);
next.next=head;
head.next=null;
return reverse;
}
总结
BM2 链表内指定区间反转
https://www.nowcoder.com/share/jump/6243324481684200129562
思路一:头插法迭代
思路大体可以分为三个步骤:
1️⃣ 先创建一个前序头指针,为了防止第一个元素也要逆序。
2️⃣用双指针prev和cur找到m的位置,prev就指向m的前一个位置,方便把cur的下一个元素移动到cur的前面
3️⃣对于从m到n这些个位置的节点,依次断掉指向后续的指针,反转指针方向。
public ListNode reverseBetween (ListNode head, int m, int n) {
ListNode res=new ListNode(-1);
res.next=head;
ListNode pre=res;
ListNode cur=head;
for(int i=1;i<m;i++){
pre=cur;
cur=cur.next;
}
for(int i=m;i<n;i++){
ListNode tmp=cur.next;
cur.next=tmp.next;
tmp.next=pre.next;
pre.next=tmp;
}
return res.next;
}
思路二:递归
我们来看看另一种分析:如果m == 1,就相当于反转链表的前n个元素;如果 m != 1,我们把 head 的索引视为 1,那么我们是想从第 m 个元素开始反转,如果把 head->next 的索引视为1,那相对于 head->next的反转的区间应该是从第 m−1个元素开始的,以此类推,反转区间的起点往后就是一个子问题。
ListNode temp = null;
public ListNode reverse(ListNode head, int n){
//只颠倒第一个节点,后续不管
if(n == 1){
temp = head.next;
return head;
}
//进入子问题
ListNode node = reverse(head.next, n - 1);
//反转
head.next.next = head;
//每个子问题反转后的尾拼接第n个位置后的节点
head.next = temp;
return node;
}
public ListNode reverseBetween (ListNode head, int m, int n) {
//从第一个节点开始
if(m == 1)
return reverse(head, n);
//缩减子问题
ListNode node = reverseBetween(head.next, m - 1, n - 1);
//拼接已翻转
head.next = node;
return head;
}
BM3 链表中的节点每k个一组翻转
https://www.nowcoder.com/share/jump/6243324481684200097423
思路:递归
把每一段分开逆序,每一段的逆序前面已经做过,那么接下来就是把每段逆序后的结果链接到一起。
要注意的是: 例如第一段逆序完后head为尾部,那么链接下一段就是head->next = 子问题。
当进行到最后一个分组,即不足k次遍历到链表尾(0次也算),就将剩余的部分直接返回。
public ListNode reverseKGroup (ListNode head, int k) {
ListNode tail=head;
for(int i=0;i<k;i++){
if(tail==null){
return head;
}
tail=tail.next;
}
ListNode pre=null;
ListNode cur=head;
while(cur!=tail){
//翻转
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
head.next=reverseKGroup(tail,k);
return pre;
}
总结
链表的题目一定要画图,思路可以从头插法,递归角度思考!
💓 感谢阅读!