代码随想录刷题-链表

发布于:2024-12-20 ⋅ 阅读:(112) ⋅ 点赞:(0)

1.链表定义

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 链表定义
 *
 * @Author sun
 * @Create 2024/11/26 13:30
 * @Version 1.0
 */
public class ListNode {

    // 节点的值
    int val;
    // 下一个节点
    ListNode next;

    // 节点的构造
    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}
2.思路

一个单链表节点需要有两个参数,值和指向下一个节点的指针,以及两个构造

2.移除链表元素

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 203. 移除链表元素
 *
 * @Author sun
 * @Create 2024/11/26 13:35
 * @Version 1.0
 */
public class T2_2 {

    public ListNode removeElements(ListNode head, int val) {
        // 构造哨兵节点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        // 使用临时指针循环遍历
        ListNode temp = dummy;
        while (temp.next != null) {
            // 如果需要删除,则进行删除操作
            if (temp.next.val == val) {
                temp.next = temp.next.next;
            } else {
                // 如果不需要删除,则移动temp
                temp = temp.next;
            }
        }
        // 返回新的头结点
        return dummy.next;
    }
}
2.思路

使用一个哨兵节点,指向头节点,然后使用临时指针去循环遍历,进行删除操作

3.设计链表

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 707.设计链表
 *
 * @Author sun
 * @Create 2024/11/26 13:50
 * @Version 1.0
 */
public class MyLinkedList {

    /**
     * 值
     */
    int val;

    /**
     * 指向下一个节点的指针
     */
    MyLinkedList next;

    /**
     * 哨兵节点
     */
    MyLinkedList dummy;

    /**
     * 构造器
     *
     * @param val
     */
    private MyLinkedList(int val) {
        this.val = val;
    }

    /**
     * 初始化 MyLinkedList 对象
     */
    public MyLinkedList() {
        // 初始化哨兵节点
        dummy = new MyLinkedList(-1);
    }

    /**
     * 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1
     *
     * @param index
     * @return
     */
    public int get(int index) {
        // 求链表的长度
        int len = getLen();
        // 下标无效,直接返回
        if (index < 0 || index >= len) {
            return -1;
        } else {
            MyLinkedList curr = dummy;
            int num = -1;
            while (curr.next != null) {
                num ++;
                // 此时的num,就是下一个元素的下标
                if (num == index) {
                    return curr.next.val;
                } else {
                    curr = curr.next;
                }
            }
            return -1;
        }
    }

    /**
     * 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
     *
     * @param val
     */
    public void addAtHead(int val) {
        // 构建节点
        MyLinkedList node = new MyLinkedList(val);
        // 插入到第一个元素之前
        node.next = dummy.next;
        dummy.next = node;
    }

    /**
     * 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
     *
     * @param val
     */
    public void addAtTail(int val) {
        // 构建节点
        MyLinkedList node = new MyLinkedList(val);
        // 遍历链表
        MyLinkedList curr = dummy;
        while (curr.next != null) {
            curr = curr.next;
        }
        // 添加节点
        curr.next = node;
    }

    /**
     * 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。
     * 如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
     *
     * @param index
     * @param val
     */
    public void addAtIndex(int index, int val) {
        int len = getLen();
        // 当index比链表的长度更长,就直接返回
        if (index > len) {
            return;
        }
        // 当index是链表的长度,那么就插入到链表的末尾
        if (index == len) {
            addAtTail(val);
        } else {
            // 其余情况,就插入到该元素之前
            // 首先,移动到该元素的前一个位置
            MyLinkedList curr = dummy;
            int num = -1;
            while (curr.next != null) {
                num ++;
                // 此时的num就代表下一个元素的下标
                if (num == index) {
                    // 如果下一个元素的下标就是index,则插入元素
                    // 构建节点
                    MyLinkedList node = new MyLinkedList(val);
                    // 插入到下一个元素之前
                    node.next = curr.next;
                    curr.next = node;
                    // 直接退出
                    break;
                } else {
                    // 如果不是,则继续遍历
                    curr = curr.next;
                }
            }
        }
    }

    /**
     * 求链表的长度
     *
     * @return
     */
    private int getLen() {
        int len = 0;
        MyLinkedList curr = dummy;
        while (curr.next != null) {
            curr = curr.next;
            len++;
        }
        return len;
    }

    /**
     * 如果下标有效,则删除链表中下标为 index 的节点。
     *
     * @param index
     */
    public void deleteAtIndex(int index) {
        // 求链表的长度
        int len = getLen();
        // 下标无效,直接返回
        if (index < 0 || index >= len) {
            return;
        }
        // 找到指定下标的元素
        int num = -1;
        MyLinkedList curr = dummy;
        while (curr.next != null) {
            num ++;
            // 此时的num则代表下一个元素的下标,如果满足要求,就直接删除,然后退出
            if (num == index) {
                // 删除下一个元素,然后退出
                curr.next = curr.next.next;
                return;
            } else {
                // 不满足要求就继续遍历
                curr = curr.next;
            }
        }
    }
}
2.思路

核心就是设置一个虚拟的头结点,然后使用临时节点进行遍历

4.反转链表

