力扣hot100刷题(链表)

1.160. 相交链表

理解:

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pA =headA; ListNode pB =headB;
        while (pA!=pB) {
            pA=pA==null?headB:pA.next;
            pB=pB==null?headA:pB.next;
        }
        return pA;// 返回交点,如果没有交点,这里将返回null
        //同时到达尾端都等于null       
    }
}

2.206. 反转链表

方法1:双指针

循环停止:

cur=null,说明所有元素已经遍历完成

代码:

/**
 * 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; ListNode pre=null;
        while (cur!=null) {
            ListNode temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
}

方法2:递归

反向连接(4连5,变成5连4,(head为4))

断开之前4连接5(先反连接了,5连4)

cur一直是5

代码:

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head==null||head.next==null) {
            return head;
        }
        ListNode NewHead=reverseList(head.next);
        head.next.next=head;//反向连接
        head.next=null;//断开正向的连接
        return NewHead; //返回的是最后一次递归的值,最后一个节点

    }
}

3.回文链表

错误方法:直接翻转链表

翻转链表,回文链表翻转前后一致

将翻转前后的链表逐一比较,不一致则返回false

(迭代双指针的翻转(o(n),o(1)))

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head==null||head.next==null) {
            return true;
        }
        ListNode ReverLink=reverse(head);
        //翻转链表,回文链表翻转前后一致
        ListNode p1=head;ListNode p2=ReverLink;
        while (p1!=null) {
            if (p1.val!=p2.val) {
                return false;
            }
            p1=p1.next;
            p2=p2.next;
        }
        return true;
    }

    public ListNode reverse(ListNode head){
        ListNode cur=head;ListNode pre=null;
        while (cur!=null) {
            ListNode temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }

}

正确方法:只翻转后半部分(快慢指针+翻转)

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head==null||head.next==null) {
            return true;
        }

        //快慢指针找中点,翻转后半部分
        ListNode slow=head;ListNode fast=head;
        while (fast != null && fast.next != null) {
            slow=slow.next;
            fast=fast.next.next;
        }
        ListNode ReverLink=reverse(slow);
        //翻转后半部分链表,回文链表翻转前后一致
        ListNode p1=head;ListNode p2=ReverLink;
        while (p1!=null&p2!=null) {
            if (p1.val!=p2.val) {
                return false;
            }
            p1=p1.next;
            p2=p2.next;
        }
        return true;
    }

    public ListNode reverse(ListNode head){
        ListNode cur=head;ListNode pre=null;
        while (cur!=null) {
            ListNode temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
}

4.环形链表

快慢指针检测环--不存在环,快指针一定到null;存在环,快慢指针一定能相遇

代码:

正确的:

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode fast=head.next; ListNode slow=head;
        while (fast!=slow) {
            if (fast == null || fast.next == null) {
                return false;
            }
            fast=fast.next.next;
            slow=slow.next;
            
        }
        return true;
    }
}

5.环形链表 II

这几个环形链表都是尾部连接环,题目已经说明

索引从0开始

思想:两次相遇

第一次相遇,慢指针刚好走了n圈

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head==null||head.next==null)  return null;
        ListNode fast=head;
        ListNode slow=head;
        while (true) {
            if (fast==null ||fast.next==null) return null;
            slow=slow.next;
            fast=fast.next.next;
            if (fast==slow) break;
        }
        //构建第二次相遇
        fast=head;
        while (fast!=slow) {
            slow=slow.next;
            fast=fast.next;
        }
        return fast;
    }
}

6.合并两个排序列表

方法1:迭代

将链表1的1->4的连接改为到第二条链表的连接

class Solution {
    public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
        //创建哨兵节点
        ListNode prehead=new ListNode(-1);
        
        ListNode prev = prehead;//记录当前加到哪里了
        while (head1!=null & head2!=null) {
            if (head1.val<head2.val) {
                prev.next=head1;
                head1=head1.next;
            }else{
                prev.next=head2;
                head2=head2.next;
            }
            prev=prev.next;
        }
        //如果某条链已经全部安排完
        //将另外那条链的剩余部分全加进去 
        prev.next=head1==null?head2:head1;

        return prehead.next;
    }
}

方法2:递归

递归头,结束条件:某条链为空,都比较完了

递归体,循环:从后往前,从一条链仅剩最后一个元素开始,另外那条链都是好的

class Solution {
    public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
        if (head1 == null)
            return head2;
        if (head2 == null)
            return head1;
        if (head1.val < head2.val) {
            head1.next = mergeTwoLists(head1.next, head2);
            return head1;
        } else {
            head2.next = mergeTwoLists(head2.next, head1);
            return head2;
        }

    }
}

7.两数相加

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(-1);
        ListNode cur = pre;
        int carry = 0;
        while (l1 != null || l2 != null) {
            // 用0补齐链
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;

            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);
            cur = cur.next;

            if (l1 != null)
                l1 = l1.next;
            if (l2 != null)
                l2 = l2.next;
        }
        if (carry == 1)
            cur.next = new ListNode(carry);
        return pre.next;

    }
}

8.删除链表的倒数第 N 个结点

方法一:暴力解法

先查找到倒数第n个节点---两次遍历(一次找全长L,第二次找该节点)

再删掉

方法二:双指针

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode start = pre, end = pre;
        while(n != 0) {
            start = start.next;
            n--;
        }
        while(start.next != null) {
            start = start.next;
            end = end.next;
        }
        end.next = end.next.next;
        return pre.next;

    }
}

9.两两交换链表中的节点

方法一:迭代

class Solution {
    public ListNode swapPairs(ListNode head) {
        // 边界判断  至少要有两个元素才可以
        if(head== null) return head;
        if(head.next == null) return head;

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;

        while(pre.next != null && pre.next.next != null) {
            ListNode temp=pre.next.next;
            ListNode cur=pre.next;
            pre.next=temp;
            cur.next=temp.next;
            pre.next.next=cur;
            pre=cur;别忘了把 pre 的位置变一下
        }
        return dummy.next;        

    }
}

方法二:递归

递归头:链表中没有节点,或者链表中只有一个节点,此时无法进行交换。

递归体:(1)两两交换链表中的节点之后,原始链表的头节点变成新的链表的第二个节点,原始链表的第二个节点变成新的链表的头节点。

(2)在对链表中的其余节点递归地两两交换之后,更新节点之间的指针关系

class Solution {
    public ListNode swapPairs(ListNode head) {
        // 递归头,终止条件
        if (head==null || head.next==null) {
            return head ;
        }      

        ListNode newHead=head.next; //head的第二个节点是newHead的第一个
        head.next=swapPairs(newHead.next); 
        //重新排列好 两个节点后面的节点对(两个两个的排)
        newHead.next=head;
        //前两个对换完成
        return newHead;
    }
}

10.K 个一组翻转链表

k个一组反转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0, head);
        ListNode pre = dummy;
        // ListNode cur = head;
        while (head != null) {
            ListNode tail = pre;
            for (int i = 0; i < k; i++) {
                tail = tail.next;
                // 查看剩余部分长度是否大于等于 k
                if (tail == null) {
                    return dummy.next;
                }
            }
            ListNode tNext = tail.next;
            // 反转链表
            ListNode[] reverseNode = reverseListNode(head, tail);
            head = reverseNode[0];
            tail = reverseNode[1];

            // 子链表重新接回原链表
            pre.next = head;
            tail.next = tNext;
            pre = tail;
            head = tNext;
        }
        return dummy.next;
    }

    private ListNode[] reverseListNode(ListNode head, ListNode tail) {
        // 不直接操作head,tail用于返回,相当于head和tail的位置不变,值发生了变化
        ListNode tNext = tail.next; // 用于交换后head指向
        ListNode cur = head;
        while (tNext != tail) {
            ListNode cNext = cur.next; // tail的临时变量
            cur.next = tNext; // cur下个节点指向交换成tail的指向
            tNext = cur; // tail下个节点交换成cur,
            cur = cNext; // 此时cNext由于cur.next的改变发生变化,指向tail.next,即cur=tail.next,
        }
        return new ListNode[] { tail, head };
    }
}

11.随机链表的复制

class Solution {
    public Node copyRandomList(Node head) {
        if(head==null)  return null;

        Node p=head;
        while (p!=null) {
            Node newNode=new Node(p.val);
            newNode.next=p.next;
            p.next=newNode;
            p=p.next.next;
        }

        p=head;
        while (p!=null) {
            if(p.random!=null) p.next.random=p.random.next;
            p=p.next.next;
        }

        //拆分成两条链表
        Node ans=new Node(-1);
        Node pre=ans;
        p=head;
        while (p!=null) {
            pre.next=p.next;
            pre=pre.next;
            p.next=pre.next;
            p=p.next;
        }
        return ans.next;
    }
}

12.排序链表

归并排序

public ListNode sortList(ListNode head) {
        if(head==null || head.next==null) return head;
        //fast=head.next  确保slow
        ListNode fast=head.next; ListNode slow=head;
        while (fast!=null && fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
        }
        ListNode mid=slow.next;
        slow.next=null;
        ListNode left=sortList(head);
        ListNode right=sortList(mid);
        ListNode ans=merge(left, right);
        return ans;
    }

合并两个链表

static ListNode merge(ListNode l1,ListNode l2){
        ListNode ans=new ListNode(1);
        ListNode pre=ans;
        while (l1!=null && l2!=null) {
            if (l1.val<l2.val) {
                pre.next=l1;
                l1=l1.next;
            }else{
                pre.next=l2;
                l2=l2.next;
            }
            pre=pre.next;
        }
        pre.next=l1==null?l2:l1;
        return ans.next;
    }

13.合并 K 个升序链表

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        return merge(lists,0, lists.length-1);
    }

    //合并
    static ListNode merge(ListNode[] lists,int l,int r){
        if(l==r) return lists[l];
        if(l>r) return null;
        int mid=(l+r)/2;
        return mergeTwo(merge(lists, l, mid), merge(lists, mid+1, r));
    }

    static ListNode mergeTwo(ListNode l1,ListNode l2){
        ListNode ans=new ListNode(-1);
        ListNode pre=ans;
        while(l1!=null&&l2!=null){
            if(l1.val<l2.val){
                pre.next=l1;
                l1=l1.next;
            }else{
                pre.next=l2;
                l2=l2.next;
            }
            pre=pre.next;
        }
        pre.next=l1==null?l2:l1;
        return ans.next;
    }
}

14 LRU缓存--HashMap+双向链表

双向链表--头插尾删--维护最近最少使用--每次使用了,就删掉原来的,头插入

满了--尾删--最久未使用

LRU属性

/*
    HashMap+双向链表
        */

    HashMap<Integer,Node> map=new HashMap<>();
    int capacity;
    Node head, tail;  // 双向链表节点

