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节点的

原文地址:https://www.cnblogs.com/gezi1007/p/12913317.html