链表反转
给定单链表的头节点 head
,请反转链表,并返回反转后的链表的头节点。
解题思路
cur遍历指向当前节点,pre指向cur前一个节点,链表反转就是将cur指向自己的前一个节点 pre,然后 pre=cur cur=tmp 向后移动 终止条件是cur当前节点不为空
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null;//cur遍历指向当前节点,pre指向cur前一个节点,链表反转就是将cur指向自己的前一个节点 pre,然后 pre=cur cur=tmp 向后移动 终止条件是cur当前节点不为空
while(cur != null) {
ListNode nxt = cur.next; // 存cur下一个节点节点
cur.next = pre; // cur的下一个节点改为指向前面的节点
pre = cur; // pre 指向 cur 后移过程
cur = nxt; // cur 访问下一节点 后移过程
}
return pre;
}
}
相交链表
给定两个单链表的头节点 headA
和 headB
,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
解题思路:
1、哈希表判断法
遍历其中一个链表,将其放在hashset集中,然后再判断另一个链表的各个节点是否在hashset集中,如果另一个链表的某个节点出现在hashset集中,那么此节点之后的节点必然出现在hashset集中,当前节点即为两链表的相交节点,如果两链表不相交,hashset集里面可以将全部节点放下,此时返回null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> listNodes = new HashSet<>();
ListNode temp = headA;
while (temp!=null){
listNodes.add(temp);
temp=temp.next;
}
temp=headB;
while (temp!=null){
if (listNodes.contains(temp))
return temp;
temp=temp.next;
}
return null;
}
2、双指针法(时间复杂度更低)
任意一个链表为空则必不相交,判断是否为空;相交的链表如果遍历的话走过的长度一致,故一定在相交节点处第一次相遇
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
ListNode A =headA;
ListNode B=headB;
while(A!=B){//A==B有两种情况,AB链表相交 A=B=null
if(A==null) A=headB;
else A=A.next;
if(B==null) B=headA;
else B=B.next;
}
return A;
}
回文链表
解题代码:
/**
* 复制链表值到数组列表中。
* 使用双指针法判断是否为回文。
* @param head
* @return
*/
public boolean isPalindrome(ListNode head) {
ArrayList<Integer> values = new ArrayList<>();
ListNode temp =head;
while (temp!=null){
values.add(temp.val);
temp=temp.next;
}
int front =0;
int back =values.size()-1;
while (front<back){
if (values.get(front)!=values.get(back)){
return false;
}
front++;
back--;
}
return true;
}
链表成环的判断
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
解题思路
用快慢指针,如果成环一定会相遇
public boolean hasCycle(ListNode head) {
if(head==null||head.next==null){
return false;
}
ListNode fast=new ListNode(-1);
ListNode slow=new ListNode(-1);
fast=head.next;
slow=head;
while(fast!=slow){
if(fast==null||fast.next==null){
return false;
}
fast=fast.next.next;
slow=slow.next;
}
return true;
}
链表成环位置判断
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next
指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1
,则在该链表中没有环。注意,pos
仅仅是用于标识环的情况,并不会作为参数传递到函数中。
解题思路:
1、哈希表判断
public ListNode detectCycle(ListNode head) {
ListNode pos = head;
Set<ListNode> visited = new HashSet<ListNode>();
while (pos != null) {
if (visited.contains(pos)) {//如果哈希表里第一次有,就成环
return pos;
} else {//如果哈希表里没有就添加
visited.add(pos);
}
pos = pos.next;
}
return null;
}
2、快慢指针,判断是否成环,如果成环了,让快指针指向head,之后两指针保持相同速度遍历,第一次相遇的点就是环连接点。
(可简记一个推导结论:从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。)
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
// 有环
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
// 无环
return null;
}
有序链表合并
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
解题代码:
/**
思路:如果l1和l2都不为空的话,首先定义一个新的节点,值设为-1 便于之后返回新的链表
合并链表时如果一个链表先为空,则将另一个链表的剩余部分直接加到新链表后面
*/
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode newhead =new ListNode(-1);
ListNode listNode=newhead;
while (list1!=null&&list2!=null){
if (list1.val<=list2.val){
listNode.next=list1;
list1=list1.next;
}else {
listNode.next=list2;
list2=list2.next;
}
listNode=listNode.next;
}
listNode.next=list1==null?list2:list1;
return newhead.next;
}