List——顺序表&链表OJ


前言

上两篇内容,对链表和顺序表进行了讲解并手动实现了自己的顺序表和链表,本篇文章将结合LeetCode上的OJ题,进行具体的使用以熟悉其中的方法和使用细节。


一、合并两个有序链表

题目链接:Leetcode.21题

在这里插入图片描述

有序、链表这是题目中已经说明的,具体思路是我们创建一个新的头结点用来保存合并后的链表。分别遍历两个链表的头节点,将较小的头结点链接在新的链表后 然后头节点后移继续比较,直到一个链表为空(当有一个链表为空时,只需要将另一个链表剩余部分全部连接在答案链表之后)。

代码实现:

public class Leetcode21_MergeTwoList {
    public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode head1=list1;
        ListNode head2=list2;
        //定义一个储存答案的头结点
        ListNode ansHead= new ListNode();
        ListNode ansLast=ansHead;  //尾节点

        while (head1!=null&&head2!=null) {
        //比较两个链表头结点的大小
            if (head1.val < head2.val) {
                //先将待连接的头节点保存下来
                ListNode cur = head1;
                //链表1头节点后移
                head1 = head1.next;
                //将之前保存的头节点连接到结果链表中
                ansLast.next = cur;
                //更新结果链表的尾节点
                ansLast = cur;
            } else {
                ListNode cur = head2;
                head2 = head2.next;
                ansLast.next = cur;
                ansLast = cur;
            }
        }

            if(head1!=null){
                ansLast.next=head1;
            }else {
                ansLast.next=head2;
            }

        return ansHead.next;
    }
 }

测试代码正确性:

//测试代码正确性
    public static void main(String[] args) {
        ListNode listN1 = new ListNode(100);
        ListNode listN2 = new ListNode(200);
        ListNode listN3 = new ListNode(300);

        listN1.next = listN2;
        listN2.next = listN3;
        listN3.next=null;

        ListNode listN4 = new ListNode(100);
        ListNode listN5 = new ListNode(200);
        ListNode listN6 = new ListNode(300);

        listN4.next=listN5;
        listN5.next=listN6;
        listN6.next=null;

        ListNode ans=mergeTwoLists(listN1,listN4);

        ListNode cur=ans;
        while (cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }
  }

运行结果:

在这里插入图片描述

二、使用顺序表实现“杨辉三角”

题目链接:Leetcode.118题
在这里插入图片描述

这道题,我们需要使用List接口下的ArrayList。我们可以创建一个线性表,线性表中的每一个元素也是一个线性表,每个线性表是一个ArrayList,存储的是该行对应的元素。需要特殊处理的是三角形的前两行,第三行开始只需要首尾填1,其他使用循环结构从上一行取元素加和即可。

代码实现:

class Solution {
    public static List<List<Integer>> generate(int num) {
        List<List<Integer>> ans = new ArrayList<>();
        //零行不做任何处理  直接返回
        if (num == 0) {
            return ans;
        }
        //走到这儿,说明至少有一行,先添加一行进去
        ans.add(new ArrayList<>());
        //对第一行的顺序表添加元素1
        ans.get(0).add(1);
        //判断是不是只有一行,若是  返回
        if (num == 1) {
            return ans;
        }
        //走到这儿说明至少有两行 再添加一行(一个顺序表)进去
        ans.add(new ArrayList<>());
        //处理第二行元素
        ans.get(1).add(1);
        ans.get(1).add(1);
        //判断是否只有两行
        if (num == 2) {
            return ans;
        }
        //走到这儿  就有规律可循了  第三行之后每一行只有首尾是1,其他元素根据上一行得到
        for (int i = 3; i <= num; i++) {
        //每走一趟循环  添加一行进去
            List<Integer> cur = new ArrayList<>();
            ans.add(cur);
            ans.get(i - 1).add(1);   //这一行的首元素
            //循环体中对每一行除首尾之外的元素进行处理
            for (int j = 0; j < i - 2; j++) {
                int first = ans.get(i - 2).get(j); //获取上一行第j个元素
                int second = ans.get(i - 2).get(j + 1);//上一行第j+1个元素
                int e = first + second; //求和
                ans.get(i - 1).add(e);  //添加到这一行的顺序表中
            }
            ans.get(i - 1).add(1);  //这一行的尾元素
        }
        return ans;
    }
}

