链表问题解题思路总结——看这一篇就够啦~(例题+分析+代码示例)

参考:
左神算法课(大力推荐,感兴趣的同学都可以自己去一探究竟)

注意:本篇文章的代码并不完全正确性,主要是解题思路的分享

1. 结题思路总结

高屋建瓴,面对链表问题我们可以尝试从下面这些角度去考虑可能的解答方法

    1. 快慢指针的灵活灵活运用
    1. 用额外的数据结构来记录链表的结构以及必要的信息
    1. 大胆调整链表的结构并完成恢复

下面我们用具体的例题来说明个种方法的具体运用

2. 回文串判断

题目说明:回文串的概念是指:一个链表无论是从前往后读还是从后往前读其结果都是相同的。例如1->2->3->2->1,又如1->2->2->3。

2.1 思路一:基于栈数据结构

基于栈的思路其实是非常的浅显易懂的:如果是回文串,容易想见链表一定存在一个可以对折的中间线(见下图)。只需要将链表中的元素一个一个加入栈中,当来到中间线之后,对接下来每一个想要入栈的元素进行判断:如果该元素与栈顶元素相同,那么将栈顶元素弹出,如此反复。如果最后栈为空,那么是回文串,否则不是回文串。
注意:这个方法存在一个问题,为了找到中间线,我们势必要定位到该链表的中间节点,一个朴素的方案是直接先确定链表的长度
中间线

    public static boolean isRoundList_S(LinkedList<Integer> list) {
        Stack<Integer> stack = new Stack<>();
        int len = list.size();
        boolean even = (len % 2) == 0;  // 判断链表中的元素个数是否是偶数个
        for (int i = 0; i < len; ++i) {
            if (i < len / 2) {
                stack.push(list.get(i));
            } else if (even) {
                if (!Objects.equals(stack.pop(), list.get(i))) return false;
            } else {
                even = true; // 如果是奇数个,跳过中间元素
            }
        }
        return stack.size() == 0;
    }

2.2 思路二:栈优化(快慢指针思想)

第一种方法有一个缺陷,就是我们必须自己去判断元素的个数以及链表元素个数的奇偶性,下面介绍基于快慢指针的优化方法。
快慢指针:设置两个指针,起始时两个指针都指向头结点,之后慢指针一次向前走一步,快指针一次向前走两步,当快指针来到链表末尾时,慢指针将指向链表的“中点”(注意:这里打上引号的原因是,在不同的题目要求下,我们可以略微调整指针的起始状态或者步进方式,使慢指针最终指向我们希望其指向的位置)
下面我们用快慢指针来对第一种方法进行优化:准备一个栈,假设此时快指针已经来到了链表的末尾而慢指针则指向了链表的中间节点,那么接下来我们只需要步进慢指针就可以将链表的后半部分都添加进栈中。待后半节点都已经加入了栈中,接下来我们只需要从链表头节点开始,与栈顶元素进行对比,如果元素相等则弹出栈顶元素,如果不等则证明该单链表不是回文串,最后如果栈为空则证明该链表是回文串。
快慢指针思路

public static Boolean isRoundList_Op(Node head) {
        Stack<Node> stack = new Stack<>();
        Node slow = head, quick = head;
        while(quick.next!=null && quick.next.next!=null) {
            // 这种条件判断方式保证慢节点最终来到链表的中间节点
            // 中间节点指:奇数个元素链表的中间节点;偶数个元素俩表的中线前一个节点
            quick = quick.next.next;
            slow = slow.next;
        }
        quick = head;
        while(slow.next != null) {
            stack.push(slow.next);
            slow = slow.next;
        }
        while(stack.size() != 0) {
            if (!(quick.value == stack.pop().value)) {
                return false;
            }else {
                quick = quick.next;
            }
        }
        return true;
    }

2.3 思路三:快慢指针 + 链表结构调整

有没有一种方法可以摆脱栈空间的使用呢?答案是肯定的!
如果我们有一种方法能够从链表的两头向中间前进,逐个对比那么我们将很轻易地判断一个链表是否为回文串。这就需要我们手动改变链表的结构,如下图:
链表结构改造

    public static Boolean isRoundList_Opp(Node head) {
        Node slow = head, quick = head; // 快慢指针
        while(quick.next!=null && quick.next.next!=null) {
            slow = slow.next;
            quick = quick.next.next;
        } // 注意这里quick不一定指向最后一个节点
        while(quick.next!=null) { // 是quick指针指向最后一个节点
            quick  =quick.next;
        }
        Node start = slow;
        Node start_n = start.next;
        Node start_nn = start_n.next;
        while(start_nn!=null) {
            start_n.next = start;
            start = start_n;
            start_n = start_nn;
            start_nn = start_nn.next;
        }
        start_n.next = start;
        slow.next = null;
        start = head;
        while(start!=null && quick!=null) {
            if(start.value != quick.value) {
                return false;
            }
            start = start.next;
            quick = quick.next;
        }
        return true;
    }