双向链表

/*
    双向链表
        */
    //节点类--内部类
    class Node{
        int key;
        int val;
        Node next; //双向链表
        Node pre;

        //节点类构造函数
        public Node(int key, int val){
            this.key=key;
            this.val=val;
        } 
    }
    
    //节点的增删改操作-哨兵的头尾节点
    //头插法
    private void insertHead(Node node){
        Node nex=head.next;
        head.next=node;
        node.pre=head;
        node.next=nex;
        nex.pre=node;
    }
    //尾删法,并返回被删除的节点
    private Node cutTail(){
        Node last=tail.pre;
        last.pre.next=tail;
        tail.pre=last.pre;
        last.pre=null;
        last.next=null;
        return last;
    }
    //删除特定节点node
    private void cutNode(Node node){
        node.pre.next=node.next;
        node.next.pre=node.pre;
        node.next=null;
        node.pre=null;
    }

LRU类方法

/*
    LRU缓存类方法
        */
    //构造函数
    public LRUCache(int capacity) {
        this.capacity=capacity;
        //哨兵的头尾节点
        head=new Node(-1, -1);
        tail=new Node(-1, -1);
        head.next=tail;
        tail.pre=head;
    }
    
    public int get(int key) {
        if (map.containsKey(key)) {
            Node node=map.get(key);
            //取时,使用了,需要删掉原来的,头插入(最近使用)
            cutNode(node);
            insertHead(node);
            return node.val;
        }
        return -1;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node=map.get(key);
            node.val=value;
            //放入时,不管存不存在,使用了,需要删掉原来的,头插入(最近使用)
            cutNode(node);
            insertHead(node);
        }else{
            Node node=new Node(key, value);
            map.put(key, node);
            insertHead(node);
            if (map.size()>capacity) {
                //满了,从尾巴切除,头插尾删--保证满了,逐出最久未使用的关键字
                Node delNode =cutTail();
                map.remove(delNode.key);  // map里也删掉相应的键值对
            }
        }

