单链表重在考查代码构建和细心问题,在面试考试中经常遇到,下面是我做题时的一些总结:
- 数据结构的构建
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
this.val = x;
this.next = null;
}
}
- 实现整体逆序:
/**
* 链表反转
* <p>
* 206. Reverse Linked List (Easy)
* <p>
* 题目描述
* Input: 1->2->3->4->5->NULL
* Output: 5->4->3->2->1->NULL
*
* 分析与解答:对链表中任何一个结点的访问只能从头指针开始进行遍历。
* 在操作过程中需要特别注意在修改结点的指针域的时候,记录下后继结点的地址,否则会丢失后继结点
*
*/
public class 链表的反转 {
/**
* 就地逆序:
* 在遍历链表时,修改当前结点的指针域的指向,让其指向的的前驱接点。为此,需要一个指针变量保存前驱节点的地址。
* 还需要另一个指针变量保存后继结点的地址,走后要特别注意尾结点特殊处理。
* 时间复杂度O(N)空间复杂度O(1)
*
*/
public ListNode reverseList(ListNode head) {
ListNode pre = head;//前
ListNode now = head.next;//当前
ListNode next = null;//后
head.next = null;
//当前遍历到的结点指向其前驱结点
while (now != null) {
next = now.next;
//当前节点指向前驱节点
now.next = pre;
//
pre = now;
now = next;
}
return pre;
}
/**
* 递归法
* 思路:先逆序除第一个结点以外的子链表,接着把结点添加到逆序子链表的后面
*
*/
public ListNode reverseList2(ListNode head) {
if (head.next == null || head.next.next == null) {
return head;
}
ListNode next = head.next;//除第一个结点外
ListNode newHead = reverseList2(next);//递归
next.next = head;//链到最后
head.next = null;//是最后一个结点
return newHead;
}
/**
* 头插法
* 思路:从链表的第二个结点开始,把遍历到的结点插入到头结点的后面直到遍历结束
* */
public ListNode reverseList3(ListNode head) {
//空链表或只有一个节点的情况
if (head.next == null || head.next.next==null)
return head;
//新建一个空链表
ListNode reverseHead = null;
ListNode next = head.next;//当前结点的后继,和重要
ListNode cur = head;//当前结点
while (cur.next != null) {
cur = next;
//头插
cur.next = reverseHead.next;
reverseHead.next = cur;
//
next = next.next;
}
//
head.next = reverseHead.next;
return head;
}
}
- K个节点间逆序:
/**
* 给定一个单链表的头节点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
* 1)如果链表为空,或者长度为1,或K<2,链表不用进行调整
* 方法1:借助栈的特性,每到k个就依次弹出 时间复杂度O(n) 空间复杂度 O(k)
* 方法2:够k个了就就地逆序 时间复杂度O(n) 空间复杂度 O(1)
* <p>
* 注意的问题:
* 1)函数应该设为具有返回类型的函数
* 2)应该对第一组和最后一组做特殊处理
* 3)应记录下上一组的最后一个结点引用,因为每组间要进行连接
*
*/
public class k个结点间逆序 {
/**
*就地逆序
*/
public ListNode reverse(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode pre = head;//前驱
ListNode cur = head.next;//当前结点
ListNode next = null;//后继
pre.next = null;
//是当前遍历到的结点cur指向其前驱结点
while(cur!=null){
next = cur.next;
//
cur.next = pre;
//后移
pre = cur;
cur = next;
}
return pre;
}
/**
* 对带头结点链表的k翻转
* 方法二:
* 步骤:
* 1)首先设置pre指向头结点,然后让begin指向链表第一个结点,找到从begin开始第k=3个结点end
* 2)因为要使用end.next = null 所以在此之前需要记录下end指向的结点用pNext表示
* 3)end.next=null,使得从begin到end为一个单独子链表,从而可以对这个子链表进行翻转
* 4)对以begin为第一个结点,end为尾节结点的3个结点进行翻转
* 5)由于翻转后begin变为了end,因此执行pre.next = end,把翻转后的子链表连起来
* 6)把链表中剩余的还未完成的翻转的子链表链接到以经完成翻转的子链表的后面(主要针对个数不足的情况)
* 7)让pre指向以完成翻转的最后一个结点
* 8)让begin指向下一个需要翻转的子链表的第一个结点
* 重复啊(1)-(8)
*
*/
public void inverse(ListNode head, int k) {
if (head == null || head.next == null || k<2)
return;
int i = 1;
ListNode pre = head;//头结点
ListNode begin = head.next;//第一个结点
ListNode end = null;//从begin开始的第k个结点
ListNode pNext = null;//暂存end的下一个结点
while(begin !=null){
end = begin;
//1)
for(;i<k;i++){
if(end.next !=null)
end= end.next;
else//剩余结点小于k个
return;
}
pNext = end.next;//2记录后面的
end.next = null;//3断开
pre.next = reverse(begin);//4 5 逆序并把翻转后的子链表连接起来
begin.next = pNext;//6 针对k不足的情况
pre = begin;//7 完成翻转的最后一个结点
begin = pNext;//8指向下一个需要翻转的第一个结点
i = 1;
}
}
}
欢迎访问我的个人博客:http://www.ayjup.cn