Leetcode——链表笔试题

Leetcode——链表笔试题

  • 1. Num237 删除链表中的节点
    • 1.1 题目描述
    • 1.2 思路
    • 1.3 代码
  • 2.Num83删除排序链表中的重复元素
    • 2.1 题目描述
    • 2.2 思路
  • 3.面试题 02.01. 移除重复节点(乱序)
    • 3.1 题目描述
    • 3.2 思路
  • 4.Num206反转链表
    • 4.1 题目描述
    • 4.2 思路
      • 4.2.1 方法1 创建新链表
      • 4.2.2 原地移动
  • 5.Num876链表的中间节点
    • 5.1 题目描述
    • 5.2 两种思路
  • 6.剑指 Offer 22. 链表中倒数第k个节点
    • 6.1 题目描述
    • 6.2 两种思路
  • 7.剑指 Offer II 027. 回文链表
    • 7.1 题目描述
    • 7.2 三种思路
  • 8.Num141环形链表
    • 8.1 题目描述
    • 8.2 思路
  • 9.Num21.合并两个有序链表
    • 9.1 题目描述
    • 9.2 思路

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/delete-node-in-a-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1. Num237 删除链表中的节点

1.1 题目描述

请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。

1.2 思路

1.先判断头节点是否为待删除的节点(用while删除前面所有值为val的节点)
2.删除完头节点之后,对链表判空
3.删除链表中间值为val的节点(注意创建新节点保存原链表中的节点)

1.3 代码

 public static ListNode removeElements(ListNode head, int val) {
         while (head.val==val){
             // 当头节点为待删除的节点时,删除头节点,直到头节点不为val
             head=head.next;
         }
         if(head==null){
             // 头节点删除完了之后,需要判空。因为有可能链表所有的节点都是待删除的节点
             return null;
         }else{
             // 此时链表中间的节点值为待删除的节点值
             ListNode prev=head;
             for (ListNode cur = prev.next; cur != null ; cur=cur.next) {
                 if(cur.val==val){
                     // 此时遍历到的节点为待删除的节点,此时需要判断是否为最后一个节点
                     if(cur.next==null){
                         prev.next=null;
                     }else{
                         prev.next=cur.next;
                         cur.next=null;
                     }
                 }else{
                     prev=prev.next;
                 }
             }
         }
        return head;
    }

完整测试+方法代码

package List;

/**
 * 203. 移除链表元素
 * 给你一个链表的头节点 head 和一个整数 val ,
 * 请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
 */
public class Num203_removeElements {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Num203_removeElements num203=new Num203_removeElements();
        ListNode newhead=num203.removeElements(head,6);
        System.out.println(newhead);
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(2);
        ListNode node3=new ListNode(6);
        ListNode node4=new ListNode(3);
        ListNode node5=new ListNode(4);
        ListNode node6=new ListNode(5);
        ListNode node7=new ListNode(6);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node6;
        node6.next=node7;
        return node1;
    }
    public static ListNode removeElements(ListNode head, int val) {
         while (head.val==val){
             // 当头节点为待删除的节点时,删除头节点,直到头节点不为val
             head=head.next;
         }
         if(head==null){
             // 头节点删除完了之后,需要判空。因为有可能链表所有的节点都是待删除的节点
             return null;
         }else{
             // 此时链表中间的节点值为待删除的节点值
             ListNode prev=head;
             for (ListNode cur = prev.next; cur != null ; cur=cur.next) {
                 if(cur.val==val){
                     // 此时遍历到的节点为待删除的节点,此时需要判断是否为最后一个节点
                     if(cur.next==null){
                         prev.next=null;
                     }else{
                         prev.next=cur.next;
                         cur.next=null;
                     }
                 }else{
                     prev=prev.next;
                 }
             }
         }
        return head;
    }
}

2.Num83删除排序链表中的重复元素

2.1 题目描述

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
Leetcode——链表笔试题_第1张图片

2.2 思路

Leetcode——链表笔试题_第2张图片

package List;
/**
 * 83. 删除排序链表中的重复元素
 * 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。
 * 返回 已排序的链表 。
 */
public class Num83_deleteDuplicates {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }

    public static ListNode deleteDuplicates(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode tmp=head.next;
        ListNode newhead=head;
        while (tmp!=null){
            while (tmp!=null && head.val==tmp.val){
                tmp=tmp.next;
            }
            head.next=tmp;
            if(tmp==null){
                break;
            }
            head=head.next;
            tmp=tmp.next;
        }
        return newhead;
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Num83_deleteDuplicates Num83=new Num83_deleteDuplicates();
        System.out.println(deleteDuplicates(head));
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(1);
        ListNode node3=new ListNode(2);
        ListNode node4=new ListNode(3);
        ListNode node5=new ListNode(3);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        return node1;
    }
}

