LeetCode刷题第七周

LeetCode刷题第七周_第1张图片

武功山好漂亮呀~

文章目录

  • 链表专题
    • 简单
      • 876. 链表的中间结点
      • 1290. 二进制链表转整数
      • 剑指 Offer 06. 从尾到头打印链表
      • 剑指 Offer 18. 删除链表的节点
      • 剑指 Offer 22. 链表中倒数第k个节点
      • 剑指 Offer 24. 反转链表
      • 剑指 Offer 52. 两个链表的第一个公共节点
      • 面试题 02.01. 移除重复节点
      • 面试题 02.02. 返回倒数第 k 个节点
      • 面试题 02.03. 删除中间节点
      • 面试题 02.04. 分割链表
      • 面试题 02.06. 回文链表
      • 面试题 02.07. 链表相交
    • 中等
      • 445. 两数相加 II
      • 707. 设计链表
      • 面试题 02.05. 链表求和
      • 面试题 02.08. 环路检测


链表专题

LeetCode刷题第七周_第2张图片

简单

876. 链表的中间结点

题目链接: 点击跳转至本题

题目大意:
给定一个非空单链表,要求返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

解题思路:二倍追击
本题仍是二倍追击问题,这里要求的中间点是右边的,之前在234. 回文链表中写过求中间点左边的。现在来比较一下两者的差异:

  • 求中间点靠左:
    while(fast.next != null && fast.next.next != null)
  • 求中间点靠右:
    while(fast != null && fast.next != null)//相当于比上面的循环多走了一步

差异主要在while循环中的循环条件。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next; 
        }
        return slow;
    }
}

1290. 二进制链表转整数

题目链接: 点击跳转至本题

题目大意:
给定一个单链表的引用结点 head。此链表是一个整数数字的二进制表示形式。要求返回该链表所表示数字的 十进制值 。

解题思路:反转链表+模拟

二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个数乘以2的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。

根据这个特性,可以先将链表反转,然后从低位到高位方便操作。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    /** 二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个数乘以2的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。 **/
    public int getDecimalValue(ListNode head) {
        head = reverseList(head);
        int i = 0,result = 0;
        while(head != null){
            //<<左移运算符:n<< m  相当于n乘以2的m次方
            result += head.val * (1 << i++);
            head = head.next;
        }
        return result;
    }

    //反转链表
    private ListNode reverseList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

剑指 Offer 06. 从尾到头打印链表

题目链接: 点击跳转至本题

题目大意:输入一个链表的头节点,从尾到头反过来返回每个节点的值,要求用数组返回。

解题思路:数组赋值
先求出链表的长度length,然后直接定义一个长度为length的数组,倒序向数组中装载链表结点的val值。注意是从length-1到0。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        //求链表的长度lenth
        int length = 0;
        ListNode temp = head;
        while(temp != null){
            length++;
            temp = temp.next;
        }
        //倒序装入数组中
        int[] ans = new int[length];
        for(int i = length-1;i>=0;i--){
            ans[i] = head.val;
            head = head.next;
        }
        return ans;
    }
}

剑指 Offer 18. 删除链表的节点

题目链接: 点击跳转至本题

题目大意:
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
要求返回删除后的链表的头节点。

解题思路:
此题和上周的203.移除链表元素相同,先设置哨兵结点preHead方便结果的存储。定义pre1和pre2工作指针分别指向preHead和head。遍历过程中,使用将pre2与val值比较,如果相等则将pre1的下一个结点连接上pre2的下一个结点。否则pre1移动到ore2。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        //设置哨兵节点
        ListNode preHead = new ListNode(0);
        preHead.next = head;
        //pre1和pre2为工作指针
        ListNode pre1 = preHead;
        ListNode pre2 = head;
        
        while(pre2 != null){
            if(pre2.val == val){
                pre1.next = pre2.next;
            }else{
                pre1 = pre2;
            }
            pre2 = pre2.next;
        }
        return preHead.next;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

