• LeetCode链表专题


    update:

    首先链表题,说到双指针那就应该是ListNode类型的,即不是考虑链链只需考虑节点。

    然后就是一定会有head赋值发生。ListNode a =head之类的

    还有就是节点判空是需要考虑的

    一般需要while循环,while后面括号里是循环条件,一般是某位移节点不为空或某节点不等于某节点

    红色是依然没有做出来的


    141. 环形链表

    public boolean hasCycle(ListNode head) {
        if(head==null||head.next==null)
            return false;
            ListNode ptA = head;
            ListNode ptB = head.next;//必须加,不然while进不去
            while(ptA!=ptB){//while是满足执行,不满足才跳出
                if(ptB==null||ptB.next==null)
                return false;//快的先到null就直接无环
    
                ptA = ptA.next;
                ptB = ptB.next.next;
            }
            return true;//while不满足即a和b相等了则有环
        }
    }

    按照CS-Notes仓库的leetCode 题解来刷题

    https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E9%93%BE%E8%A1%A8.md

    1. 找出两个链表的交点

    160. Intersection of Two Linked Lists (Easy)

    https://leetcode-cn.com/problems/intersection-of-two-linked-lists/description/

    应该要知道一旦相交则后面的都是相同的。因为相交而拥有同一个Node,该Node只会指向唯一后继结点,则又是同一个Node。不会出现又分开的情况!!!

    notes上分析和解法说的都很详细,就是a+c+b=b+c+a双指针法

    即A先走自己的,走完了去走B的,B也先走自己的,走完了去走A的,这个过程中相遇的点就是交点。

    /**
     * Definition for singly-linked list.
     * public class ListNode {//这是结点的结构
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode ptA=headA;//因为后面headA还有用所以不能破坏了 让替身去干
            ListNode ptB=headB;
            while(ptA!=ptB){  //有相交点则在交点出退出循环  这里是因为pt是更新的值来做判断,不一定是A和B的next
               ptA= ptA==null?headB:ptA.next;//后面是一个三目判断 注意是ptA.next进行迭代 因为headA还有用不能被破坏所以找一个ptA=headA来赋值
               ptB= ptB==null?headA:ptB.next;
            }
            return ptA;
        }
    }

    详细分析:https://www.cnblogs.com/Yunus-ustb/p/12896382.html

    notes上也分析地很详细了。

    -----------------------------------------------------------入门有点困难,学习一下数据结构再来刷-------------------------------

    2. 链表反转(头插法插入的顺序是反着的,s.next=l.next意思是s指向l的下一个)

    206. Reverse Linked List (Easy)

    https://leetcode-cn.com/problems/reverse-linked-list/

     反转输出即在head前面插入值,但不是插进原链表

    要新建一个链表输出,然后是原链表从第一个头head插进新链表,后面的也是依次头插进head前面。则5就在新链表的头结点的下一个上了。。。。

    原链表插,则是原链表.next开头!!

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode reverseList(ListNode head) {
            ListNode dummy = new ListNode(-1);//哑结点 最后输出的链表
            while(head!=null){
                ListNode next = head.next;//让head.next用next暂存
                head.next = dummy.next;//
                dummy.next = head;//这两步是头插法,在dummy与dummy.next之间插入了head节点   随后第二步是在dummy与dummy指向的head中间插head.next
    由此实现反转 搞不明白去研究头插法就明白了
    head
    = next;//而head现在是head.next了 } return dummy.next; } }

    搞明白是谁头插谁啊输出的是什么很重要。。。

    链表的头插法:

     s.next=l.next;//这是s指向l的下一个!!!我想成了s的下一个指向l的下一个emmmmm

    l.next=s;//这是l指向s!!!不是l的下一个指向s!!!!

    node->next = head->next; // 将头指针所指向的下一个结点的地址,赋给新创建结点的next
    head->next = node; // 将新创建的结点的地址赋给头指针的下一个结点

    1. 头插法插入的数据与插入的顺序相反;(插入两个,第二个插的在第一个前面,所以实现了反转
    2. 尾插法插入的数据与插入顺序相同

    链表的尾插法:

    end.next=node;//end指向新的node

    end=node;//node成为end


    (对于链表的题目,首先可以考虑一下是不是需要用到 dummy 哑节点,用了哑节点之后题目会不会好做一点。 或者,考虑一下 curr工作指针。这都是思路的来源。

    其次,链表的题目跟树的题目一样,都有一个指向另一个节点的指针。所以我们想一想题目是不是可以通过递归去做。这样避免了使用迭代的方式,减少自己的思考难度。)

    递归的代码看着少其实很难写出来顺序。。

    3. 归并两个有序的链表

    21. Merge Two Sorted Lists (Easy)

    https://leetcode-cn.com/problems/merge-two-sorted-lists/submissions/

    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if (l1 == null) return l2;
            if (l2 == null) return l1;
            ListNode dummy = new ListNode(-1);
            ListNode cur = dummy;//工作指针必须用了,如果还用dummy是无法输出多个值的
         while(l1!=null&l2!=null){//循环条件看输入
             if(l1.val<l2.val){
                 cur.next=l1;
                 l1=l1.next;
             }else{
                 cur.next=l2;
                 l2=l2.next;
             }
             cur=cur.next;//这里卡住了。。。没想到应该要用一个dummy的工作指针
         }
         cur.next= l1==null?l2:l1;
         return dummy.next;
        }
    }

    搞清dummy和cur,像206就可以只用dummy因为是头插,但该题需要指针遍历后赋值。

    其实搞不太清。。。我好晕。。所以就都用cur去工作吧!!!把206也加入了cur...(0924清楚了)

    4. 从有序链表中删除重复节点

    83. Remove Duplicates from Sorted List (Easy)

    Leetcode / 力扣

    Given 1->1->2, return 1->2.
    Given 1->1->2->3->3, return 1->2->3.

    这题用不着哑结点啊直接在输入链表上操作就行啊...删除当前链表上的重复点。。。

    把head赋值给cur。。。

    class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if(head == null)return null;
            ListNode cur = head;
            while(cur!=null && cur.next != null){//指针代替head做判断了
                 if(cur.val==cur.next.val){
                     cur.next = cur.next.next;
                 }else{
                     cur=cur.next;//0924这里卡。。。
                }
                //不用管cur=cur.next了前面已经更新过了
             }
             return head;
         }
    }

     找出固定关系,递推就是。。

    我感觉我脑子在冒烟了。。。我一开始用dummy和cur,那样cri=cur.next更新只能移一位,解决不了三个相等值。原来是得判断cur位移cur才行。

    感觉明天就会忘记可怎么办。。。

    还有notes上一直用的递归虽然能看懂但我写不出来。。但是真的要学会递归啊!(回头秋招也得学!)

    5. 删除链表的倒数第 n 个节点

    19. Remove Nth Node From End of List (Medium)

    Leetcode / 力扣

    Given linked list: 1->2->3->4->5, and n = 2.
    After removing the second node from the end, the linked list becomes 1->2->3->5.

    好晕 这里又是用dummy.next=head 而不是cur。。吐血。。。(可以不用0924)

    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummy = new ListNode(-1);
            ListNode cur = dummy;
            //dummy.next=head;//用的链表 dummy指向head?
    cur.next=head;//也可以
    ListNode p
    = head; int length = 0; int removeLength = 0; while(p!=null){ length++; p=p.next; }//得到head链表的长度!!!! removeLength=length-n; while (removeLength > 0) { removeLength--; cur = cur.next; }//第一阶段得到前面相同的结点们 cur.next = cur.next.next;//删除点改指向 return dummy.next;//这就是dummy.next=head的好处 后面就不用管了都(这里想破头) } }

    我怎么看完答案理解了思路也写不出正确的代码

    coding能力太弱了。。。

    0924三要素:

            ListNode dummy = new ListNode(-1);

            ListNode cur = dummy;

            cur.next = head;

    6. 交换链表中的相邻结点(设一个start节点一个end节点)

    24. Swap Nodes in Pairs (Medium)

    Leetcode / 力扣

    Given 1->2->3->4, you should return the list as 2->1->4->3.

    题目要求:不能修改结点的 val 值,O(1) 空间复杂度。

    class Solution {
        public ListNode swapPairs(ListNode head) {
            ListNode dummy= new ListNode(0);
            ListNode cur= dummy;
          cur.next=head;
    while(cur.next != null && cur.next.next != null) {//得让指针来循环 即head和head.next // 这里的两个新建节点 start和 end有助于理解 ListNode start = cur.next; ListNode end = cur.next.next;
    // 让 cur指向两个节点中的后一个( end反转之后就是第一个) cur.next = end; // 进行反转 start.next = end.next; end.next = start;//这两步不能反 cur.next=end放这俩前面后面都可以
    // 此时,第一个 start反转之后就变成最后一个,此时让cur等于它 cur= start; } return dummy.next; } }

    搞清楚固定关系,才能从容应对循环,同题4,while出来后直接return。(用head换前三个没用的,要弄出关系好循环。。。

    以后也首先考虑指针cur代替head循环。。

    虽然没一题会的但往下走吧。。。。

    7. 链表求和(用两个stack实现每个位上的值相加,且设置一个进位数)

    445. Add Two Numbers II (Medium)

    Leetcode / 力扣

    Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
    Output: 7 -> 8 -> 0 -> 7

    题目要求:不能修改原始链表。

    class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            Stack<Integer> s1 = new Stack<>();//栈是这样定义的
            Stack<Integer> s2 = new Stack<>();
    
            while(l1 != null) {
                s1.push(l1.val);
                l1 = l1.next;
            }//实现把链表全部放入栈中
            while(l2 != null) {
                s2.push(l2.val);
                l2 = l2.next;
            }
    
        ListNode dummy = new ListNode(-1);
        int carry = 0;
        while (!s1.isEmpty() || !s2.isEmpty() || carry != 0) {//要满足两栈都空且进位为0才能退出循环
            int x = s1.isEmpty() ? 0 : s1.pop();
            int y = s2.isEmpty() ? 0 : s2.pop();
            int sum = x + y + carry;
            ListNode node = new ListNode(sum % 10);//余数写在该位结点上!!!!!
            node.next = dummy.next;//每一位余数来头插(插在dummy和它的next中间),后一位余数就插在在前一位前面
            dummy.next = node;
            carry = sum / 10;//进位给下两个xy加上
        }
        return dummy.next;
    }
    }

    给我整麻木了已经。。。

    0924:这题不要cur也行,要一个也行,之前是习惯性不要,现在习惯性要一个了。头插法啊又是!!因为是从后面算起,反着插入的!!!

    8. 回文链表

    234. Palindrome Linked List (Easy)

    Leetcode / 力扣

    题目要求:以 O(1) 的空间复杂度来求解。

    切成两半,把后半段反转,然后比较两半是否相等。

    难以置信我居然还没搞懂i++和++i (若是只涉及i的更新,则两个都是自增;但若涉及式子的值,则需要注意了,i++第一下还是等于原来i的值的

    1.a = i++; 等校为
      a = i;(a=1)
      i = i + 1;
    2.a = ++i; 等校为
      i = i + 1;
      a = i;(a=2)

    class Solution {
        public boolean isPalindrome(ListNode head) {
            if (head == null || head.next == null) {
                return true;
            }
            ArrayList<Integer> list = new ArrayList<>();
            ListNode p = head;
            while(p!=null){
                list.add(p.val);
                p = p.next;
            }
            int start = 0;//头指针
            int end = list.size()-1;//尾指针  求链表的长度不行。
            while (start <= end) {
                if (!list.get(start).equals(list.get(end))) {//好像while里面的if总是写非正常条件
                    return false;
                }else{
                start++;//只看i自增没用式子的值 所以++在前在后都可 都会自增
                end--;
                }
            }//循环结束
            return true;
        }
    }

    9. 分隔链表

    725. Split Linked List in Parts(Medium)

    Leetcode / 力扣

    Input:
    root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
    Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
    class Solution {
        public ListNode[] splitListToParts(ListNode root, int k) {
            ListNode p = root;
            ListNode[] res = new ListNode[k];
            int length = 0;
            while(p!=null){
                length++;
                p=p.next;
            }
            ListNode cur = root;
    
        if(k>=length){
                //错了res = root;
                //错了return res;
                //应该是把结点们一个一个按索引存入
            for (int i = 0; i < length; i++) {//用length不能用k!!!!
             res[i] = new ListNode(cur.val);//8个索引
            cur = cur.next;
             }
        } else{//必须分情况
            int a = length%k;//8%3=2 则前a段要加1
            int b = length/k;//8/3=2 每段基数是多少 btw除以会从少取整
            //不会了。。。
    
            cur = root;//索引0处
            for (int i = 0; cur != null && i < k; i++) {//k=3
            res[i] = cur;//0 1 2这三组3个索引
            int curSize = b + (a-- > 0 ? 1 : 0);//3,3,2共8  这里a--是取值!!分隔且前面大就必须做这个判断!!!
            for (int j = 0; j < curSize - 1; j++) {
                cur = cur.next;//指针后移 循环最后移停到索引2处 索引5处 索引7处
                 }
            ListNode next = cur.next;//先给他存起来下一段的开始点
            cur.next = null;//截断这一段的结束点
            cur = next;//后移为新的开始 索引3处 索引6处
             }    
          }return res;
     }
    }

    好难写出。。。。

    3个要点:1.分k大于等于length和小于length两种情况

    2.小于时分k段 还得考虑每段基数是多少 前多少段要加1

    3.小于时如何产生每一段 通过截断和开始

    0924我下意识反应用queue...但是看答案queue好像更复杂了没人用这个解题

    Queue<Integer> queue = new LinkedList<Integer>();

    queue 为啥是new LickedList?

    10. 链表元素按奇偶聚集

    328. Odd Even Linked List (Medium)

    Leetcode / 力扣

    Example:
    Given 1->2->3->4->5->NULL,
    return 1->3->5->2->4->NULL.

    拿到题目之后,第一时间想到的是把链表分成两条一条奇数,一条偶数,这应该算是最朴素的想法了然后把它们连起来

    (没想到。。。我想成第6题那样找关系了。。。)

    第一步:头节点肯定是奇数节点,这毋庸置疑。所以odd = head,把头节点拿出来当奇数链表的头节点
    第二步:头节点的下一个节点肯定是偶数节点了,所以even = head.next把 head.next拿出来当偶数链表的头节点,
    这里要注意了,一定要把偶数链表的头节点单独拿出来,这里我后面详细说
    第三步:找出关系式,奇数链表的下下个节点才会是奇数节点,同样偶数节点的下下个节点才会是偶数节点 odd.next
    = odd.next.next even.next = even.next.next PS:之前做链表题目的时候,.next我经常搞混,但做得多了,我总结了一条经验 =’左边的.next一般指的是该节点中存的next(链表节点包括两个部分组成,一个是val,一个是next用于指向下一个部分的),
    而右边的.next一般来讲是指的指向的某个具体节点(0924终于在头插法那儿记住了这个)
    第四步:找循环终止的条件,什么时候我们可以把所有的节点全部取完? 当odd.next
    == None or even.next == None时,代表奇数或者偶数已经全部取完了,剩下的一个会根据它是奇数还是偶数填充到对应链表中 所以循环条件为 while odd.next and even.next: odd和even链表的头节点进一位(扮演迭代器,这里需要看图才理解。。。我想成到even上了,其实不会,因为第三步已经跳过了!!! odd,even = odd.next,even.next
    第五步:我第二步说的,为什么要把头节点拿出来,因为奇数链表的尾节点要跟偶数链表的头节点相连,从而形成完整的链表 odd.next
    = even_head 作者:zbzzbz 链接:https://leetcode-cn.com/problems/odd-even-linked-list/solution/zui-po-su-de-xiang-fa-dai-ma-zhu-shi-fei-chang-xia/

    最终return的是head!!!!!!看图就很容易明白。看代码半天不明白为啥head啥也没做就返回head....看图就知道,由于odd和even已经让head后面已经全变化了。可见链表之间的赋值是真的把自己送给它了,然后他就能修改链表了。

     我居然做了三天的题才反应过来这一点。。。(0924一天反应过来了。。。之前dummy和cur就是这样的关系)

    class Solution {
        public ListNode oddEvenList(ListNode head) {
            if(head==null)return null;
            ListNode odd = head;//奇数链表头节点
            ListNode even = head.next;
            ListNode evenHead = even;//拿出来之后做连接用 存起来
    
            while(odd.next!=null&&even.next!=null){
                odd.next=odd.next.next;
                even.next=even.next.next;
                odd=odd.next;
                even=even.next;
            }
            odd.next=evenHead;
            return head;
        }
    }

     0924:看看代码其实这题还挺简单的,可是我看到说在原链表上原地完成就没有了思路。

    应该想到定义odd节点与even节点的

  • 相关阅读:
    桥接模式(从多个角度对实现进行分类)
    单例模式
    组合模式(解决 树形 ,局部与整体关系)
    备忘录模式
    适配器模式
    状态模式
    StartUML建模及生成java实体代码
    JPA删除接口报错 : org.hibernate.LazyInitializationException: failed to lazily initialize a collection, could not initialize proxy
    idea创建的gradle项目没有src目录怎么办?
    idea spring源码 gradle编译失败问题
  • 原文地址:https://www.cnblogs.com/gezi1007/p/12913317.html
Copyright © 2020-2023  润新知