3.面试题 02.01. 移除重复节点(乱序)

3.1 题目描述

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]

3.2 思路

Leetcode——链表笔试题_第3张图片

package List;

import com.sun.xml.internal.ws.api.ha.HaInfo;

import java.util.HashMap;
import java.util.Map;

/**
 * 面试题 02.01. 移除重复节点
 * 编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
 *  输入:[1, 2, 3, 3, 2, 1]
 *  输出:[1, 2, 3]
 *  思路:
 *  将遍历到的节点值(不重复)保存在哈希表中,
 *  当最新遍历到的节点值在哈希表中,移除
 *  当不在哈希表中,向后移动
 */
public class Num0201_removeDuplicateNodes {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }

    public static ListNode removeDuplicateNodes(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        Map<Integer,Integer> map=new HashMap<>();
        map.put(head.val,1);
        ListNode newhead=head;
        ListNode tmp=head.next;
        while (tmp!=null){
            if(!map.containsKey(tmp.val)){
                map.put(tmp.val,1);
                head.next=tmp;
                head=head.next;
                tmp=tmp.next;
            }else{
                tmp=tmp.next;
            }
        }
        head.next=null;
        return newhead;
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Num0201_removeDuplicateNodes Num83=new Num0201_removeDuplicateNodes();
        System.out.println(removeDuplicateNodes(head));
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(2);
        ListNode node3=new ListNode(3);
        ListNode node4=new ListNode(3);
        ListNode node5=new ListNode(2);
        ListNode node6=new ListNode(1);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node6;
        return node1;
    }
}

4.Num206反转链表

4.1 题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
Leetcode——链表笔试题_第4张图片

4.2 思路

4.2.1 方法1 创建新链表

创建新链表,遍历原链表,将遍历到的节点头插到新链表中。最后返回虚拟头节点的下一个节点
Leetcode——链表笔试题_第5张图片
将遍历到的节点头插到dummyhead虚拟头节点的后面
Leetcode——链表笔试题_第6张图片
第二个节点
Leetcode——链表笔试题_第7张图片

package List;
/**
 * 206. 反转链表
 * 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
 *
 */
public class Num206_reverseList {
      public class ListNode {
          int val;
          ListNode next;
          ListNode() {}
          ListNode(int val) { this.val = val; }
          ListNode(int val, ListNode next) { this.val = val; this.next = next; }
      }
    public ListNode reverseList(ListNode head) {
          if(head==null || head.next==null){
              // 链表为空,或者链表中只有一个元素
              return head;
          }
          // 此时链表中至少两个元素
        // 创建新链表的虚拟头节点
        ListNode dummyhead=new ListNode(5001);
          // 遍历原链表
          while (head!=null){
              ListNode node=new ListNode(head.val);
             node.next=dummyhead.next;
             dummyhead.next=node;
              head=head.next;
          }
          return dummyhead.next;
    }
}

4.2.2 原地移动

通过改变链表的指向
Leetcode——链表笔试题_第8张图片

 public ListNode reverseList1(ListNode head) {
        if(head==null || head.next==null){
            // 链表为空,或者链表中只有一个元素
            return head;
        }
        ListNode prev=null;
        ListNode cur=head;
        while (cur!=null){
            ListNode next=cur.next;
            cur.next=prev;
            prev=cur;
            cur=next;
        }
        return prev;
    }

5.Num876链表的中间节点

5.1 题目描述

给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/middle-of-the-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

5.2 两种思路

思路一:先遍历链表,看链表中有多少个节点count,再计算中间节点count=count/2+1;
再遍历一遍链表,每遍历一个节点,count–,当count==0时,返回

 public ListNode middleNode(ListNode head) {
        int count=0;  // 计算链表中有多少个节点
        ListNode cur=head;
        while (cur!=null){
            count++;
            cur=cur.next;
        }
        count=(count/2)+1;  // 计算中间节点
        while (head!=null){
            count--;
            if(count==0){
                break;
            }else{
                head=head.next;
            }
        }
        return head;
    }

