代码随想录Day3,4 链表相关题目

引言

补博客打卡阶段,之前做了笔记,但没有发布博客,借着回顾把这些笔记都整理发布吧。
一些做题思路(很简略)和心得

一、移出链表元素

203 移出链表元素

思路

链表操作的两种方式:

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点在进行删除操作。
    本题采用设置一个虚拟头结点进行操作

代码

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode curr = dummyHead;
        while (curr != null && curr.next != null){
            if (curr.next.val == val){
                curr.next = curr.next.next;
            }else{
                curr = curr.next;
            }
        }
        return dummyHead.next;
    }
}

注意点

最后的返回值要注意,是虚拟头结点的下一个节点,因为会发生把头结点删除的情况,如果返回头结点,那么返回的是删除的头结点。

二、设计链表

链接: 707.设计链表

思路

【细节】本题的关键,我认为有两个。一个非常重要的是对于虚拟头节点的理解,另一个是for循环过程中, i 和 index之间关系中是否带有等号
虚拟头节点是在定义链表过程中的一个虚拟项,并不真实存在于链表中,并不代表着该链表Index的第一项即 index=0 是定义的虚拟头节点 head。
其实对于这两点,主要是在草稿上面举例尝试一下,用脑子一般会想岔

代码

class ListNode {
    int val;
    ListNode next;
    ListNode(){}
    ListNode(int val) {
        this.val=val;
    }
}

class MyLinkedList {

    int size;
    ListNode head;


    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }
    
    public int get(int index) {
        if (index < 0 || index >= size){
            return -1;
        }
        ListNode current = head;
        for(int i = 0;i <= index;i++){
            current = current.next;
        }
        return current.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    public void addAtIndex(int index, int val) {
        if (index > size){
            return;
        } ·
        if (index < 0){
            // index小于0时的处理方法 和 index=0时的处理方法相同,所以此种情况直接将index赋值为0
            index = 0;
        }
        size++;
        //找到要插入节点的前驱
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = pred.next;
        pred.next = toAdd;
        
    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size){
            return;
        }
        size--;
        // 一定要单独考虑头节点的情况。
        // 头节点和一般情况不同,删除头节点时,是直接将头节点设置成了虚拟头节点
        // 所以这个时候就不能简单的只是删除,也要改变虚拟头节点的值
        if (index == 0){
            head = head.next;
            return;
        }
        ListNode current = head;
        for (int i = 0;i < index;i++){
            current = current.next;
        }
        current.next = current.next.next;
    }
}

心得

可以称之为错误集锦,总会出现一些不可思议的错误

1.为了不影响阅读性,我将力扣中的一段代码剪贴进微信,在调试完其他代码将该代码粘贴至原位置时疯狂报错。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在这里插入图片描述
原因是:在粘贴进微信后,微信改变了空格的编码方式。

解决方法:在力扣中注释就好,不要剪切到其他文件中。

2.在删除和增加节点时,一定要记得在链表的size里+1

3.关于头节点,不一定能像一般节点一样处理,一定要思考一遍,如果能和一般情况合并再合并。

4.删除头结点时很特殊。头节点和一般情况不同,删除头节点时,是直接将头节点设置成了虚拟头节点,所以这个时候就不能简单的只是删除,也要改变虚拟头节点的值

三、反转链表

链接: 206.反转链表

思路

双指针法,cur.next = pre 然后同时移动,注意边界条件

代码

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

注意,为了更符合逻辑一点,将pre 赋值给head,最后返回head

四、两两交换链表中的节点

链接: 24.两两交换链表中的节点

思路

思路很简单,就是两个节点交换,需要考虑两个节点前面和后面的节点
很容易绕晕,多设几个变量也没关系,清楚明了是重点、

代码

// 自己的方法 容易绕晕
class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null)   return null;
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        while (cur != null && cur.next != null){
            ListNode temp = cur.next;
            pre.next = temp;
            cur.next = cur.next.next;
            temp.next = cur;
            
            pre = pre.next.next;
            //这步我出错了,我写成了cur = cur.next.next 
            //cur其实已经换过位置了
            cur = cur.next;
        }
        return dummyHead.next;
    }
}