题目链接: 点击跳转至本题

题目大意:给定一个链表,输出该链表中倒数第k个结点。本题从1开始计数,即链表的尾结点是第1个结点。
LeetCode刷题第七周_第3张图片

解题思路:倒数第k个结点 == 下标为length-k的结点

本题与19. 删除链表的倒数第N个节点,有些类似,但是更简单,只需要记住倒数第k个结点 == 下标为length-k的结点即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //删除倒数第k个结点 == 下标为第length-k的结点
        //求链表长度length
        int length = 0;
        ListNode temp = head;
        while(temp != null){
            length++;
            temp = temp.next;
        }
        for(int i=0;i<length-k;i++){
            head = head.next;
        }
        return head;
    }
}

剑指 Offer 24. 反转链表

题目链接: 点击跳转至本题

题目大意:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
在这里插入图片描述

解题思路:反转链表

本题与206. 反转链表相同,经典题目了,整体思路是定义三个指针,pre指针始终指向新链表的表头,cur指针做为遍历的工作指针,temp指针始终指向cur结点的下一个结点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //定义pre结点始终指向新链表的头部
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            //temp结点始终存储cur结点的下一个结点
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

剑指 Offer 52. 两个链表的第一个公共节点

题目链接: 点击跳转至本题

题目大意:给定两个链表,要求返回它们的第一个公共节点。
LeetCode刷题第七周_第4张图片

解题思路:浪漫指针
本题与160. 相交链表相同,思路是定义两个指针,进行追击,追击方式是每当一个链表走到尽头时,就从对方链表的头节点开始走,直到相遇。