1.答案(递归实现)
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 206. 反转链表
 *
 * @Author sun
 * @Create 2024/11/26 14:27
 * @Version 1.0
 */
public class T2_4 {

    private ListNode res;

    public ListNode reverseList(ListNode head) {
        reverse(null, head);
        return res;
    }

    /**
     * 函数调用记录状态
     *
     * @param prev 前一个节点
     * @param curr 当前节点
     */
    public void reverse(ListNode prev, ListNode curr) {
        // 退出条件
        if (curr == null) {
            res = prev;
            return;
        } else {
            reverse(curr, curr.next);
            curr.next = prev;
        }
    }
}
2.思路

这里使用了递归的思想,用到了核心之一,就是函数调用,记录状态,使用递归分别记录当前节点和前一个节点的状态,从而完成链表的反转

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

1.答案(递归实现)
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 24. 两两交换链表中的节点
 *
 * @Author sun
 * @Create 2024/11/26 15:06
 * @Version 1.0
 */
public class T2_5 {

    public ListNode swapPairs(ListNode head) {
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            return head;
        }
        return swap(head.next, head);
    }

    public ListNode swap(ListNode curr, ListNode prev) {
        // 终止条件
        if (curr == null) {
            return prev;
        } else if (curr.next == null) {
            // 反转当前这对节点
            curr.next = prev;
            prev.next = null;
            // 然后返回当前节点
            return curr;
        } else {
            ListNode swap = swap(curr.next.next, curr.next);
            // 反转当前这对节点
            curr.next = prev;
            prev.next = swap;
            return curr;
        }
    }
}
2.思路

递归思考的顺序是参数传递状态,然后考虑终止条件,最后考虑返回值。

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

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 19. 删除链表的倒数第 N 个结点
 *
 * @Author sun
 * @Create 2024/11/26 16:20
 * @Version 1.0
 */
public class T2_6 {

    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 校验合法性
        if (n < 1 || n > getLen(head)) {
            return null;
        }
        // 计算倒数第n个是正数的下标
        int delete = getLen(head) - n;
        // 头节点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode curr = dummy;
        int index = -1;
        while (curr.next != null) {
            index ++;
            // 此时的index就是正数的下标,删除并返回头结点
            if (index == delete) {
                curr.next = curr.next.next;
                return dummy.next;
            } else {
                // 如果不是就移动
                curr = curr.next;
            }
        }
        return null;
    }

    /**
     * 返回链表的长度
     *
     * @param head
     * @return
     */
    private int getLen(ListNode head) {
        int len = 0;
        ListNode curr = head;
        while (curr != null) {
            len++;
            curr = curr.next;
        }
        return len;
    }
}
2.思路

首先计算出删除倒数第n个节点其实是删除正数的节点下标,然后就变成了删除指定下标的节点

7.链表相交

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 面试题 02.07. 链表相交
 *
 * @Author sun
 * @Create 2024/11/26 16:46
 * @Version 1.0
 */
public class T2_7 {

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 分别计算两个链表长度
        int lenA = getLen(headA);
        int lenB = getLen(headB);

        ListNode currA = headA;
        ListNode currB = headB;
        // 根据长度不同,做不同的处理
        if (lenA > lenB) {
            int difference = lenA - lenB;
            // A移动这么长
            while (difference > 0) {
                currA = currA.next;
                difference --;
            }
        } else if (lenA < lenB) {
            int difference = lenB - lenA;
            // B移动这么长
            while (difference > 0) {
                currB = currB.next;
                difference --;
            }
        }
        // 长度相同则不需要处理
        // A、B同时移动,直到相遇,或者到了最后
        while (currA != null && currB != null) {
            // 判断是否相遇
            if (currA == currB) {
                return currA;
            }
            currA = currA.next;
            currB = currB.next;
        }
        // 如果退出循环了,就说明没有相遇
        return null;
    }

    /**
     * 计算链表长度
     *
     * @param head
     * @return
     */
    private int getLen(ListNode head) {
        ListNode curr = head;
        int len = 0;
        while (curr != null) {
            len ++;
            curr = curr.next;
        }
        return len;
    }
}
2.思路

首先计算两个链表的长度,根据长度不同,让长的那个链表移动差值的长度,然后两个指针同时移动,然后就可以判断是否相遇了

8.环形链表 II

1.答案
package com.sunxiansheng.suanfa.daimasuixianglu;

/**
 * Description: 142. 环形链表 II
 *
 * @Author sun
 * @Create 2024/11/27 10:28
 * @Version 1.0
 */
public class T2_8 {

    public ListNode detectCycle(ListNode head) {
        // 快慢指针
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            // 快指针移动两步,慢指针移动一步
            fast = fast.next.next;
            slow = slow.next;
            // 判断是否相遇
            if (fast == slow) {
                // 这样只能代表有环,让slow指向head,然后再一起走,再次相遇即是相遇起始节点
                slow = head;
                while (slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return fast;
            }
        }
        return null;
    }
}
2.思路

使用快慢指针,只要相遇了,就说明一定有环,但是如果想要找到环的起始点,需要让slow指向head,然后一起移动,直至相遇


网站公告

今日签到

点亮在社区的每一天
去签到