链表刷题总结

链表刷题总结

总结就是,给我好好用头结点!!!虚拟头结点十分,肥肠之关键。

203 移除链表元素 以这道题为例,我们来看看头结点是如何使用的

在没有使用头结点的情况下

    public ListNode removeElements(ListNode head, int val) {
        while (head != null && head.val == val) head = head.next;
        ListNode cur = head;
        ListNode pre;
        if (cur != null){
            pre = cur;
            cur = cur.next;
            while (cur != null){
                if (cur.val == val){
                    pre.next = cur.next;
                    cur = cur.next;
                    continue;
                }
                pre = cur;
                cur = cur.next;
            }
        }
        return head;
    }

用了头结点的方法

    public ListNode removeElements(ListNode head, int val) {
        if (head == null) return null;
        ListNode dummy = new ListNode(-1);
        dummy.next = head; // 在这里我们假设了一个虚拟的头结点来辅助我们操作
        ListNode pre = dummy;
        ListNode cur = head;
        
        while (cur != null){
            if (cur.val == val){
                pre.next = cur.next;
                cur = cur.next;
                continue;
            }
            pre = cur;
            cur = cur.next;
        }
        return dummy.next;
    }

最关键的地方在于链表的第一个点。第一个点不需要我们去费心思操作应该怎么办了,所以链表最牛的地方就在于,他可以帮助你把第一个节点的操作虚拟化成每一个其他节点的操作。

707 设计链表
这题也不难,关键还是考验对于代码的掌控能力。其实都是一些基础的操作。我在链表里面维护了一个头结点,这个是相当关键的。

//单链表
class Node{
    int val;
    Node next;
    public Node(){}
    public Node(int val){
        this.val = val;
    }
}

public class MyLinkedList {
    int size;
    Node head;
    public MyLinkedList() {
        this.size = 0;
        this.head = new Node(-1);
    }

    public int get(int index) {
        if (index < 0 || index >= size) return -1;
        Node temp = head;
        for (int i = 0;i<index;i++)
            temp = temp.next;
        return temp.next.val;
    }

    public void addAtIndex(int index, int val) {
        if (index <= 0){
            //说明这个时候要在头结点加入一个节点,事实上,head就是我们的虚拟头结点
            Node node = new Node(val);
            node.next = head.next;
            head.next = node;
            size++;
        }else if (index == size){
            //说明这个时候要在末尾加入节点
            Node temp = head;
            while (temp.next != null) temp = temp.next;// 找到最后一个节点
            temp.next = new Node(val);
            size++;
        }else if (0 < index && index < size){
            Node temp = head;
            for (int i =0;i<index;i++)
                temp = temp.next;
            Node node = new Node(val);
            node.next = temp.next;
            temp.next = node;
            size++;
        }
    }

    public void addAtHead(int val) {
        addAtIndex(0,val);
    }

    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        Node temp = head;
        for (int i = 0;i<index;i++)
            temp = temp.next;
        temp.next = temp.next.next;
        size--;
    }

    public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addAtHead(1);
        linkedList.addAtTail(3);
        linkedList.addAtIndex(1,2);
        linkedList.deleteAtIndex(1);
        System.out.println(linkedList.get(0));
        System.out.println(linkedList.get(1));
        System.out.println(linkedList.get(2));
    }
}
//双链表

206 反转链表

    ListNode pre = null;
    public ListNode reverseList(ListNode head) {
        if (head == null) {
            return head;
        }
        if (head.next ==null){
            head.next = pre;
            return head;
        }
        ListNode temp = head.next;
        head.next = pre;
        pre = head;
        head = temp;
        return reverseList(head);
    }

递归的手法。递归的手法避免了前节点搞来搞去还可以

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

不递归就要思考对于前置节点应该如何处理。

24 两两交换链表中的节点
还是记录一下这道题是怎么写的吧
首先这道题会有奇数点产生,对于奇数点产生应该怎么操作。
在第一眼看到这道题的时候,脑子里只有四个字,冒泡排序,这和冒泡排序有点类似。区别在于两个点

  1. 这个是间隔的冒泡排序,也就是说,只有1 3 5这些点是需要进行排序的。
  2. 这是链表,不是数组,需要链接。我遇到的问题就是在,在
    1 2 交换之后,对于点3需要1去链接他,这个时候就需要额外的操作了。
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        ListNode cur = head;
        ListNode temp;
        while (cur != null){
            if (cur.next == null) break;
            temp = cur.next;
            cur.next = cur.next.next;
            temp.next = cur;
            pre.next = temp;//重点,pre是充当那个桥梁的作用
            pre = cur;
            cur = cur.next;
        }
        return dummy.next;
    }

这是我的代码。从这里看出头结点是多么重要。

19 删除链表的倒数第N个节点

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        for (int i =0;i<n;i++){
            head = head.next;
        }
        while (head != null){
            pre = pre.next;
            head = head.next;
        }
        pre.next = pre.next.next;
        return dummy.next;
    }

这道题这样的解法应该是最具体的思路。这个借鉴了滑动窗口的思想。相当于这是一个窗口级别的滑动。当停止的时候就直接删除一个节点就好了。关键就在于对于边界的把控。

面试题 02.07. 链表相交
Krahets给出的方法真的。我看完都无语,他主要把我了一个点就是,一个点去遍历两条路线。如果有相同的节点,在某一个时刻,他们一定会汇聚到一个点上,这是何等的天才啊。如果没有相等的点,就会是两个null.

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA;
        ListNode B = headB;
        while (A != B){
            A = A != null? A.next:headB;
            B = B != null? B.next:headA;
        }
        return A;
    }

142 环形链表 II
这道题最简单的思路,就是用一个哈希表记录。因为hash表记录的东西是没有重叠的。一旦出现了重叠,就会报错。

    public ListNode detectCycle(ListNode head) {
        if (head == null ) return null;
        HashSet<ListNode> hs = new HashSet<ListNode>();
        while (head.next !=null){
            boolean add = hs.add(head);
            if (!add) return head;
            head = head.next;
        }
        return null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值