完整代码

class LRUCache {

    /*
    HashMap+双向链表
        */

    HashMap<Integer,Node> map=new HashMap<>();
    int capacity;
    Node head, tail;  // 双向链表节点

     /*
    双向链表
        */
    //节点类--内部类
    class Node{
        int key;
        int val;
        Node next; //双向链表
        Node pre;

        //节点类构造函数
        public Node(int key, int val){
            this.key=key;
            this.val=val;
        } 
    }
    
    //节点的增删改操作-哨兵的头尾节点
    //头插法
    private void insertHead(Node node){
        Node nex=head.next;
        head.next=node;
        node.pre=head;
        node.next=nex;
        nex.pre=node;
    }
    //尾删法,并返回被删除的节点
    private Node cutTail(){
        Node last=tail.pre;
        last.pre.next=tail;
        tail.pre=last.pre;
        last.pre=null;
        last.next=null;
        return last;
    }
    //删除特定节点node
    private void cutNode(Node node){
        node.pre.next=node.next;
        node.next.pre=node.pre;
        node.next=null;
        node.pre=null;
    }
    
    /*
    LRU缓存类方法
        */
    //构造函数
    public LRUCache(int capacity) {
        this.capacity=capacity;
        //哨兵的头尾节点
        head=new Node(-1, -1);
        tail=new Node(-1, -1);
        head.next=tail;
        tail.pre=head;
    }
    
    public int get(int key) {
        if (map.containsKey(key)) {
            Node node=map.get(key);
            //取时,使用了,需要删掉原来的,头插入(最近使用)
            cutNode(node);
            insertHead(node);
            return node.val;
        }
        return -1;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node=map.get(key);
            node.val=value;
            //放入时,不管存不存在,使用了,需要删掉原来的,头插入(最近使用)
            cutNode(node);
            insertHead(node);
        }else{
            Node node=new Node(key, value);
            map.put(key, node);
            insertHead(node);
            if (map.size()>capacity) {
                //满了,从尾巴切除,头插尾删--保证满了,逐出最久未使用的关键字
                Node delNode =cutTail();
                map.remove(delNode.key);  // map里也删掉相应的键值对
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值