【数据结构(四)】链表经典练习题

❣博主主页: 33的博客❣
▶️文章专栏分类:数据结构◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你学更多数据结构知识

目录

  • 1.前言
  • 2.删除值为key的所有结点
  • 3.反转链表
  • 4.返回中间结点
  • 5.输出倒数第k个结点
  • 6.链表分割
  • 7.链表回文
  • 8.相交链表
  • 9.环形链表
    • 9.1是否为环形链表
    • 9.2入环第一个节点
  • 10.总结

1.前言

在上一篇文章中博主已经介绍了链表的基础知识,什么是链表,如何实现一个链表,以及LinkedList的操作方法,那么在这篇文章中通过一些链表的经典习题来练习吧。

2.删除值为key的所有结点

删除值为val的所有结点:OJ链接
解题思路:

1.遍历链表。
2.如果某个结点A的值等于key,那么A的前一个结点B的next值直接等于A的next。
3,继续遍历,遇到等于值等于key的重复上诉操作,直到全部遍历完成。

public void removeAllKey(int key) {
    if (head==null){
        return;
    }
    Node node=head;
    Node cur=head.next;
    while (cur!=null){
        if(cur.val==key){
            node.next=cur.next;
            cur=cur.next;
        }else {
            node=node.next;
            cur=cur.next;
        }
    }
    if(head.val==key){
        head=head.next;
    }
    }

3.反转链表

反转链表:OJ链接

public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null){
            return head;
        }
        ListNode cur=head.next;
        head.next=null;
        while (cur!=null){
            ListNode curNext=cur.next;
            cur.next=head;
            head=cur;
            cur=curNext;
        }
        return head;
    }

4.返回中间结点

返回中间结点:OJ链接
方式一:
解题思路:

1.求链表的大小size
2.中间结点为size/2
遍历链表,走到size/2的位置,返回该节点

class Solution {
	//求链表长度
    public int size(ListNode head) {
        ListNode node=head;
        int count=0;
        while (node!=null){
           count++;
            node=node.next;
        }
        return count;
    }
    public ListNode middleNode(ListNode  head) {
        if(head==null||head.next==null){
            return head;
        }
        int Size=size(head);
        int middle=Size/2;
        ListNode node=head;
        for (int i=0;i<middle;i++){
            node=node.next;
        }
        return node;
    }    
}

方式二:
解题思路:

1.设置一个快结点:fast,设置一个慢结点:slow。
2.我们试想:如果fast的速度是slow的两倍,那么当fast到达路程的终点时,此时slow就走了路程的一半。
3.所以我们让fast每次走两步,slow每次走一步,当fast=null或者fast.next=null的时候,slow就是中间结点