注意:链表的结构已经被我们改变了,如果需要保持原链表不变的话,还需要添加一段链表的结构恢复代码,如果感兴趣的话大家可以自己探索一下

未完待续,尽情期待!!
要是有收获的话,希望大家关注一波哦!!

3. 荷兰国旗问题(单链表版本)

荷兰国旗

题目说明:如上图所示,所谓的荷兰国旗问题就是依据数据的大小,将给定的数据集按一定的规则分成三个组别;特殊的,通常而言说起荷兰国旗问题指的是:给定一个num,将数据集分为“小于num组”,“等于num组”以及“大于num组”,对于本题我们要求小于num的组排列在前,等于num的排列在列表的中间,而大于num的元素则排在链表的最后。

3.1 思路一:基础Partition

3.2 思路二:优化Partition(基于链表本身的特性)

4. 特殊链表复制问题

题目说明:假设我们有一个特殊的链表结构,其中每个节点不仅仅有一个next指针(指向链表中的下一个对象),还有一个rand指针。其中rand指针可以指向该链表中的任意指针(也可以指向null)。而我们的要求则是,我们要完整地复制一份该链表(即复制每一个节点对象并完整地构建节点间连接关系)

4.1 思路一:借助其他数据结构

4.2 思路二:链表的结构调整与恢复

5. 判断链表是否相较返回第一个相交节点

题目说明:假设现在又两条链表(有可能有环,有可能无环),请判断这两条链表是否相交,如果相交,请返回这两条链表的第一个相交节点。

5.1 证明单链表是否有环

5.1.1 思路一:Set数据结构
5.1.2 思路二:快慢指针

5.2 判断是否相交,如果相交返回第一个相交节点

5.2.1 思路一:Set数据结构
5.2.2 思路二:分情况讨论
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 建立学生信息链表解题思路如下: 1. 定义学生信息结构体 首先,我们需要定义一个学生信息的结构体,包括学生的姓名、学号、性别、年龄等信息。 2. 定义链表结构体 其次,我们需要定义链表结构体,包括链表节点的指针和学生信息结构体。 3. 创建头节点 接着,我们需要创建头节点,头节点的作用是方便链表的操作,例如插入、删除、查找等。 4. 插入节点 然后,我们可以通过输入学生信息的方式,向链表中插入节点,每个节点包括一个学生信息结构体和一个指向下一个节点的指针。 5. 遍历链表 最后,我们可以通过遍历链表的方式,输出链表中所有节点的学生信息。 总的来说,建立学生信息链表的过程就是创建链表结构体、插入节点和遍历链表的过程。在这个过程中,我们需要注意指针的使用以及内存的分配和释放。 ### 回答2: 建立学生信息链表解题思路如下: 1. 首先,定义一个学生信息的结构体,包括学生的姓名、学号、年龄等信息。 2. 创建一个头节点,作为链表的初始节点。 3. 通过用户输入,逐个添加学生信息节点到链表中。可以采用循环结构,在每次循环中,创建一个新的节点,并让用户输入该节点的学生信息。然后根据链表的结构,将该节点插入到链表的合适位置。可以根据学生的学号或者姓名来决定插入的位置。 4. 当添加完所有学生信息后,可以提供以下功能: - 查找:根据学号或者姓名来查找某个学生的信息。遍历整个链表,找到对应学生节点,并输出相关信息。 - 修改:根据学号或者姓名来定位到某个学生的节点,并允许修改该学生的信息。 - 删除:根据学号或者姓名来定位到某个学生的节点,并删除该节点。 - 遍历:遍历整个链表,输出所有学生的信息。 5. 可以在每个功能的实现中加入一些输入校验的逻辑,确保用户输入的学生信息的有效性,例如学号不能重复。 6. 最后,记得在程序结束时释放链表内存,避免内存泄漏。 以上是建立学生信息链表的基本思路,具体的实现细节可以根据实际需求进行调整和完善。 ### 回答3: 建立学生信息链表可以采用以下步骤: 1. 定义学生信息的数据结构:首先需要定义学生的各种信息,如学号、姓名、年龄、性别等,可以使用一个结构体来存储这些信息。 2. 创建链表结点:每个链表结点包含一个学生信息的数据结构和一个指向下一个结点的指针。 3. 初始化链表:创建一个头结点,将头结点的指针指向NULL,表示链表为空。 4. 添加学生信息:根据用户输入的学生信息,创建一个新的链表结点,并将输入的信息存入结点中。然后将该结点的指针指向原链表的头结点,再将新结点设为头结点,即可完成添加操作。 5. 查找学生信息:遍历链表,逐个比较每个结点中的学生信息,找到与要查找的信息匹配的结点。 6. 修改学生信息:同样需要遍历链表,找到需要修改的结点,然后更新其信息。 7. 删除学生信息:遍历链表,找到需要删除的结点,将其从链表中摘除即可。 8. 输出学生信息:通过循环遍历链表,将每个结点中的学生信息输出到屏幕上。 最后,根据具体需求,可以进一步完善链表功能,如排序、插入、统计等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值