思路2:快慢指针,定义两个指针fast low ,fast一次走两步,low一次走一步。当fast走到最后为null时,low为中间节点

 public ListNode middleNode1(ListNode head) {
        ListNode fast=head;
        ListNode low=head;
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            low=low.next;
        }
        return low;

    }

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

6.1 题目描述

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

6.2 两种思路

思路1:
先遍历一遍链表计算链表中的节点个数count,
再遍历一遍链表当走到count-k+1节点时 返回

public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode cur=head;
        int count=0;
        while (cur!=null){
            count++;
            cur=cur.next;
        }
        count=count-k+1;
        while (head!=null){
            count--;
            if(count==0){
                break;
            }else{
                head=head.next;
            }
        }
        return head;
    }

思路2:快慢指针
定义两个指针 fast low
fast先走K步,low再开始走,保证fast和low的相对距离为k,当fast为null时,返回low

public ListNode getKthFromEnd1(ListNode head, int k) {
        ListNode fast=head;
        ListNode low=head;
        while (k!=0){
            fast=fast.next;
            k--;
        }
        while (fast!=null){
            fast=fast.next;
            low=low.next;
        }
        return low;
    }

7.剑指 Offer II 027. 回文链表

7.1 题目描述

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

7.2 三种思路

思路1
1.创建新链表,将原链表进行转置
2.同时遍历原链表和新建的链表,当遍历到的值不同时,return false
两个链表遍历完,return true

package List;

/**
 * 剑指 Offer II 027. 回文链表
 * 给定一个链表的 头节点 head ,请判断其是否为回文链表。
 * 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
 *先将链表进行反转,再判断反转后的链表和原链表是否相等
 */
public class Offer027_isPalindrome {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }
    public static boolean isPalindrome(ListNode head) {
       ListNode head1=reverseList(head);
        while (head1!=null){
            if(head.val!=head1.val){
                return false;
            }
            head=head.next;
            head1=head1.next;
        }
        return true;
    }
    // 反转链表
    public static ListNode reverseList(ListNode head){
        if(head==null || head.next==null){
            return head;
        }
        ListNode dummyhead=new ListNode(10);
        while (head!=null){
            ListNode node=new ListNode(head.val);
            node.next=dummyhead.next;
            dummyhead.next=node;
            head=head.next;
        }
        return dummyhead.next;
    }

    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Offer027_isPalindrome offer027=new Offer027_isPalindrome();
        System.out.println(isPalindrome(head));
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(1);
        ListNode node3=new ListNode(2);
        ListNode node4=new ListNode(1);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        return node1;
    }
}

思路2
1.先找到原链表的中间节点
2.将中间节点之后的子链表进行反转
同时遍历原链表和反转后的子链表,注意便遍历满足的条件是以遍历子链表的长度

package List;

/**
 * 剑指 Offer II 027. 回文链表
 * 给定一个链表的 头节点 head ,请判断其是否为回文链表。
 * 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
 *先将链表进行反转,再判断反转后的链表和原链表是否相等
 */
public class Offer027_isPalindrome {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }
    public static boolean isPalindrome1(ListNode head) {
        ListNode middlenode=middleNode(head);
        ListNode reverse_middlenode=reverseList(middlenode);
        while (reverse_middlenode!=null){
            if(head.val!=reverse_middlenode.val){
                return false;
            }
            head=head.next;
            reverse_middlenode=reverse_middlenode.next;
        }
        return true;
    }
    // 反转链表
    public static ListNode reverseList(ListNode head){
        if(head==null || head.next==null){
            return head;
        }
        ListNode dummyhead=new ListNode(10);
        while (head!=null){
            ListNode node=new ListNode(head.val);
            node.next=dummyhead.next;
            dummyhead.next=node;
            head=head.next;
        }
        return dummyhead.next;
    }
    // 寻找链表的中间节点
    public static ListNode middleNode(ListNode head){
        while (head==null||head.next==null){
            return head;
        }
        ListNode fast=head;
        ListNode low=head;
        while (fast.next!=null){
            fast=fast.next.next;
            low=low.next;
        }
        return low;
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Offer027_isPalindrome offer027=new Offer027_isPalindrome();
        System.out.println(isPalindrome1(head));
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(0);
        ListNode node3=new ListNode(0);
        node1.next=node2;
        node2.next=node3;
        return node1;
    }
}

思路3 将值复制到数组中后用双指针法
1.复制链表值到数组列表中。
2.使用双指针法判断是否为回文。

package List;
import java.util.ArrayList;
import java.util.List;