// 代码随想录的解法
class Solution {
  public ListNode swapPairs(ListNode head) {
        ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
        dumyhead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode cur = dumyhead;
        ListNode temp; // 临时节点,保存两个节点后面的节点
        ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
        ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
        while (cur.next != null && cur.next.next != null) {
            temp = cur.next.next.next;
            firstnode = cur.next;
            secondnode = cur.next.next;
            cur.next = secondnode;       // 步骤一
            secondnode.next = firstnode; // 步骤二
            firstnode.next = temp;      // 步骤三
            cur = firstnode; // cur移动,准备下一轮交换
        }
        return dumyhead.next;  
    }
}

代码随想录中的方法,第一个节点firstnode,第二个节点secondnode,两个节点前面的节点cur,两个节点后面的节点temp
思路很清晰

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

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

思路

本题的思路很巧妙,运用双指针,设置快指针和慢指针,快指针先进行n个节点的遍历,然后快慢两个指针同时继续遍历
当快指针到达null时,慢指针正好到达需要删除节点的前一个节点 slowIndex.next = slowIndex.next.next;
方法是这个方法,具体细节和边界条件,需要找个具体的链表模拟一下。

代码

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 1.定义一个虚拟头节点
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        // 2.将fast节点先移动到对应位置
        ListNode fast = dummyHead;
        ListNode slow = dummyHead;
        n = n + 1;
        while (n-- > 0 && fast != null){
            fast = fast.next;
        }
        // 3.fast与slow同时移动,当fast = null时,slow在对应位置
        while (fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        // 删除
        slow.next = slow.next.next;
        
        // [1] 删除1 的例子中,head始终指向头节点,即使利用虚拟头节点删除后也是指向的被删除的头节点,要注意
        // return head;  错误
        return dummyHead.next;
    }
}

关于涉及到链表删除的操作,不管是删除什么元素,要设置虚拟头节点,并且在最后 return dummyHead.next
目的是为了防止符合条件的元素是头节点,这样在返回时就会出错。

六、链表相交

链接: 160.链表相交

思路

本题是求链表相交 注意理解题目,一旦相交了,后面所有的节点都重合了。
所以对于长度较长的那一方,我们需要遍历到长度差的节点,两个链表在同一起点(末尾位置对齐)

代码

自己卡顿的错误做法:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lenA = 1;
        int lenB = 1;
        ListNode curr = headA;
        while (curr != 0){
            lenA++;
            curr = curr.next;
        }
        ListNode curr = headB;
        while (curr != 0){
            lenB++;
            curr = curr.next;
        }
        // 知道应该交换链表A和链表B,之后会很复杂
        if (lenB > lenA){

        }
    }
}

一开始给链表A和B分别设定长度 和 指向头节点的变量,之后交换时,只用交换指向头节点的变量即可。


public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lenA = 0;
        int lenB = 0;
        ListNode curA = headA;
        ListNode curB = headB;
        while (curA != null){
            curA = curA.next;
            lenA++;
        }
        while (curB != null){
            curB = curB.next;
            lenB++;
        }
        curA = headA;
        curB = headB;
        if (lenB > lenA){
            int tempN;
            tempN = lenB;
            lenB = lenA;
            lenA = tempN;

            ListNode tempList;
            tempList = curB;
            curB = curA;
            curA = tempList;
        }
        int gap = lenA - lenB;
        while (gap-- > 0 && curA != null){
            curA = curA.next;
        }
        while (curA != null){
            if(curA == curB) return curA;
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }
}

很顺利,带着脑子做,就很赞,加油!

七、环状链表II

思路

类似数学问题
数学问题与双指针的结合
设置快慢两个指针,快指针每次遍历跳两个节点,慢指针一个节点,如果链表有环,迟早能追上。
记录下快慢指针相等的节点,经过一系列的数学公式
在这里插入图片描述
【图片转载至代码随想录】
x = z
设置一个新的指针index1 = head ; index2 = slow;以一个节点为单位向前遍历,相等的节点,即为我们所求的链表入环的节点

代码

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){
                ListNode index1 = head;
                ListNode index2 = slow;
                // if 和 while 要想一想
                // 既然有fast = slow了,那一定存在index
                while (index1 != index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index2;
        }  
    }
        return null;
  } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值