例题集 链表

本文深入探讨了链表的多种操作,包括单向链表和双向链表的反转、判断链表是否为回文结构的三种方法、按值划分链表、合并有序链表、两两交换链表节点、复制含随机指针的链表、寻找两个链表的相交节点。详细分析了各种算法的时间和空间复杂度,并提供了具体的Java实现。
摘要由CSDN通过智能技术生成
  1. 反转单向链表
  2. 反转双向链表
  3. 判断一个链表是否是回文结构
  4. 将单向链表按某值划分成左边小、中间相等、右边大的形式
  5. 打印有序链表的公共部分
  6. 复制含有随机指针节点的链表
  7. 两个单链表相交的一系列问题
  8. 合并升序链表
  9. 两两交换相邻结点
  10. 链表相加
  11. 删除链表的倒数第 n 个结点
  12. 链表中环的入口节点

1.反转单向链表
要求:时间复杂度要求为O(N),额外空间 复杂度要求为O(1)
思路:

  1. next指针指向last
public class ReverseSingleList18 {
    public static Node  reverseSingleList(Node head){
        Node pre = null;
        Node cur = head;
        Node next = null;

        while (cur != null){
            //保存下一个结点
            next = cur.next;
            //将当前结点链接至上一个结点
            cur.next = pre;
            //上一个结点指向当前结点
            pre = cur;
            //当前结点指向下一个结点
            cur = next;
        }
        return pre;
    }
}
    public Node reverseList(Node head) {
        //递归终止条件是当前为空,或者下一个节点为空
        if(head==null || head.next==null) {
            return head;
        }
        //这里的cur就是最后一个节点
        Node cur = reverseList(head.next);
        //这里请配合动画演示理解
        //如果链表是 1->2->3->4->5,那么此时的cur就是5
        //而head是4,head的下一个是5,下下一个是空
        //所以head.next.next 就是5->4
        head.next.next = head;
        //防止链表循环,需要将head.next设置为空
        head.next = null;
        //每层递归函数都返回cur,也就是最后一个节点
        return cur;
    }

2.反转双向链表
思路

  1. last指针指向next
  2. next指针指向last

public class ReverseDoubleList19 {

