21.合并两个有序链表
方法一:递归
1)递归关系:从两链表的头节点开始比较元素大小,假设l1头节点元素值更小,寻找下一个较小节点 l1.next=mergeTwoLists(l1.next,l2)
2)终止条件:后续节点为空
/**
* 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 mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}else if(l2==null){
return l1;
}else if(l1.val<l2.val){
l1.next=mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next= mergeTwoLists(l1,l2.next);
return l2;
}
}
}
时间复杂度: 因为最多要遍历两链表所有元素,O(m+n)。两链表节点元素值为非递减,当有一链表遍历到空时,时间复杂度会更小。
方法二: 迭代
创建一个哨兵节点prenode,后面直接返回。创建prev节点,作为游走节点。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prenode =new ListNode(-1);
ListNode prev =prenode;
while(l1!=null&&l2!=null){
if(l1.val<l2.val){
prev.next=l1;
l1=l1.next;
}else{
prev.next=l2;
l2=l2.next;
}
prev=prev.next;
}
// if(l1==null)
// prev.next=l2;
// else
// prev.next=l1;
prev.next=l1==null?l2:l1;
return prenode.next;
}
}
时间复杂度:O(m+n)
空间复杂度:O(1)
83.删除排序链表中的重复元素
比较两值是否相同,相同改变next指向,不相同则移动游走的prev节点
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode prev=head;
while(prev!=null&&prev.next!=null){
if(prev.val==(prev.next).val){
prev.next=(prev.next).next;
continue;//跳过后面节点移动
}
prev=prev.next;
}
return head;
}
}
public ListNode deleteDuplicates(ListNode head) {
ListNode current = head;
while (current != null && current.next != null) {
if (current.next.val == current.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
141.环形链表
方法一:HashSet的add方法参数不能重复
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
HashSet<ListNode> checkkk =new HashSet<>();
while(head!=null){
if(!checkkk.add(head)){
return true;
}//不能添加,证明有环
head=head.next;
}
//链表为空或者无环情况
return false;
}
}
方法二:快慢指针,两指针会循环跳跃在链表环上,随着跳跃次数增加,快指针会和慢指针相遇。
public class Solution {
public boolean hasCycle(ListNode head) {
//快慢指针,均指向头节点
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){//注意初始fast.next为空时,快指针跳两步出错
slow=slow.next;
fast=fast.next.next;
if(fast==slow)
return true;
}
//链表为空、只有一个元素或则无环时返回false
return false;
}
}
- 相交链表
方法一:暴力搜索
方法二:HashSet
先存入headA的节点,在存入headB时若报错则有一相交节点。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> checkkk =new HashSet<>();
while(headA!=null){
checkkk.add(headA);
headA=headA.next;
}
while(headB!=null){
if(!checkkk.add(headB))
return headB;
headB=headB.next;
}
return null; //链表为空或不相交情况
}
}
时间复杂度:O(m+n)
空间复杂度:O(m+n )
方法三:双指针
pA遍历完headA再跳转到headB,pB遍历完headB再跳转到headA。假设有交点,交点后快慢指针还能跳跃的次数相同(即之前发生的条约次数相同)。若没有交点,pA或pB第二次指向null时结束。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null)
return null;
ListNode pA=headA;
ListNode pB=headB;
while(pA!=pB){
pA=pA==null?headB:pA.next;
pB=pB==null?headA:pB.next;
}
return pA;//相等时跳出while返回交点或者没有交点 null==null情况跳出循环
}
}
时间复杂度:O(m+n)
空间复杂度:O(1)