文章目录
leetcode206. 反转链表
题目描述
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解题思路
思路一:使用双指针
- 定义两个指针: pre 和 cur ;pre 在后 cur 在前。(pre .next = cur)
- 每次让 cur的 next 指向 pre ,使用临时tmp 保存cur.next不然会找不到后边,这样就实现一次局部反转
- 局部反转完成之后,pre 和 cur 同时往前移动一个位置
- 循环上述过程,直至 cur 到达链表尾部
实现代码
public ListNode reverseList(ListNode head) {
if (head == null) {
return head;
}
ListNode cur = head;
ListNode pre = null;
while (cur != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
思路二:递归实现
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
思路三:
public ListNode reverseList(ListNode head) {
ListNode newHead = null;// 指向新链表头节点的指针
while (head != null) {
ListNode temp = head.next;//备份head的next 不然后边会断掉
head.next = newHead;// 更新head.next
newHead = head;// 移动 newHead
head = temp;// 遍历链表
}
return newHead;// 返回新链表头节点
}
leetcode92.反转链表
题目描述
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
解题思路
步骤一:找到逆置的头节点和尾结点,并记录前驱和后继。
步骤二:逆置n - m
步骤三:连接起来
具体见代码解释
代码实现
public ListNode reverseBetween(ListNode head, int m, int n) {
// 输入: 1->2->3->4->5->NULL, m = 2, n = 4
// 输出: 1->4->3->2->5->NULL
//如果头节点为空直接返回
if (head == null) {
return null;
}
// 当 m == n 则不需要翻转直接返回头节点
if (m - n == 0) {
return head;
}
// 计算需要反转的长度 2-4 总共3个元素长度
int len = n - m + 1;
ListNode second = head;//该节点记录n位置节点(4)
ListNode first = head;//该节点记录m位置节点(2)
ListNode first_pre = null;//该节点用于保存m位置节点的前驱
while (m-- > 1) {
first_pre = first;
first = first.next;
}// 循环走到m位置节点,并且找到前驱节点 first->2
while (n-- > 1) {
second = second.next;
}// 循环走到n位置节点 second->4
ListNode second_next = second.next;// 保存 n 位置的后继节点 5
// 3 3 2 1
ListNode tt = null;//用来保存first的前驱
// 循环翻转
while (len-- > 0) {
tt = first;
ListNode temp = first.next;//保存first的后继 也就是3 节点
first.next = second_next;//2->5
second_next = first;//second_next 往前走,指向2
first = temp;// first指向 3
}
// 循环结束4->3->2->5->NULL
//注意first结束后指向的temp 是 节点5位置,也就是需要tt 节点保存没有更新之前,tt是指向4的
// 如果m == 1 则first_pre == null 则直接返回tt即可
if (first_pre == null) {
return tt;
}
// 不为空则将 之前保存的前驱节点first_pre指向tt,返回头节点
first_pre.next = tt;
return head;
}
示意图就先不画了。如有需要后边补充。
还有个思路就是从4开始连,这个里边就需要递归比较麻烦。
leetcode876. 链表的中间结点
题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
解题思路
思路一:单指针两次遍历
第一遍统计长度,第二遍再去找N/2.
这种方法我就不在说了。
思路二:快慢指针
定义两个指针,一个快指针fast,一个慢指针slow。同时走,fast一次走两步,slow一次走一步,那么当fast为空或者fast.next为空时,slow就是寻找的中间节点。
代码实现
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
leetcode160 相交链表
题目描述
编写一个程序,找到两个单链表相交的起始节点。(传送门)
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
解题思路
方法一:Set集合
- 把第一个链表的节点全部存放到集合set中
- 遍历第二个链表的每一个节点,判断在集合set中是否存在,如果存在就直接返回这个存在的结点。如果遍历完了,在集合set中还没找到,说明他们没有相交,直接返回null即可
方法二:
- 步骤一:计算headA和headB链表的长度,和较长链表多出的长度
- 步骤二:较长链表的指针先走,差值,这样两者就对齐了
- 步骤三:一起走,当两节点相同时,就找到了,否则返回null
方法三:双指针
- 指针 pA 指向 A 链表,指针 pB 指向 B 链表,依次往后遍历
- 如果 pA 到了末尾,则 pA = headB 继续遍历
- 如果 pB 到了末尾,则 pB = headA 继续遍历
- 相遇时就找到了(因为这样下来两指针走的长度是一样的)
代码实现
方法一:Set
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//创建集合set
Set<ListNode> set = new HashSet<>();
//先把链表A的结点全部存放到集合set中
while (headA != null) {
set.add(headA);
headA = headA.next;
}
//然后访问链表B的结点,判断集合中是否包含链表B的结点,如果包含就直接返回
while (headB != null) {
if (set.contains(headB))
return headB;
headB = headB.next;
}
//如果集合set不包含链表B的任何一个结点,说明他们没有交点,直接返回null
return null;
}
方法二:
public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
// 步骤一
ListNode temp = headA;
int lenA = 0;
while (temp != null) {
temp = temp.next;
lenA++;
}
temp = headB;
int lenB = 0;
while (temp != null) {
temp = temp.next;
lenB++;
}
// 步骤二:
if (lenA > lenB) {
while (lenA - lenB > 0) {
headA = headA.next;
}
}else {
while (lenB - lenA > 0) {
headB = headB.next;
}
}
//步骤三
while (headA != null) {
if (headA == headB) {
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
方法三:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pa = headA;
ListNode pb = headB;
while (pa != pb) {
if (pa == null) {
pa = headB;
}else {
pa = pa.next;
}
if (pb == null) {
pb = headA;
}else {
pb = pb.next;
}
}
return pa;
}
leetcode141.环形链表
题目描述
给定一个链表,判断链表中是否有环。(传送门)
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
解题思路
方法一:Set
方法二:快慢指针
实现代码
方法一:
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return true;
}
set.add(head);
head = head.next;
}
return false;
}
方法二:
public boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode slow = head;
if (head.next == null) {
return false;
}
ListNode fast = head.next;
while (fast != null && fast.next !=null) {
if (slow == fast) {
return true;
}
slow = slow.next;
fast = fast.next.next;
}
return false;
}
leetcode142.环形链表II
题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。(传送门)
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
解题思路
方法一:set集合
方法二:快慢指针
同上一个题一样,相遇的时候就能判断他是有环的。
代码实现
方法一:Set
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return true;
}
set.add(head);
head = head.next;
}
return false;
}
方法二:快慢指针
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
ListNode meet = null;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (fast == slow) {
meet = fast;
break;
}
}
while (meet != null) {
if (head == meet) {
return head;
}
meet = meet.next;
head = head.next;
}
return null;
}
leetcode203. 移除链表元素
题目描述
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
解题思路
方法:哨兵节点(在这里我先不使用傀儡节点,使用傀儡节点问题也就简单了)
首先这个要删除的节点位置有几种情况:
prev代表前驱节点:cur = head;
cur代表当前节点:cur = head.next;
情况一:要删除的节点在链表中间,不在开头
在这里我们只需要循环遍历在cur 不为空的时候。如果遇到要删除的节点,让prev指向cur的下一个节点即可,然后更新cur这样就完成删除。
pre.next = cur.next;
cur = cur.next;
情况二:当要删除的一个或多个节点位于链表的头部时
我们这时需要用循环处理一下头节点,一直更新head使得头节点不是你要删除的节点,这样问题就转换为情况一了。
代码实现
第一种:不使用傀儡节点
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
while (head.val == val) {
head = head.next;
if (head == null) {
return head;
}
}
ListNode cur = head.next;
ListNode pre = head;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
cur = cur.next;
}else {
cur = cur.next;
pre = pre.next;
}
}
return head;
}
第二种:使用傀儡节点
public ListNode removeElements(ListNode head, int val) {
ListNode kind = new ListNode(0);
kind.next = head;
ListNode cur = head;
ListNode prev = kind;
while (cur != null) {
if (cur.val == val) {
prev.next = cur.next;
cur = cur.next;
}else {
prev = prev.next;
cur = cur.next;
}
}
return kind.next;
}