/**
 * 剑指 Offer II 027. 回文链表
 * 给定一个链表的 头节点 head ,请判断其是否为回文链表。
 * 如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
 *先将链表进行反转,再判断反转后的链表和原链表是否相等
 */
public class Offer027_isPalindrome {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {}
        ListNode(int val) { this.val = val; }
        ListNode(int val, ListNode next) { this.val = val; this.next = next; }
    }
    public static boolean isPalindrome2(ListNode head) {
        List<Integer> list=new ArrayList<>();
        while (head!=null){
            list.add(head.val);
            head=head.next;
        }
        int left=0;
        int right=list.size()-1;
        while (left<right){
            if(list.get(left)!=list.get(right)){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build();
        Offer027_isPalindrome offer027=new Offer027_isPalindrome();
        System.out.println(isPalindrome2(head));
    }
    // 创建链表
    public static ListNode build(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(0);
        ListNode node3=new ListNode(0);
        node1.next=node2;
        node2.next=node3;
        return node1;
    }
}

8.Num141环形链表

8.1 题目描述

给你一个链表的头节点 head ,判断链表中是否有环。
Leetcode——链表笔试题_第9张图片

8.2 思路

快慢指针法:试想两个人在环形跑道跑步,一个人跑的快,一个人跑的慢,两个人总会相遇。
1.定义两个指针fast low
2.fast每次走两步,low每次走一步,条件:当fast!=null时
如果fast和low相遇,则带环

 public boolean hasCycle(ListNode head) {
        if(head==null){
            return false;
        }
        ListNode fast=head;
        ListNode low=head;
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            low=low.next;
            if(fast==low){
                return true;
            }
        }
        return false;
    }

9.Num21.合并两个有序链表

9.1 题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
Leetcode——链表笔试题_第10张图片

9.2 思路

创建新链表,遍历两个链表同时比较两个链表的值大小,将值较小的链表节点尾插到新链表中。

package List;
/**
 * 剑指 Offer 25. 合并两个排序的链表
 * 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
 * 输入:1->2->4,
 *      1->3->4
 * 输出:1->1->2->3->4->4
 */
public class Offer25_mergeTwoLists {
      public static class ListNode {
          int val;
          ListNode next;
          ListNode(int x) { val = x; }
      }
    public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
          if(list1==null){
              return list2;
          }
          if(list2==null){
              return list1;
          }
          ListNode dummyhead=new ListNode(101);
          ListNode cur=dummyhead;
          while (list1!=null || list2!=null){
              if(list1!=null){
                  if(list2==null||(list1.val<=list2.val)){
                      ListNode node=new ListNode(list1.val);
                      dummyhead.next=node;
                      dummyhead=dummyhead.next;
                      list1=list1.next;
                  }else{
                      ListNode node=new ListNode(list2.val);
                      dummyhead.next=node;
                      dummyhead=dummyhead.next;
                      list2=list2.next;
                  }
              }else if(list2!=null){
                  ListNode node=new ListNode(list2.val);
                  dummyhead.next=node;
                  dummyhead=dummyhead.next;
                  list2=list2.next;
              }
          }
          return cur.next;
    }
    public static void main(String[] args) {
        // 创建链表[1,2,6,3,4,5,6]
        ListNode head=build1();
        ListNode head1=build2();
        Offer25_mergeTwoLists offer25=new Offer25_mergeTwoLists();
        System.out.println(mergeTwoLists(head,head1));
    }
    // 创建链表
    public static ListNode build1(){
        ListNode node1=new ListNode(2);
        return node1;
    }
    public static ListNode build2(){
        ListNode node1=new ListNode(1);
        return node1;
    }
}

这样更好理解

public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
          if(list1==null){
              return list2;
          }
          if(list2==null){
              return list1;
          }
          ListNode dummyhead=new ListNode(101);
          ListNode cur=dummyhead;
          while (list1!=null && list2!=null){
                  if(list1.val<=list2.val){
                      ListNode node=new ListNode(list1.val);
                      dummyhead.next=node;
                      dummyhead=dummyhead.next;
                      list1=list1.next;
                  }else{
                      ListNode node=new ListNode(list2.val);
                      dummyhead.next=node;
                      dummyhead=dummyhead.next;
                      list2=list2.next;
                  }
              }
          if(list1==null){
              dummyhead.next=list2;
          }else if(list2==null){
              dummyhead.next=list1;
          }
          return cur.next;
    }

你可能感兴趣的:(笔试题,链表,leetcode,算法)