public Node middleNode2() {
        Node fast=head;
        Node slow=head;
        while (fast!=null||fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }

5.输出倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点:OJ链接
解题思路:

1。让fast先走k-1步数
2.再同时走,当fast到达终点时,那么它们就相差k
3.此时的slow就是倒数第k个结点

ublic Node FindKthToTail (int k) {
        Node fast=head;
        Node slow=head;
    if(k<=0||head==null){
        return null;
    }
    while (k-1>0){
        fast=fast.next;
        k--;
    }
    while (fast.next!=null){
        fast=fast.next;
        slow=slow.next;
    }
    return slow;
    }

6.链表分割

以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前:OJ链接
解题思路:

1.首先,遍历链表
2.再创建两个链表,一个用于存放小于x的值,另一个用于存放大于x的值
3.再把第一个链表的最后一个结点与第二个链表的头节点串起来。

public Node partition(int x) {
        if (head==null){
            return null;
        }
        Node as=null;
        Node ae=null;
        Node bs=null;
        Node be=null;
        Node cur=head;
        while (cur!=null){
            if (cur.val<x){
              if(as==null){
                  //插入第一个节点
                  as=cur;
                  ae=cur;
                  cur=cur.next;
              } else {
                  ae.next=cur;
                  ae=ae.next;
                  cur=cur.next;
              }
            }else {
             //cur.val>x
             if(bs==null){
             //插入第一个节点
                 bs=cur;
                 be=cur;
                 cur=cur.next;
             }  else {
                 be.next=cur;
                 be=be.next;
                 cur=cur.next;
             }
            }
        }
        //如果第一个链表为空就直接返回第二个链表的头节点
        if(as==null){
            return bs;
        }
        ae.next=bs;
        if(bs != null) {
            //第2个区间有数据
            be.next = null;
        }
        head=as;
        return head;
    }

7.链表回文

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构:OJ链接
解题思路:

1.查看链表是否回文,是指从前向后遍历,或者从后向前遍历,输出的之都相同。
2.我们可以先找到中间位置,再从中间位置进行翻转,使它同时从两头向中间遍历
3.遍历的时候如果如果链表有偶数个的情况,和有奇数个的情况是不一样的,当有奇数个时,走到相同的位置就停止,当有偶数个时,当下再走一步就相遇时就停止。

public boolean chkPalindrome() {
		 if(head == null || head.next == null) {
            return true;
        }
        //按照中间位置翻转链表
        //1.找到中间位置(middle已经在4中写过)
        Node middle=middleNode2();
        //2.翻转
        Node ehead=middle;
        Node cur=ehead.next;
        while (cur!=null){
            Node curNext=cur.next;
            cur.next=ehead;
            ehead=cur;
            cur=curNext;
        }
        //从两头开始遍历
        Node snode=head;
        Node enode=ehead;
        while (snode!=enode){
            if (snode.val!=enode.val){
                return false;
            }
            if (snode.next==enode){
                return true;
            }
                snode=snode.next;
                enode=enode.next;
        }
        return true;
    }

8.相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点:OJ链接
解题思路:

1.求两个链表的长度,再求长度差值
2.定义节点fast代表长的链表头节点,slow代表短的链表的头节点。先让长的链表先走差值步,再同时走。
3.两个链表相遇时就是相交节点。

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {        
        ListNode nodeA=headA;
        ListNode nodeB=headB;
        int lenA=size(headA);
        int lenB=size(headB);
        ListNode fast=headA;
        ListNode slow=headB;
        int len=lenA-lenB;
        if(len<0){
            len=lenB-lenA;
            fast=headB;
            slow=headA;
        }
        while(len>0){
            fast=fast.next;
            len--;
        }
        while(fast!=slow&&fast!=null){
            fast=fast.next;
            slow=slow.next;
        }       
        return fast;      
    }

9.环形链表

9.1是否为环形链表

给你一个链表的头节点 head ,判断链表中是否有环:OJ链接
解题思路:

1.定义节点fast代表长的链表头节点,slow代表短的链表的头节点.
2.每次让fast走两步,slow走一步,如果是环形链表,那么它们一定会在环中的某一个位置相遇,否则不是环形链表
为什么每次让fast走两步,slow走一步?不可以fast走3步,4步吗?假设环中只有两个元素A,B,当slow进入环时在A的节点的位置,此时fast在B节点的位置,slow走一步就到B的位置,fast走3步就到A的位置,那么它们永远都不会相遇。
只有每次让fast走两步,slow走一步,换的长度为1,那么肯定会相遇。

 public boolean hasCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            //fast.next!=null&&fast!=null,不能这样写
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return true;
            }   
        }
        return false;       
    }

9.2入环第一个节点

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null:OJ链接
解题思路:

让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。
证明如下:

public class Solution {
    ListNode hasCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            //fast.next!=null&&fast!=null,不能这样写
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return fast;
            }   
        }
        return null;       
    }
    public ListNode detectCycle(ListNode head) {
          ListNode fast=hasCycle(head);
          if(fast==null){
            return null;
          }
          ListNode slow=head;
          while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
          }
          return fast;  
    }
}

10.总结

本篇博客主要对链表的经典习题进行了讲解,同学们要理解解题的一些思想做到融会贯通,如果有疑惑的地方欢迎大家来和博主沟通,交流。

下期预告:栈

版权声明:本文为博主作者:PU-YUHAN原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/weixin_69049913/article/details/137612381

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2024年5月6日
下一篇 2024年5月6日

相关推荐