算法题解 —— 链表(6-10)

这篇博客介绍了用Java解决6个关于链表的算法问题:环形单链表的约瑟夫问题、判断链表是否为回文结构、按值划分链表、复制带有随机指针的链表以及两个单链表生成相加链表。通过不同的解题思路和代码实现,展示了如何在O(N)时间复杂度内解决这些问题。
摘要由CSDN通过智能技术生成

内容来源于自己的刷题笔记,对一些题目进行方法总结,用 java 语言实现。

6. 环形单链表的约瑟夫问题:
  1. 题目描述:

    输入:一个环形单向链表的头节点 head 和报数的值 m

    返回:最后生存下来的节点,且这个节点自己组成环形单向链表,其他节点都删掉

    进阶问题:如果链表节点数为 N,想在时间复杂度为 O(N) 时完成原问题的要求

  2. 解题思路:

    • 普通解法:

      • 如果链表为空或者链表节点为1,或者 m 的值小于1,则不用调整就直接返回
      • 当环形链表中遍历每个节点,不断转圈,不断让每个节点报数
      • 当报数到达 m 时,就删除当前报数的节点
      • 删除节点后,把剩下的节点继续连成环状,继续转圈报数,继续删除
      • 不停的删除,直到环形链表中只剩下一个节点,过程结束
    • 进阶解法:

      该解法与链表本身的思想无关,主要是找到最后剩下节点的规律,遍历链表找到该节点

  3. 代码实现:

    public class JosephusKill {
         
    
        private class Node{
         
            public int value;
            public Node next;
            public Node(int value){
         
                this.value = value;
            }
        }
    
        /**
         * 普通解法,时间复杂度为 O(m*n)
         * @param head
         * @param m
         * @return
         */
        public Node josephusKill1(Node head,int m){
         
            if (head == null || head.next == head || m < 1){
         
                return head;
            }
            Node last = head;
            while (last.next != head){
         
                last = last.next;
            }
            int count = 0;
            while (head != last){
         
                if (++count == m){
         
                    last.next = head.next;
                    count = 0;
                }else {
         
                    last = last.next;
                }
                head = last.next;
            }
            return head;
        }
    
        /**
         * 进阶解法
         * @param head
         * @param m
         * @return
         */
        public Node josephusKill2(Node head,int m){
         
            if (head == null || head.next == head || m < 1){
         
                return head;
            }
            Node cur = head.next;
            int tmp = 1;
            while (cur != head){
         
                tmp++;
                cur = cur.next;
            }
            tmp = getLive(tmp,m);
            while (-- tmp != 0){
         
                head = head.next;
            }
            head.next = head;
            return head;
        }
    
        private int getLive(int i,int m){
         
            if (i == 1){
         
                return 1;
            }
            return (getLive(i-1,m) + m - 1) % i + 1;
        }
    }
    
7. 判断一个链表是否为回文结构:
  1. 题目描述:

    给定一个链表的头节点 head,请判断此链表是否是回文结构

    进阶:如果链表长度为 N,时间复杂度为 O(N),额外空间复杂度达到 O(1)

  2. 解题思路:

    • 第一种解法:利用栈结构,从左到右遍历链表,遍历的过程中把每个节点依次压入栈中,因为栈是先进后出的,遍历完成后,从栈顶到栈底的节点值出现顺序会与原链表从左到右的出现顺序反过来
    • 第二种解法:对第一种进行了优化,虽然也是利用了栈结构,单其实并不是所有的节点都要压入栈中,只用压入一半的节点即可,压入链表的右半部分,压入完成后,再检查栈顶到栈底值出现的顺序是否和链表左半部分的值相对应
    • 第三种解法:
      • 改变链表右半区的结构,使整个右半区反转,最后指向中间节点
      • 两边同时向中间节点移动,移动异步时都比较节点的值,看是否一样,如果都一样就是回文结构
  3. 代码实现:

    public class Palindrome {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        /**
         * 普通解法:全栈
         * @param head
         * @return
         */
        public boolean isPalindrome1(Node head){
            Node cur = head;
            Stack<Node> stack = new Stack<>();
            while (cur != null){
                stack.push(cur);
                cur = cur.next;
            }
            while (head != null){
                if (head.value != stack.pop().value){
                    return false;
                }
                head = head.next;
            }
            return true;
        }
    
        /**
         * 普通解法:半栈
         * @param head
         * @return
         */
        public boolean isPalindrome2(Node head){
            if (head == null || head.next == null){
                return true;
            }
            Node cur = head;
            Node right = head.next;
            while (cur.next != null && cur.next.next != null){
                cur = cur.next.next;
       
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值