    public static DoubleNode reverseDoubleList(DoubleNode head) {

        DoubleNode pre = null;
        DoubleNode cur = head;
        DoubleNode next = null;

        while (cur != null){
            //找到下一个结点
            next = cur.next;
            //last反转
            cur.last = next;
            //next反转
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

3.判断一个链表是否是回文结构
给定一个链表的头节点head,请判断该链表是否为回文(正念和反念是一样的)结构
方法:
(1)开一个栈,将所有数都倒进去,从头开始遍历,一个一个进行比对

空间复杂度O(N)
public class isPalindrome20_1 {

    //判断是否是回文结构
    public static boolean isPalindrome(Node head){
        Stack<Integer> stack = new Stack<>();
        boolean isPalin = true;

        //将所有数依次倒进栈里
        Node headnode = head;
        while (headnode != null){
            int value = headnode.getValue();
            stack.push(value);
            headnode = headnode.getNext();
        }
        
        //从头开始一一比较
        Node firstNode = head;
        while (firstNode != null){
            if (firstNode.value == stack.pop()){
                firstNode = firstNode.getNext();
            }else {
                isPalin = false;
                break;
            }
        }
        return isPalin;
    }
}

(2)开一个栈,快指针走两步,慢指针走一步。从中间开始压栈,一个一个进行比对

空间复杂度O(2/N)
public class isPalindrome20_2 {

    public static boolean isPalindrome(Node head){
        boolean isPalin = true;
        Stack<Integer> stack = new Stack<>();

        if (head == null || head.next == null){
            return true;
        }

        //快指针
        Node fast = head;
        //慢指针
        Node slow = head;
        //区分是链表结点是奇数还是偶数
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //慢指针来到了中间
        slow = slow.next;
        while (slow != null){
            stack.push(slow.value);
            slow = slow.next;
        }

        //一一比对
        Node firstNode = head;
        while (!stack.isEmpty()){
            if (firstNode.value == stack.pop()){
                firstNode = firstNode.next;
            }else {
                isPalin = false;
                break;
            }
        }
        return isPalin;
    }
}

(3)快指针走两步,慢指针走一步。将后半部分逆序,从两端开始比较,比较结束都再逆序回来

空间复杂度O(1)
public class isPalindrome20_3 {

    public static boolean isPalindrome(Node head){
        boolean isPalin = true;
        Node fast = head;
        Node slow = head;

        if (head == null || head.next == null){
            return true;
        }

        //找到中间结点
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //反转单链表
        Node pre = null;
        Node next = null;
        while (slow != null){
            //找到下一结点
            next = slow.next;
            slow.next = pre;
            pre = slow;
            slow = next;
        }

        //一一比对
        Node firstNode = head;
        while (firstNode.next != null){
            if (firstNode.value == pre.value){
                firstNode = firstNode.next;
                pre = pre.next;
            }else {
                isPalin = false;
                break;
            }
        }
        return isPalin;
    }
}

4.将单向链表按某值划分成左边小、中间相等、右边大的形式
要求:
(1)给定一个单向链表的头节点head,节点的值类型是整型,再给定一个 整数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于 pivot 的节点,中间部分都是值等于pivot的节点,右部分都是值大于 pivot的节点。
(2)不作稳定性要求

思路:
(1)使用荷兰国旗问题求解,荷兰国旗问题做不到稳定性
(2)开创一个数组,将结点放到数组中
(3)使用荷兰国旗问题求解

public class listPartition_21arr {

    //用数组实现荷兰国旗问题
    private static Node  arrPartition(Node head, int num) {
        //判空
        if (head == null){
            return head;
        }

        //遍历一次找到单链表长度
        Node sizeLink = head;
        int size = 0;
        while (sizeLink != null){
            size++;
            sizeLink = sizeLink.next;
        }

        //开创数组
        Node firstNode = head;
        Node[] arr = new Node[size];
        for (int i = 0;i < size;i++){
            arr[i] =firstNode;
            firstNode = firstNode.next;
        }

        //荷兰国旗问题
        //头指针
        int first = -1;
        //尾指针
        int last = size;
        //当前指针
        int cur = 0;

        while (cur != last){
            //相等
            // 指针下移
            if (arr[cur].value == num){
                cur++;
            }else if (arr[cur].value < num){
                //小于
                //当前数与first下一个数交换,指针下移
                swap(arr,cur++,++first);
            }else {
                //大于
                //当前数与last前一个数交换,指针不动
                swap(arr,cur,--last);
            }
        }

        //将数组串成新链表
        Node newHead = arr[0];
        Node returnNode = newHead;
        newHead.next = null;

        for (int i = 1;i < size;i++){
            newHead.next = arr[i];
            newHead = arr[i];
            newHead.next = null;
        }

        return returnNode;
    }

    private static void swap(Node[] arr, int cur, int i) {
        Node tmp = arr[cur];
        arr[cur] = arr[i];
        arr[i] = tmp;
    }

    //实现单链表
    public static class Node {
        private int value;
        private Node next;

        public Node(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }
}

要求:
(1)给定一个单向链表的头节点head,节点的值类型是整型,再给定一个 整数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于 pivot 的节点,中间部分都是值等于pivot的节点,右部分都是值大于 pivot的节点。
(2)实现稳定性
(3)时间复杂度请达到O(N),额外空间复杂度请达到O(1)

思路:
(1)创建3个链表分别是small equal big
(2)遍历原链表 若小于、等于、大于pivot则将结点取下分别放进small equal big链表

public class listPartition_21list {

    private static Node listPartition(Node head, int num) {
        //判空
        if (head == null){
            return null;
        }

        //小链表指针
        Node small = null;
        //小链表头
        Node smallHead = null;
        //相同链表指针
        Node equal = null;
        //相同链表头
        Node equalHead = null;
        //大链表指针
        Node big = null;
        //大链表头
        Node bigHead = null;
        //记录下一个结点
        Node next = null;

        while (head != null){
            //保存下一个结点
            next = head.next;
            //将原结点从原链表上断开
            head.next = null;

            if (head.value < num){
                //小于
                if (small == null){
                    small = head;
                    smallHead = small;
                }else {
                    small.next = head;
                    small = head;
                }
            }else if (head.value == num){
                //等于
                if (equal == null){
                    equal = head;
                    equalHead =equal;
                }else {
                    equal.next = head;
                    equal = head;
                }
            }else {
                //大于
                if (big == null){
                    big = head;
                    bigHead = big;
                }else {
                    big.next = head;
                    big = head;
                }
            }

            head = next;
        }
        //返回结点
        Node returnHead = null;
        //将三个链表串起来  ->  有的链表可能为空
        if (smallHead == null && equalHead == null){
            returnHead = bigHead;
        }else if (equalHead == null && bigHead ==null){
            returnHead = smallHead;
        }else if (smallHead == null && bigHead == null){
            returnHead = equalHead;
        }else if (smallHead == null && equalHead != null && bigHead != null){
            equal.next = bigHead;
            returnHead = equalHead;
        }else if (smallHead  != null &&equalHead == null && bigHead != null){
            returnHead = smallHead;
            small.next = bigHead;
        }else if (smallHead  != null && equalHead != null && bigHead == null){
            returnHead = smallHead;
            small.next = equalHead;
        }else {
            returnHead = smallHead;
            small.next = equalHead;
            equal.next = bigHead;
        }

        return returnHead;
    }

    //实现单链表
    public static class Node {
        private int value;
        private Node next;

        public Node(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }
}

5.打印有序链表的公共部分
要求:给定两个有序链表的头指针head1和head2,打印两个链表的公共部分
思路:
(1)只用专注于一个链表,这个链表上是否有等同于另外一个链表的部分
(2)如果head1<head2,head1 下移
(3)如果head1>head2,head2 下移
(4)如果head1=head2,打印 head1、head2下移

public class printListCommonPart22 {

    private static void printListCommonPart(Node head1, Node head2) {
        if (head1 == null || head2 == null){
            return;
        }

        while (head1 != null && head2 != null){
            if (head1.value < head2.value){
                head1 = head1.next;
            }else if (head1.value > head2.value){
                head2 = head2.next;
            }else {
                System.out.println("相同" + head1.value);
                head1 = head1.next;
                head2 = head2.next;
            }
        }
    }

    //实现单链表
    public static class Node {
        private int value;
        private Node next;

        public Node(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }
}

6.复制含有随机指针节点的链表
题解:
一种特殊的链表节点类描述如下:
Node类中的value是节点值,next指针和正常单链表中next指针的意义 一 样,都指向下一个节点,rand指针是Node类中新增的指针,这个指针可能指向链表中的任意一个节点,也可能指向null。

要求:
给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。

在这里插入图片描述
方法1:
使用哈希表

需要额外控件O(N)
public class copyList_hashmap23 {


    private static Node copyList_hashmap(Node head) {

        if (head == null){
            return null;
        }

        HashMap<Node,Node> hashMap = new HashMap<>();
        //遍历一次复制结点
        Node cur = head;
        while (cur != null){
            hashMap.put(cur,new Node(cur.value));
            cur = cur.next;
        }

        //再遍历一次 复制next指针,rand指针
        cur = head;
        while (cur != null){
            hashMap.get(cur).next = hashMap.get(cur.next);
            hashMap.get(cur).rand = hashMap.get(cur.rand);
            cur = cur.next;
        }

        return hashMap.get(head);
    }

    public static class Node {
        public int value;
        public Node next;
        public Node rand;
        public Node(int data) {
            this.value = data;
        }
    }
}

方法2:
要求:
不使用额外的数据结构,只用有限几个变量,且在时间复杂度为 O(N) 内完成原问题要实现的函数
思路:

  1. 第一次遍历的时候加入新结点
  2. 第二次遍历赋值rand
  3. 第三次遍历赋值next,并分离结构
public class copyList_newList23 {

    private static Node  copyList_newList(Node head) {
        if (head == null){
            return null;
        }

        //遍历第一次创造新结点
        Node cur = head;
        Node next = null;
        while (cur != null){
            next = cur.next;
            cur.next = new Node(cur.value);
            cur.next.next = next;
            cur = next;
        }

        //遍历第二次 赋值rand
        cur = head;
        Node copy = null;
        while (cur != null){
            next = cur.next.next;
            copy = cur.next;
            copy.rand = cur.rand != null ? cur.rand : null;
            cur = next;
        }

        //遍历第三次分离结构 并 赋值next
        cur = head;
        Node returnNode = head.next;
        copy = null;
        while (cur != null){
            next = cur.next.next;
            copy = cur.next;
            copy.next = cur.next.next != null ? next.next : null;
            cur.next = next;
            cur = next;
        }


        return returnNode;
    }


    public static class Node {
        public int value;
        public Node next;
        public Node rand;
        public Node(int data) {
            this.value = data;
        }
    }
}

7.两个单链表相交的一系列问题
题解:
在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。
请实现一个函数, 如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。

核心:
两个单链表若相交,要么都有环,要么都没有环
若相交只可能存在以下几种情况:
(1)两个无环单链表相交
(2)两个单链表先相交再成环
(3)两个单链表先成环再相交

思路:

  1. 先判断两个单链表是否有环,有则返回第一个入环结点
  2. -> 都没环 -> 判断最后一个结点是否相同(相同则相交,不相同则不相交)-> 相同则遍历找到第一个相交
  3. -> 都有环 -> 判断入环结点是否相同(相同则遍历找到第一个相交;不相同遍历去找,找到即相交,找不到即不想交)
  4. -> 一个有环一个没环 -> 不可能相交

只可能存在以下拓扑结构:
在这里插入图片描述

方法1:使用hashmap


//用hashmap实现
public class getIntersectNode24_hashmap {

    public static void main(String[] args) {
        //得到两个无环单链表
//        Node[] arrNoNode = getNoLoopNode();
//        Node intersectNode = getIntersectNode(arrNoNode[0], arrNoNode[1]);
//        if (intersectNode == null){
//            System.out.println("null");
//        }else {
//            System.out.println(intersectNode.value);
//        }

        //得到两个都有环单链表
        Node[] arrBothNode = getBothLoopNode();
        Node intersectNode = getIntersectNode(arrBothNode[0], arrBothNode[1]);
        if (intersectNode == null){
            System.out.println("null");
        }else {
            System.out.println(intersectNode.value);
        }
    }

    public static Node  getIntersectNode(Node head1,Node head2){

        Node returnNode = null;

        //判空
        if (head1 == null || head2 == null){
            return null;
        }
        //得到两个链表的第一个入环结点
        Node loopNode1 = getLoopNode(head1);
        Node loopNode2 = getLoopNode(head2);

        //两个链表都有环
        if (loopNode1 != null && loopNode2 != null){
            returnNode = bothLoop(head1,loopNode1,head2,loopNode2);
        }else if (loopNode1 == null && loopNode2 == null){
            //两个链表都没有环
            returnNode = noLoop(head1, head2);
        }

        return returnNode;
    }

    //两个有环单链表找第一个相交结点
    private static Node bothLoop(Node head1, Node loopNode1, Node head2, Node loopNode2) {
        HashSet<Node> hashSet = new HashSet<>();

        //如果两个入环结点相同    ->     重复noLoop()
        if (loopNode1 == loopNode2){
            //存入head1 ~ (loopNode1 - 1)结点
            Node head1Cur = head1;
            while (head1Cur != loopNode1){
                hashSet.add(head1Cur);
                head1Cur = head1Cur.next;
            }

            //让head2 ~ (loopNode2 - 1)结点一一比对
            Node head2Cur = head2;
            while (head2Cur != loopNode2){
                if (hashSet.contains(head2Cur)){
                    return head2Cur;
                }
                head2Cur = head2Cur.next;
            }

            return head1Cur;

        }else {
            //两个入环结点不相同     ->      可能有环也可能没环

            //存入链表1 loopNode ~ loopNode结点
            Node cur = loopNode1.next;
            while (cur != loopNode1){
                hashSet.add(cur);
                cur = cur.next;
            }
            hashSet.add(loopNode1);

            //一一比对
            //先比较loopNode2
            if (hashSet.contains(loopNode2)){
                return loopNode1;
            }
            //再比较 (loopNode2 + 1) ~ (loopNode2 - 1)结点
            cur = loopNode2.next;
            while (cur != loopNode2){
                if (hashSet.contains(cur)){
                    return loopNode1;
                }
                cur = cur.next;
            }
            return null;
        }
    }

    //两个无环单链表找第一个相交结点
    private static Node noLoop(Node head1, Node head2) {
        HashSet<Node> hashSet = new HashSet<>();
        //存入所有head1链表结点
        Node head1Cur = head1;
        while (head1Cur != null){
            hashSet.add(head1Cur);
            head1Cur = head1Cur.next;
        }

        //让head2链表结点一一比对
        Node head2Cur = head2;
        while (head2Cur != null){
            if (hashSet.contains(head2Cur)){
                return head2Cur;
            }
            head2Cur = head2Cur.next;
        }

        return null;
    }


    private static Node getLoopNode(Node head1) {
        //用hashSet一直存放结点
        //若有相等Node  则可以得到第一个入环结点
        //若没有相等Node 则没有环
        HashSet<Node> hashSet = new HashSet<>();
        while (head1 != null){
            if (hashSet.contains(head1)){
                return head1;
            }
            hashSet.add(head1);
            head1 = head1.next;
        }

        return  null;
    }

    private static Node[] getNoLoopNode() {
        //插入数据
        Node head1 = new Node(0);
        head1.setNext(null);
        Node node11 = new Node(1);
        head1.setNext(node11);
        node11.setNext(null);
        Node node12 = new Node(2);
        node11.setNext(node12);
        node12.setNext(null);
        Node node13 = new Node(3);
        node12.setNext(node13);
        node13.setNext(null);

        Node head2 = new Node(0);
        head2.setNext(null);
        Node node21 = new Node(1);
        head2.setNext(node21);
        node21.setNext(node13);


        return new Node[]{head1,head2};
    }

    private static Node[] getBothLoopNode() {
        //插入数据
        Node head1 = new Node(0);
        head1.setNext(null);
        Node node11 = new Node(1);
        head1.setNext(node11);
        node11.setNext(null);
        Node node12 = new Node(2);
        node11.setNext(node12);
        node12.setNext(null);
        Node node13 = new Node(3);
        node12.setNext(node13);
        node13.setNext(null);
        Node node14 = new Node(4);
        node13.next = node14;
        node14.next = node12;

        Node head2 = new Node(5);
        head2.setNext(null);
        Node node21 = new Node(6);
        head2.next = node21;
        node21.next = node13;


        return new Node[]{head1,head2};
    }


    //实现单链表
    public static class Node{
        private int value;
        private Node next;

        public Node(int value){
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }
}

方法2:使用有限几个变量
要求:
如果链表1 的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。

public class getIntersectNode24_improve {

    private static Node getIntersectNode(Node head1,Node head2) {
        //记录返回结点
        Node returnNode = null;

        //判空
        if (head1 == null || head2 == null){
            return null;
        }

        //得到入环第一个结点
        Node loopNode1 = getLoopNode(head1);
        Node loopNode2 = getLoopNode(head2);

        if (loopNode1 == null && loopNode2 == null){
            //两个都没环
            returnNode = noLoop(head1, head2);

        }else if (loopNode1 != null && loopNode2 != null){
            //两个有环
            returnNode = bothLoop(head1,loopNode1,head2,loopNode2);

        }

        return returnNode;
    }

    private static Node bothLoop(Node head1, Node loopNode1, Node head2, Node loopNode2) {
        //记录返回结点
        Node returnNode = null;

        //如果两个入环结点相同    ->  重复noLoop()
        if (loopNode1 == loopNode2){
            //记录单链表的差值
            int num = 0;
            //记录当前head1指针
            Node curHead1 = head1;
            //记录当前head2指针
            Node curHead2 = head2;
            //遍历一次得到单链表1的 (长度-1) 和 最后一个结点
            while (curHead1.next != loopNode1){
                num++;
                curHead1 = curHead1.next;
            }
            //遍历第二次得到单链表2 与 单链表1 的差值 和 最后一个结点
            while (curHead2.next != loopNode2){
                num--;
                curHead2 = curHead2.next;
            }

            curHead1 = head1;
            curHead2 = head2;

            //如果最后两个结点相交
            if (num > 0){
                //链表1长度 > 链表2长度
                //单链表1 先走n步 与 单链表2 同长度
                while (num != 0){
                    curHead1 = curHead1.next;
                    num--;
                }
                //一一比对
                while (curHead1 != null){
                    if (curHead1 == curHead2){
                        returnNode = curHead1;
                        break;
                    }
                    curHead1 = curHead1.next;
                    curHead2 = curHead2.next;
                }
            }else {
                //链表1长度 <= 链表2长度
                num = Math.abs(num);

                //单链表2 先走n步 与 单链表1 同长度
                while (num != 0){
                    curHead2 = curHead2.next;
                    num--;
                }
                //一一比对
                while (curHead1 != null){
                    if (curHead1 == curHead2){
                        returnNode = curHead1;
                        break;
                    }
                    curHead1 = curHead1.next;
                    curHead2 = curHead2.next;
                }

            }

        }else {
            //入过两个入环结点不相同   ->  可能有环也可能无环
            Node curLoopNode1 = loopNode1.next;
            while (curLoopNode1 != loopNode1){
                if (curLoopNode1 == loopNode2){
                    returnNode = curLoopNode1;
                    break;
                }
                curLoopNode1 = curLoopNode1.next;
            }
        }


        return returnNode;
    }

    private static Node noLoop(Node head1, Node head2) {
        //记录返回的Node
        Node returnNode = null;
        //记录单链表的差值
        int num = 0;
        //记录当前head1指针
        Node curHead1 = head1;
        //记录当前head2指针
        Node curHead2 = head2;

        //遍历一次得到单链表1的 (长度-1) 和 最后一个结点
        while (curHead1.next != null){
            num++;
            curHead1 = curHead1.next;
        }
        //遍历第二次得到单链表2 与 单链表1 的差值 和 最后一个结点
        while (curHead2.next != null){
            num--;
            curHead2 = curHead2.next;
        }

        //如果最后两个结点不相同 说明没相交
        if (curHead1 != curHead2){
            return null;
        }
        curHead1 = head1;
        curHead2 = head2;

        //如果最后两个结点相交
        if (num > 0){
            //链表1长度 > 链表2长度

            //单链表1 先走n步 与 单链表2 同长度
            while (num != 0){
                curHead1 = curHead1.next;
                num--;
            }
            //一一比对
            while (curHead1 != null){
                if (curHead1 == curHead2){
                    returnNode = curHead1;
                    break;
                }
                curHead1 = curHead1.next;
                curHead2 = curHead2.next;
            }
        }else {
            //链表1长度 <= 链表2长度
            num = Math.abs(num);

            //单链表2 先走n步 与 单链表1 同长度
            while (num != 0){
                curHead2 = curHead2.next;
                num--;
            }
            //一一比对
            while (curHead1 != null){
                if (curHead1 == curHead2){
                    returnNode = curHead1;
                    break;
                }
                curHead1 = curHead1.next;
                curHead2 = curHead2.next;
            }

        }

        return returnNode;
    }

    private static Node getLoopNode(Node head1) {
        //快指针   ->  在没相遇前一次走两步,相遇后一次走一步
        Node fast = head1;
        //慢指针   ->  在没相遇前一次走一步,相遇后一次走一步
        Node slow = head1;
        //基本判是否为环
        if (fast.next == null || fast.next.next == null){
            return null;
        }

        //第一次遍历 -> 若fast指针找到空了说明没有环
        do {
            fast = fast.next.next;
            slow = slow.next;
        } while (fast.next != null && fast.next.next != null && fast != slow);

        if (fast.next == null || fast.next.next == null){
            return null;
        }

        //第二次遍历 -> fast指针指向头结点,fast,slow一次走一步,相遇即为第一个
        fast = head1;
        while (fast != slow){
            fast = fast.next;
            slow = slow.next;
        }

        return fast;
    }
}

8.合并升序链表
题目:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

思路1
外排 + 辅助链表
//时间 100%
//空间 7%
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {

        ListNode help = new ListNode();
        ListNode cur = help;

        while (l1 != null && l2 != null){
            if (l1.val < l2.val){
                cur.next = l1;
                l1 = l1.next;
                cur = cur.next;
            }else {
                cur.next = l2;
                l2 = l2.next;
                cur = cur.next;
            }
        }

        while (l1 != null){
            cur.next = l1;
            l1 = l1.next;
            cur = cur.next;
        }

        while (l2 != null){
            cur.next = l2;
            l2 = l2.next;
            cur = cur.next;
        }

        return help.next;
    }
思路2
递归
//时间 100%
//空间 93%
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {

        if (l1 == null){
            return l2;
        }
        if (l2 == null){
            return l1;
        }
        if (l1.val < l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else {
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }

    }

9.两两交换相邻结点
题目:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表
要求:
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换

思路1:
在原链表上操作
 	//空间 13%
    public ListNode swapPairs(ListNode head) {

        if (head == null || head.next == null){
            return head;
        }

        ListNode cur = head;
        ListNode last;
        ListNode lastlast;
        ListNode pre = cur;
        boolean isFirst = true;

        ListNode returnNode = cur.next;

        while (cur != null && cur.next != null){
            last = cur.next;
            lastlast = cur.next.next;

            if (isFirst){
                last.next = cur;
                cur.next = lastlast;
                isFirst = false;;
            }else {
                last.next = cur;
                cur.next = lastlast;
                pre.next = last;
                pre = cur;
            }

            cur = lastlast;
        }

        return returnNode;
    }
思路2
递归
//空间 95%
public class _9_swapListPairs_2_leetcode {

    public ListNode swapPairs(ListNode head) {

        if (head == null || head.next == null) {
            return head;
        }

        ListNode newHead = head.next;
        head.next = swapPairs(newHead.next);
        newHead.next = head;
        return newHead;
    }



    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;
      }
    }
}
  1. 链表相加
    //时间 77.70%
    //空间 79.04%
    public ListNode addTwoNumbers_2(ListNode l1, ListNode l2) {
        //方便读取数据
        StringBuilder builder1 = new StringBuilder();
        while (l1 != null){
            builder1.append(l1.val);
            l1 = l1.next;
        }
        StringBuilder builder2 = new StringBuilder();
        while (l2 != null){
            builder2.append(l2.val);
            l2 = l2.next;
        }

        //指针
        int i = builder1.length() - 1;
        int j = builder2.length() - 1;
        //判断是否将进位
        int add = 0;
        ListNode ans = null;

        while (i >= 0 || j >= 0 || add != 0){
            //判断是否补零
            int x = i >= 0 ? builder1.charAt(i) - '0' : 0;
            int y = j >= 0 ? builder2.charAt(j) - '0' : 0;
            int cur = x+y+add;
            //cur/10为进位
            add = cur / 10;
            //cur/10为位数
            cur = cur % 10;
            //头插法
            ListNode curnode = new ListNode(cur);
            curnode.next = ans;
            ans = curnode;
            i--;
            j--;
        }

        return ans;
    }

11.删除链表的倒数第 n 个结点
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点

    //时间 100
    //空间 64.95
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null || n <=0){
            return head;
        }

        ListNode slow = head;
        ListNode fast = head;

        //快指针先走n步
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }

        //如果走完fast已经为空
        //说明要删除头结点
        //返回头结点下一个结点
        if (fast == null){
            return head.next;
        }

        //快慢指针各走一步
        while (fast.next != null){
            slow = slow.next;
            fast = fast.next;
        }

        //断开
        slow.next = slow.next.next;
        return head;
    }

12.链表中环的入口节点

    //双指针改进
    //时间 100
    //空间 65.17
    public ListNode detectCycle_3(ListNode head) {
        if (head == null){
            return head;
        }

        ListNode slow = head;
        ListNode fast = 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;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值