/**
 * 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) {
        //特判,如果是空链表就直接返回
        if(headA==null || headB==null){
            return null;
        }
        //走到尽头见不到你,就走过你来时的路,知道相遇才发现,你也走过我走过的路。
        ListNode curA = headA,curB = headB;
        while(curA != curB){
            curA = curA == null ? headB : curA.next;
            curB = curB == null ? headA : curB.next;
        }
        return curA;
    }
}

面试题 02.01. 移除重复节点

题目链接: 点击跳转至本题

题目大意:给定一未排序的链表,要求移除链表中的重复节点。

解题思路:set去重
从链表头开始遍历,依次放入set集合中,如果下一个结点的val值已经存在在set集合中,就将其移除掉。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        Set<Integer> set = new HashSet<>();
        ListNode cur = head;
        while(cur!=null && cur.next!=null){
            set.add(cur.val);
            if(set.contains(cur.next.val)){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        return head;
    }
}

面试题 02.02. 返回倒数第 k 个节点

题目链接: 点击跳转至本题

题目大意:给定一个链表,要求返回倒数第k个结点

解题思路:倒数第k个结点 == 下标为length-k的结点
与剑指 Offer 22. 链表中倒数第k个节点类似,只是返回的结果不同。牢记回倒数第k个结点 == 返回下标为第length-k的结点即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthToLast(ListNode head, int k) {
        //返回倒数第k个结点 == 返回下标为第length-k的结点
        int length = 0;
        ListNode temp = head;
        while(temp != null){
            length++;
            temp = temp.next;
        } 
        for(int i=0;i<length-k;i++){
            head = head.next;
        }
        return head.val;
    }
}

面试题 02.03. 删除中间节点

题目链接: 点击跳转至本题

题目大意:要求编写一个函数,可以删除某个链表中给定的(非末尾)节点。传入函数的参数为需要被删除的节点

解题思路:与下一个节点交换
本题与237. 删除链表中的节点相同。给定了需要被删除的结点,而不是前一个结点。此时需要将下一个结点的值赋值给当前节点,并将本节点的下一个节点 指向 下一个节点的指向。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        //将后一个节点的val赋给node
        node.val = node.next.val;
        //删除node后面的节点
        node.next = node.next.next;
    }
}

面试题 02.04. 分割链表

题目链接: 点击跳转至本题

题目大意:给定一个无序链表和一个定值x,返回一个新的链表,要求使得值小于x的元素位于大于等于x的元素前。

解题思路:哨兵节点
此题与86. 分隔链表相同。设置两个哨兵节点L和R分别表示记录值小于x和值大于等于x的链表,遍历时,将小于特定值x的节点链接到L链表上,将大于等于特定值x的节点链接到R链表上,最后将第二个链表链接到第一个链表上。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
        //给定一个无序链表和一个定值x,返回一个新的链表,要求使得值小于x的元素位于大于等于x的元素前
        //定义链表L记录小于x的值,R记录大于等于x的值
        ListNode L = new ListNode(0);
        ListNode R = new ListNode(0);
        ListNode curL = L,curR = R;
        while(head != null){
            if(head.val < x){
                curL.next = new ListNode(head.val);
                curL = curL.next;
            }else{
                curR.next = new ListNode(head.val);
                curR = curR.next;
            }
            head = head.next;
        }
        //拼接两段链表
        curL.next = R.next;
        return L.next;
    }
}

面试题 02.06. 回文链表

题目链接: 点击跳转至本题

题目大意:编写一个函数,检查输入的链表是否是回文的。

解题思路:反转链表+二倍追击
本题与 234. 回文链表相同。先将链表从中间分隔开,对后半段进行反转,然后将反转后的链表与前半段进行比较。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode temp = salfList(head);
        ListNode L= reversalList(temp);
        boolean flag = true;
        ListNode p1 = head,p2 = L;
        while(flag && p2!=null){
            if(p1.val != p2.val){
                flag = false; 
            }
            p1 = p1.next;
            p2 = p2.next;
        }
        return flag;
    }

    //反转链表
    private ListNode reversalList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
    //分割链表
    private ListNode salfList(ListNode head){
        ListNode slow = head,fast = head;
        while(fast != null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

面试题 02.07. 链表相交

题目链接: 点击跳转至本题

题目大意:给定两个链表,要求返回两个链表相交的起始节点,若无相交节点则返回null。

解题思路:浪漫指针
本题与160. 相交链表和剑指 Offer 52. 两个链表的第一个公共节点相同,思路仍是定义两个指针,进行追击,追击方式是每当一个链表走到尽头时,就从对方链表的头节点开始走,直到相遇。

/**
 * 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) {
        if(headA == null || headB == null){
            return null;
        }
        
        ListNode pA = headA,pB =headB;
        while(pA != pB){
            pA = pA != null ? pA.next:headB;
            pB = pB != null ? pB.next:headA;
        }
        return pA;
    }
}

中等

445. 两数相加 II

题目链接: 点击跳转至本题

题目大意:
给定两个正序非空链表,求出它们的和。假设这两个链表都不会以0开头。

解题思路:链表反转+模拟加法
可以对两链表进行反转,然后变成类似2. 两数相加的题目,反转链表可以参照206. 反转链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode L1 = reverseList(l1);
        ListNode L2 =  reverseList(l2);
        ListNode L3 =  new ListNode(1);
        //创建指针p,q,v分别指向链表L1,L2,L3
        ListNode p = L1, q = L2 ,v = L3;
        int carry = 0;
        while(p!=null || q!=null){
            int x = (p!=null) ? p.val:0;
            int y = (q!=null) ? q.val:0;
            int sum = x + y + carry;
            carry = sum/10;
            v.next = new ListNode(sum%10);
            v = v.next;
            if(p != null) p = p.next;
            if(q != null) q = q.next;
        }
        if(carry > 0){
            v.next = new ListNode(1);
        }
        //注意返回的结果是从L3的下一个节点开始的反转
        return reverseList(L3.next);
    }

    //反转链表
    private ListNode reverseList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

707. 设计链表

题目链接: 点击跳转至本题

题目大意:根据给出的方法名和参数,使用单链表或双链表完成链表的设计。

解题思路:单链表的基本增删查
这里使用单链表完成设计,我们的数据结构中需要多定义一个size表示链表的长度,注意添加时对其+1,删除时对其-1。

//定义节点
class ListNode{
    int val;
    ListNode next;
    ListNode(int x){
        val = x;
    }
}

class MyLinkedList {
    int size = 0;
    ListNode preHead = null;
    /** 在这儿初始化我们的数据结构. */
    public MyLinkedList() {
        preHead = new ListNode(0);
    }
    
    /** 根据index下标获得节点的val **/
    public int get(int index) {
        if(index < 0 || index >= size){
            return -1;
        }
        ListNode curr = preHead;
        for(int i = 0;i <= index;i++){
            curr = curr.next;
        }
        return curr.val;
    }
    
    /** 头添加 */
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    
    /** 尾添加. */
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    /** 根据index索引,添加val值. */
    public void addAtIndex(int index, int val) {
        if(index > size){
            return;
        }
        if(index < 0){
            index = 0;
        }
        size++;
        //将pre指向待添加节点的前一个节点
        ListNode pre = preHead;
        for(int i = 0;i < index;i++){
            pre =pre.next;
        }
        //进行添加操作
        ListNode toAdd = new ListNode(val);
        toAdd.next = pre.next;
        pre.next = toAdd;

    }
    
    /** 根据index索引删除节点. */
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size){
            return;
        }
        size--;
        ListNode pre = preHead;
        for(int i = 0;i < index;i++){
            pre = pre.next;
        }
        pre.next = pre.next.next;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