测试代码正确性:

public static void main(String[] args) throws InterruptedException {

        System.out.println(Leetcode118_Generate.generate(6));
    }

运行结果:

在这里插入图片描述

三、环形链表

题目链接:Leetcode.141题

在这里插入图片描述

思路一:哈希表,这道题比较容易想到的一个思路是,我们可以遍历这条链表,每次遍历时判断该节点是否被访问过。可以使用哈希表来存储所有已经访问过的节点。每次我们到达一个节点,如果该节点已经存在于哈希表中,则说明该链表是环形链表,否则就将该节点加入哈希表中。重复这一过程,直到我们遍历完整个链表即可。但是哈希表方法还没总结,因此我们使用一个更简单的方法。

思路二:快慢指针,此方法为Floyd判圈算法(又称龟兔赛跑算法),假想乌龟和兔子同时从同一起点在链表上移动,如果链表上没有环,那么兔子将一直处于乌龟的前方;若该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。

我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,块慢指针都在位置 head,这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

代码实现:

public boolean hasCycle(ListNode head){
		//先处理空链表和只有一个结点的情况
        if(head==null){
            return false;
        }
        if(head.next==null){
            return false;
        }
        //定义快慢指针
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){   //fast.next!=null  这一句很关键  没有的话会报空指针异常~
            fast=fast.next.next;  //没有fast.next!=null这个条件的话  这里会空指针异常
            slow=slow.next;
            if(fast==slow){
                return true;
            }
        }
        return false;
    }

测试代码正确性:

public static void main(String[] args) {
        ListNode listN1 = new ListNode(100);
        ListNode listN2 = new ListNode(200);
        ListNode listN3 = new ListNode(300);

        listN1.next = listN2;
        listN2.next = listN3;
        listN3.next = listN1;
        System.out.println(hasCycle(listN1));
    }

在这里插入图片描述

四、环形链表Ⅱ

题目链接:Leetcode.142题

在这里插入图片描述
与上一题不同的是,上一题只需要判断链表中有没有环,此题需要找到并返回环的入口节点,难点在于如何找到?

判断是否有环上面已经讲过了,此时找环的入口:
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:

在这里插入图片描述
那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2: (x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z,

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

这样我们只需要对上面的代码稍作修改,就可以AC这道题了!

代码如下:

public ListNode detectCycle(ListNode head){
        if(head==null){
            return null;
        }
        if(head.next==null) {
            return null;
        }

        ListNode fast=head;
        ListNode slow=head;
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                break;
            }
            if(fast==null||fast.next==null){
                return null;
            }
        }
        //运行到此处 说明存在环,快指针比慢指针多走了 n倍的环的长度
        //如果让指针从链表头部一直向前走并统计步数k,那么所有 走到链表入口节点时的步数 是:k=x+n(y+z)(先走 x 步到入口节点,之后每绕 1 圈环( y+z 步)都会再次到入口节点)。
        //而目前,slow 指针走过的步数为 n(y+z) 步。因此,我们只要想办法让 slow 再走 x 步停下来,就可以到环的入口。
        //但是我们不知道 x 的值,该怎么办?依然是使用双指针法。我们构建一个指针,此指针需要有以下性质:此指针和slow 一起向前走 x 步后,两者在入口节点重合。那么从哪里走到入口节点需要 x 步?答案是链表头部head
        fast=head;
        while (fast!=slow){
            slow=slow.next;
            fast=fast.next;
        }
        return slow;
    }

验证代码正确性:

public static void main(String[] args) {
		//listN1是头节点也是链表环的入口
        ListNode listN1 = new ListNode(100);
        ListNode listN2 = new ListNode(200);
        ListNode listN3 = new ListNode(300);
        listN1.next = listN2;
        listN2.next = listN3;
        listN3.next = listN1;
        System.out.println(detectCycle(listN1).val);//输出入口结点的值
    }

在这里插入图片描述


总结

本篇博客通过四道力扣题对顺序表和链表的使用以及其具体实现了ArrayList、LinkedList进行了使用,后面还会更新更多的Leetcode题~ 感兴趣的老铁店点关注不迷路 谢谢支持!

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭彭彭摆鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值