初学者链表题不会做----看这一篇就够了(JAVA)

目录

概念

例题

总结

概念:

链表是一种常见的基础数据结构,链表可以动态的进行存储分配,也就是说,链表是一个功能极为强大的数组,他可以在节点中定义多种数据类型,还可以根据需要随意增添,删除,插入节点。链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的。链表中每个节点都分为两部分,一个数据域,一个是指针域。说到这里你应该就明白了,链表就如同车链子一样,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。

例题:

1、合并两个有序链表

2、寻找单链表的中点

3、寻找单链表的倒数第k个节点

4、判断单链表是否包含环

5、判断两个单链表是否相交并找出交点

6、删除链表中重复元素||

这些题目都是LeetCode上链表模块最具有代表性的题目,每个题都有它极具特色的解法而且有些题的解法并不唯一,下面我们开始学习这些题目里的骚套路。

---------------------------------------------------------------------------------------------------------------------------------

这个题我说两种解题思路,一个是巧用尾插法来实现,另一个就是利用递归思想来解决。

​解法一:尾插法
​
 public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
       // 边界
      if (list1 == null) {       //判断特殊情况
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        // 此时l1和l2都不为空
        ListNode dummyHead = new ListNode(101);//虚拟头结点,需注意题中数据的范围
        ListNode last = dummyHead;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                last.next = list1;
                last = list1;
                list1 = list1.next;
            }else {
               last.next = list2;
                last = list2;
               list2 = list2.next;
            }
        }
        // 此时l1或l2为空
        if (list1 == null) {
            last.next = list2;
        }else {
           last.next = list1;
       }
        return dummyHead.next;
    }

​

​
 递归法
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null) {
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        if (list1.val <= list2.val) {
            // 说明此时要把list1.next和整个list2交给merge
            // 然后将结果拼接到list1.next
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;
        }else {
            list2.next = mergeTwoLists(list1,list2.next);
            return list2;
        }
    }
}


思路:把两个链表分成头结点和其他链表两部分,绿色部分即为递归函数要解决的问题,咱们只需要处理两个头结点即可。

递归函数的语义:接收两个不为空链表并且将它们合并升序排列

思路:这里我们用一个快慢指针来解决,快慢指针之间的距离差为2倍,当快指针走到完时满指针所在的节点即为中间结点。但是这里我们需要注意链表数目是奇数还是偶数,偶数我们就让fast.next==null作为循环的结束条件,奇数的话令fast==null即可。

public class Num876 {
    // 快慢指针法
    public ListNode middleNode(ListNode head) {
        // fast一次走两步
        ListNode fast = head;
        // low一次走一步
        ListNode low = head;
        // 当fast为null(偶数个节点) || fast.next == null(奇数个节点)
        while (fast != null && fast.next != null) {
            low = low.next;
            fast = fast.next.next;
        }
        return low;
    }
}

思路:这个题跟上一个题目解题方法一样都是使用双指针,让fast指针比low指针差K步,当fast指针走到最后一个结点时,low指针就指向了需要被删除的结点了。

public class Offer_22 {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head,low = head;
        for (int i = 0; i < k; i++) {
            fast = fast.next;
        }
        // 此时再让low和fast同时向后移动,直到fast为空
        while (fast != null) {
            low = low.next;
            fast = fast.next;
        }
        return low;
    }
}

 

思路: 双指针叒来了,这个问题类似于运动会跑步项目,如果成环的话跑的快的人一定会和跑的慢的人相遇。废话不多说直接上代码:

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

        return false;
    }
   
}

思路:这个题可以说是上一个题的升级版,大题思路差不多当fast和low相遇的时候我们创建一个新的结点,让它从头结点开始移动当它和low或者fast指针相遇时返回节点索引位置。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode low=head;
        while(fast!=null&&fast.next!=null)
        {
            fast=fast.next.next;
            low=low.next;
             if((low==fast))
        {
            ListNode third=head;
            while(third!=low)
            {
                third=third.next;
                low=low.next;
            }
            return low;
        }
        
        }
     
        return null;
    }
}

思路:我们这题使用虚拟头结点,虚拟头结点可以简化一些关于头结点的一些操作。继续使用双指针cur和prev,当cur和prev结点的值不相同继续遍历,如果相同就让prev先走。直到找到下一个不相同的值,然后把cur移动到prev结点,prev结点继续变量重复上述操作。当prev为空时,将最前面不相同的结点连接到最后一个节点即可。

public class Num82 {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummyHead = new ListNode(101);
        dummyHead.next = head;
        ListNode prev = dummyHead;
        ListNode cur = prev.next;
        while (cur != null) {
            ListNode next = cur.next;
            if (next == null) {
                return dummyHead.next;
            }else {
                // 此时链表中至少有两个节点
                if (cur.val == next.val) {
                    // cur和next重复
                    // 让next一直向后移动,直到走到第一个与cur.val不相等的节点
                    while (next != null && cur.val == next.val) {
                        next = next.next;
                    }
                    // prev - 第一个重复节点的前驱
                    // next - 最后一个重复节点的后继
                    // prev - next全是重复元素,需要删除
                    prev.next = next;
                    // prev不动,cur走到当前next继续判断后面是否还有重复元素
                    cur = next;
                }else {
                    // cur与next不重复
                    prev = prev.next;
                    cur = cur.next;
                }
            }
        }
        return dummyHead.next;
    }
}

总结

  看完上面的内容你就可以在力扣上完成至少6道题,双指针、虚拟头结点都是链表题里面很常用的技巧,链表题一定要多画图!!!

 

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值