Loading

代码随想录(2)-链表

题单

203.移除链表元素
707.设计链表(middle)
206.反转链表
24.两两交换链表中的节点(middle)
19.删除链表的倒数第 N 个结点(middle)
面试题 02.07. 链表相交
142.环形链表II(middle)

链表节点代码

public class ListNode {
    // 结点的值
    int val;

    // 下一个结点
    ListNode next;

    // 节点的构造函数(无参)
    public ListNode() {
    }

    // 节点的构造函数(有一个参数)
    public ListNode(int val) {
        this.val = val;
    }

    // 节点的构造函数(有两个参数)
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

203.移除链表元素

题意:删除链表中等于给定值 val 的所有节点。

示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]

递归方法(我的思路)

    public ListNode removeElements(ListNode head, int val) {
        // 如果当前节点为null,直接返回
        if (head == null) {
            return head;
        }
        // 遍历更新子节点
        head.next = removeElements(head.next, val);
        // 如果当前元素为该元素,则返回子节点;否则,返回
        if (head.val == val) {
            return head.next;
        }else{
            return head;
        }
    }

707.设计链表(middle)

在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

思路:
这题没啥好讲的,就是按照要求认真实现。

class MyLinkedList {
    ListNode head;

    public MyLinkedList() {

    }
    public int get(int index) {
        ListNode temp = head;
        for( int i = 0 ; temp != null; temp = temp.next,i++){
            if(i == index){
                return temp.val;
            }
        }
        return -1;
    }
    public void addAtHead(int val) {
        ListNode temp = head;
        head = new ListNode(val);
        head.next = temp;
    }
    public void addAtTail(int val) {
        if( head == null ){
            head = new ListNode(val);
            return;
        }
        ListNode temp = head;
        while( temp.next != null ){
            temp = temp.next;
        }
        temp.next = new ListNode(val);
    }
    public void addAtIndex(int index, int val) {
        if( index <= 0){
            addAtHead(val);
            return;
        }
        ListNode temp = head;
        ListNode pre = null;
        for( int i = 0 ; temp != null; pre = temp, temp = temp.next, i++){
            if(i == index){
                pre.next = new ListNode(val, temp);
                return;
            }
        }
        if(pre != null && pre.next == null){
            pre.next = new ListNode(val);
        }
    }
    public void deleteAtIndex(int index) {
        ListNode temp = head;
        ListNode pre = null;
        for( int i = 0 ; temp != null; pre = temp, temp = temp.next,i++){
            if(i == index){
                if(pre == null){
                    head = temp.next;
                }else{
                    pre.next = temp.next;
                }
                return;
            }
        }
    }
}

206.反转链表

题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

思路:遍历所有元素,修改节点指针为前一个节点

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode reverseHead = null;
        while( head != null ){
            ListNode next = head.next;
            head.next = reverseHead;
            reverseHead = head;
            head = next;
        }
        return reverseHead;
    }
}

24.两两交换链表中的节点(middle)

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

思路:

当前元素且子元素不为空。
--保存子元素的next为temp,以供当前元素更新
--设置子元素的next为当前元素
--设置前一个节点的next为子元素(如果前一个为空,则设子元素为head)
--更新前一个为当前节点
--设置当前节点的next为temp
--更新当前节点为temp

    public ListNode swapPairs(ListNode head) {
        ListNode pre = null;
        ListNode node = head;
        while(node!= null && node.next != null){
            // --保存子元素的next为temp,以供当前元素更新
            ListNode temp = node.next.next;
            // --设置子元素的next为当前元素
            node.next.next = node;
            // --设置前一个节点的next为子元素(如果前一个为空,则设子元素为head)
            if( pre == null){
                head = node.next;
            }else{
                pre.next = node.next;
            }
            // --更新前一个为当前节点
            pre = node;
            // --设置当前节点的next为temp
            node.next = temp;
            // --更新当前节点为temp
            node = temp;
        }
        return head;
    }

参考代码后,精简了循环体中的判断语句。

    public ListNode swapPairs(ListNode head) {
        ListNode pre = new ListNode();
        ListNode preHead = pre;
        pre.next = head;
        while(head!= null && head.next != null){
            // --保存子元素的next为temp,以供当前元素更新
            ListNode temp = head.next.next;
            // --设置子元素的next为当前元素
            head.next.next = head;
            // --设置前一个节点的next为子元素
            pre.next = head.next;
            // --更新前一个为当前节点
            pre = head;
            // --设置当前节点的next为temp
            head.next = temp;
            // --更新当前节点为temp
            head = temp;
        }
        return preHead.next;
    }

19.删除链表的倒数第 N 个结点(middle)

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?

思路:
暴力做法,可以扫描两遍,第一遍获得链表长度,然后判断处理倒数第n个位置。
题目提示使用一次扫描,那么可以尝试保存遍历元素前第n个元素,一次遍历即可获取最后一个元素的前n个元素信息。

设置虚拟头节点preHead=pre
遍历链表,当前元素不为空
--若遍历元素个数大于n,pre更新为下一个
删除pre下一个
返回虚拟头节点的下一个

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre = new ListNode(-1,head);
        ListNode preHead = pre;

        int counter = 0;
        while(head != null){
            if( ++counter > n){
                pre = pre.next;
            }
            head = head.next;
        }
        pre.next = pre.next.next;
        return preHead.next;
    }

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

注意,函数返回结果后,链表必须 保持其原始结构 。

思路

获取两个字符长度
判断长串a和短串b
将a指针移动到倒数b长度位置
同时遍历a、b
-- 若二者相等则直接返回该节点
返回null

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int a = 0,b = 0;
        for(ListNode temp = headA; temp != null; temp = temp.next ) a++;
        for(ListNode temp = headB; temp != null; temp = temp.next )  b++;

        ListNode curLong = headA;
        ListNode curShort = headB;
        int gap = a - b;
        if( a < b ){
            curLong = headB;
            curShort = headA;
            gap = -gap;
        }

        while( gap-- > 0 ){
            curLong = curLong.next;
        }

        while( curLong != null ){
            if( curLong == curShort ){
                return curLong;
            }
            curLong = curLong.next;
            curShort = curShort.next;
        }
        return null;
    }

142.环形链表II(middle)

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

思路:
其实没什么思路,看了题解的提示,采用快慢指针法。

快指针fast走两步,慢指针走一步
遍历快指针
-- 如果等于快指针,返回
返回null

上面遗漏考虑了,只返回了相遇节点,没有考虑入环节点。
如何查找入环点,参考了题解。

快慢指针法

    public ListNode detectCycle(ListNode head) {
        // >快指针fast走两步,慢指针走一步
        ListNode fast = head, slow = head;
        // 遍历快指针
        while( fast != null ){
            slow = slow.next;
            if(fast.next == null) return null;
            else fast = fast.next.next;
            if(fast == slow ){
                for(; slow != head; slow = slow.next, head = head.next );
                return slow;
            }
        }
        return null;
    }

总结

链表的虚拟头节点可以很好的避免头节点的前一个节点的判断。
快慢指针法 很巧妙。

posted @ 2023-01-31 20:57  丘野  阅读(16)  评论(0编辑  收藏  举报