面试题 02.05. 链表求和

题目链接: 点击跳转至本题

题目大意:给定两个非空的链表用来表示两个非负的整数,各自的位数按照逆序存储在链表中(每个节点只能存储一位数字)。要求将这两个整数加起来,返回一个新的链表来表示它们的和。(说明:除了数字0之外,这两个整数都不会以0开头。)
在这里插入图片描述

解题思路:模拟
本题与2. 两数相加相同。额外创建l3链表用来存储结果,定义cur1,cur2,cur3作为三条链表的工作指针,定义carry作为进位变量(加法只会进1位或不进位),两链表同时从头遍历时,对应位相加的值对10取余作为carry进位变量,对10取模作为存储到l3的数据。待遍历结束,对carry 进行判断,若仍有进位则对cur3的下一个节点补1。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode l3 = new ListNode(0);
        //创建指针cur1,cur2,cur3分别指向三个链表
        ListNode cur1 = l1,cur2 = l2,cur3 = l3;
        //初始化进位遍历carry
        int carry = 0;
        while(cur1!=null || cur2!=null){
            int x = (cur1 != null) ? cur1.val:0;
            int y = (cur2 != null) ? cur2.val:0;
            int sum = x + y + carry;
            carry = sum/10;
            cur3.next = new ListNode(sum%10);
            cur3 = cur3.next;
            if(cur1 != null){
                cur1 = cur1.next;
            }
            if(cur2 != null){
                cur2 = cur2.next; 
            }
        }
        //遍历结束,进位仍然>0则追加一个值为1的新节点
        if(carry > 0){
            cur3.next = new ListNode(1);
        }
         return l3.next;
    }
}

面试题 02.08. 环路检测

题目链接: 点击跳转至本题

题目大意:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

解题思路:HashSet去重
本题与142. 环形链表 II相同。可以使用不可重复的集合HashSet来进行比较判断,当下一个将要添加到set的节点已经存在再set中的时候,返回下一个节点。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<ListNode>();
        ListNode node = head;
        while(node != null){
            if(set.contains(node)){
                return node;
            }
            set.add(node);
            node = node.next;
        }
        return null;
    }
}

你可能感兴趣的:(#,LeetCode刷题,LeetCode)