算法基础部分-链表总结

本文介绍了多种链表操作的方法,包括判断单链表是否为回文结构,两种不同的空间复杂度解决方案,以及复制带有随机指针的链表。此外,还探讨了如何在不改变结构的情况下判断回文,以及如何处理链表相交问题,包括考虑环的存在。这些算法展示了对链表数据结构的深入理解和巧妙操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class LinkedList {


    public static class Node {
        public int value;
        public Node next;
        public Node rand;
        public int C;
        public int P;

        public Node(int data) {
            this.value = data;
        }

        public Node(int p, int c) {
            this.P = p;
            this.C = c;
        }

    }

    //    判断当前的单链表是否是回文结构(笔试方法)
    public static boolean isPalindroml(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
//        首先定义一个stack, stack是先进后出的结构
        Stack<Node> stack = new Stack<>();
//        定义一个变量节点cur 然后把所有next都按进栈里面
        Node cur = head;
//        如果next不为null则按入栈中
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        //开始进行比较 把head 和stack的弹出值进行比较 从第一个开始
        while (head != null) {
//            如果两者值不相同则不是回文结构,则返回false stack 每次pop就会弹出一个数,然后size自动-- 了
            if (head.value != stack.pop().value) {
                return false;
            }
//            如果 相同则指向下一个
            head = head.next;
        }
        return true;
    }

    //    判断当前的单链表是否是回文结构(笔试方法(空间复杂度少一些))
    public static boolean ispalindroml2(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
        //首先通过快慢指针来确定这个链表的中点位置(快指针走两步慢指针走一步)
        Node kuai = head;
        Node man = head;
        Node mid = null;
        Stack<Node> stack = new Stack<>();//定义一个node类型的栈
//        如果快指针的next=null说明是奇数 快指针的next的next是null说明是偶数
        //只能写成kuai.next!=null&&kuai.next.next!=null 不能写成 kuai.next.next!=null&&kuai.next!=null
        //不然会报错空指针 这个顺序要注意
        while (kuai.next != null && kuai.next.next != null) {
            kuai = kuai.next.next;
            man = man.next;
        }
//        如果是奇数的单链表则中间点就是man指针
        if (kuai.next == null) {
            mid = man;
//            如果是偶数的单链表则man指针的next就是中间点
        } else if (kuai.next.next == null) {
            mid = man.next;
        }
//        结束条件是mid=null
        while (mid != null) {
//            从mid指针开始push进栈
            stack.push(mid);
//            向后移动
            mid = mid.next;
        }
//        结束条件是size<=0
        while (stack.size() > 0) {
//            用头节点和栈弹出的value进行比较如果不相同那么就不是回文结构那么就返回false
            if (head.value != stack.pop().value) {
                return false;
            }
//            如果相同则比较下一个
            head = head.next;
        }
        return true;

    }

    //    倒叙单链表
    public static Node nixuNode(Node a) {
        Node p1 = a;
        Node p2 = a.next;
        Node p3 = null;
        p1.next = null;
        while (p2 != null) {
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
        return p1;
    }

    //    判断当前的单链表是否是回文结构(面试方法)在不改变结构的方式上
    public static boolean ispalindrom3(Node head) {
//        首先如果当前单链表只有一个节点或者没有节点则返回
        if (head == null || head.next == null) {
            return true;
        }
//        定义一个快指针和一个慢指针
        Node kuai = head;
        Node man = head;
//        当快指针和的下一个指向为null或者下下个指向为null 就结束循环
        while (kuai.next != null && kuai.next.next != null) {
            kuai = kuai.next.next;
            man = man.next;
        }
        Node mid = man;
//        如果是偶数
        if (kuai.next != null && kuai.next.next == null) {
            man = man.next;
        }
        Node p1 = man;
        Node p2 = p1.next;
        mid.next = null;
        p1.next = null;
        Node p3 = null;
//        将中间点指向null
//        对右半边进行逆序
        while (p2 != null) {
//            保持p3是p2的后一个
            p3 = p2.next;
            //将p2指向p1
            p2.next = p1;
//            p1 向后移动
            p1 = p2;
//            p2向后移动;
            p2 = p3;
        }
//        循环结束现在左半边是逆序的并且头是p1
        Node head2 = p1;
        Node head3 = head;
//        两个头相对next
        while (head2.next != null) {
            if (head2.value != head3.value) {
                return false;
            }
            head2 = head2.next;
            head3 = head3.next;
        }
//        恢复单链表的顺序
        p2 = p1.next;
        p3 = null;
        p1.next = null;
        while (p2 != null) {
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
//        偶数单链表 则需要连接起来 ,奇数的话 在恢复单链表的时候就已经连接起来了
        if (head2 != head3) {
            head3.next = p1;
        }
        return true;
    }

    public static boolean isplindrom4(Node head) {
//        开头判断这个单链表是否只有一个节点或者没有结点如果是则返回
        if (head == null || head.next == null) {
            return true;
        }
//        首先用快慢指针将中间点的位置找到 快指针一下移动两步 慢指针
//        一下移动一步
        Node kuai = head;
        Node man = head;
//    奇数单链表快指指针最后只能走一步,偶数的单链表快指针最后还是两步一起走
        boolean isjo = false;
        while (kuai.next != null && kuai.next.next != null) {
            kuai = kuai.next.next;
            man = man.next;
        }
        Node mid = man;
//        目前的中间点需要判断一下
//        如果是奇数单链表目前慢指针指向的是正中,如果是偶数的单链表目前指向的是上中
//        现在对单链表右半部分进行逆序
//        逆序开始的点很重要,如果是奇数单链表那么逆序开始的点是正中间,如果是偶数单链表则逆序开始的点是下中
//        如果是偶数 isjo为true
        if (kuai.next != null && kuai.next.next == null) {
            mid = mid.next;
            man.next = null;
            isjo = true;
        }
//        然后开始准备逆序,逆序需要三个指针
        Node p1 = mid;
        Node p2 = p1.next;
        Node p3 = null;
//        将中点下一个指向为null
        mid.next = null;
        while (p2 != null) {
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
        //目前单链表就变成了右半边是逆序的了然后中间位置都是指向的null
        //目前的右边的头还是head 左边的头就变成了p1
        Node lefthead = head;
        Node righthead = p1;
        //然后从两边头开始向中间走如果有不一样的就返回false
        while (lefthead.next != null) {
            if (lefthead.value != righthead.value) {
                return false;
            }
            //如果一样则继续往中间同时走
            lefthead = lefthead.next;
            righthead = righthead.next;
        }
        //现在都已经走到了最后一个数 目前要做的是要把单链表的右半边逆序还原
//        还原要从右半边的头开始,右半边的头是p1
        p2 = p1.next;
        p3 = null;
        p1.next = null;
        while (p2 != null) {
            p3 = p2.next;
            p2.next = p1;
            p1 = p2;
            p2 = p3;
        }
//        目前p2已经指向null了p1则是最后一个数 现在要把左半边和右半边连接起来,因为之前把左半边最后一个数也指向null 了
        //现在还是要根据偶数和奇数右其他的不同的地方
        //如果是偶数isjo是true 如果是奇数isgo是false 则直接溜了
        if (isjo) {
            //那么p1就等于倒数第二个位置
//           然后连接起来
            lefthead.next = p1;
        }
        return true;
    }

    //如何对一个单链表 指定一个数使得小于这个数的都在左边,等于这个数的都在中间,大于这个数的都在右边
    public static Node listPartition2(Node head, int pivot) {
//        首先定义六个node
//        <区域的头和尾
        Node sl = null;
        Node sr = null;
//        =区域的头和尾
        Node midl = null;
        Node midr = null;
//        >区域的头和尾
        Node bl = null;
        Node br = null;
//        定义一个next指针
        Node next = null;
        while (head != null) {
//            next 首先赋值
            next = head.next;
//            然后把head变成单独的节点
            head.next = null;
//            当head的value<pivot
            if (head.value < pivot) {
                if (sl == null) {
//                    一开始遇到了小于的数则把这个数赋值给小于区域的左右边界
                    sl = head;
                    sr = head;
//                    从第二次开始就只用管右边界了
                } else {
//                    将当前的右边界指向head 然后再将head赋值给右边界
                    sr.next = head;
                    sr = head;
                }
//                当head的value=pivot
            } else if (head.value == pivot) {
                if (midl == null) {
                    midl = head;
                    midr = head;
                } else {
                    midr.next = head;
                    midr = head;
                }
//                当head的value>pivot
            } else {
                if (bl == null) {
                    bl = head;
                    br = head;
                } else {
                    br.next = head;
                    br = head;
                }
            }
            head = next;
        }
//        三部分都有的情况
        if (sl != null && midl != null && bl != null) {
            sr.next = midl;
            midr.next = bl;
        }
//        只有大于区域的情况
        if (sl == null && midl == null && bl != null) {
            sl = bl;
        }
//        只有小于区域和等于区域的情况
        if (sl != null && midl != null && bl == null) {
            sr.next = midl;
        }
//        只有小于区域和大于区域的情况
        if (sl != null && midl == null && bl != null) {
            sr.next = bl;
        }
//        只有等于区域的情况
        if (sl == null & midl != null & bl == null) {
            sl = midl;
        }
//        只有等于和大于的的情况
        if (sl == null && midl != null && bl != null) {
            midr.next = bl;
            sl = midl;
        }
        return sr;

    }

    //    复制含有随机指针的链表 方法一用hashMap 笔试用
    public static Node copyLinklistNode(Node head) {
//        首先定义一个node
        Node cur = head;
        //创建一个hashMap
        HashMap<Node, Node> map = new HashMap<>();
        while (cur != null) {
//            然后将老节点作为key ,new 一个节点然后把老节点的值初始化给新节点作为value
            map.put(cur, new Node(cur.value));
            cur = cur.next;
        }
//        使得头节点变成cur
        cur = head;
        while (cur != null) {
//            获得以老节点为key 的value 也就是新节点 然后把新节点的next 指向下一个新节点的next
            map.get(cur).next = map.get(cur.next);
//            将新节点的rand指向老节点的rand对应的新节点
            map.get(cur).rand = map.get(cur.rand);
            cur = cur.next;
        }
//        返回头

        return map.get(head);

    }

    //复制含有随机指针的两边 方法二 面试用
    public static Node copyLinklistNode2(Node head) {
        if (head == null) {
            return null;
        }
//    首先定义一个cur指针
        Node cur = head;
        Node next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = new Node(cur.value);
            cur.next.next = next;
            cur = next;
        }
        //原单链表 1 ->  2 ->  3  -> 4
        //现在    1->1->2->2->3->3->4->4
//        新的节点是原节点的下一个
        Node copynode = null;
        cur = head;
        while (cur != null) {
            next = cur.next.next;
            copynode = cur.next;
            copynode.rand = cur.rand != null ? (cur.rand.next != null ? cur.rand.next : null) : null;
            cur = next;
        }
//        目前的单链表还是 1->1->2->2->3->3->4->4 但是新节点中的rand节点都有了指向
//        将原节点还原,将新节点提出来
        Node res = head.next;
        cur = head;
        while (cur != null) {
            next = cur.next.next;
            copynode = cur.next;
            cur.next = next;
            copynode.next = next != null ? (next.next != null ? next.next : null) : null;
            cur = next;
        }
        return res;
    }

    //给定两个可能有环也可以无环的单链表,头节点head1和head2,请实现一个函数,如果两个链表相交,请返回
    //相交的第一个节点,如果不相交,返回null
    //要求如果两个链表的长度之和为N,时间复杂度达到O(N)额外空间复杂度达到O(1)
    //首先实现一个判断链表是否有环的函数并且返回环开始的位置没有环返回null
    public static Node ishuan(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
//        定义快慢指针 快指针一下走两步,慢指针一下走一步
        Node kuai = head.next.next;
        Node man = head.next;
//        当快指针的next 或者next.next为空了说明这是一个无环的直接返回false
        while (kuai != man) {
            if (kuai.next == null || kuai.next.next == null) {
                return null;
            }
            kuai = kuai.next.next;
            man = man.next;
        }
        //快指针回到头然后一次走一步 跟慢指针同时走
        kuai = head;
        while (kuai != man) {
            kuai = kuai.next;
            man = man.next;
        }
        return kuai;
    }

    //判断是否相交的主方法
    public static Node isxiangjiao(Node head1, Node head2) {
//        首先对两个链表进行是否有环判断
        Node loop1 = ishuan(head1);
        Node loop2 = ishuan(head2);
        int temp = 0;
        Node cur = head1;
        //第一种情况都没有环的情况
        if (loop1 == null && loop2 == null) {
            while (cur != null) {
                cur = cur.next;
                temp++;
            }
            cur = head2;
            while (cur != null) {
                cur = cur.next;
                temp--;
            }
//            如果temp>=0 说明head1的长度大于head2 的长度或者等于
            if (temp >= 0) {
//                head1走到差值的地方
                while (temp > 0) {
                    head1 = head1.next;
                    temp--;
                }
            } else {
                temp = Math.abs(temp);
                while (temp > 0) {
                    head2 = head2.next;
                    temp--;
                }
            }
            //此时的head1和head2是同一起跑线
            while (head1 != null && head2 != null) {
                head1 = head1.next;
                head2 = head2.next;
                if (head1 == head2) {
                    return head1;
                }
            }
//                两个链表都有环的情况 只有这两种情况,不存在一个有环一个没环还可以相交,画不出来的
        } else if (loop1 != null && loop2 != null) {
//            第一种情况两个链表的环的开始节点是一样的
            if (loop1 == loop2) {
                while (cur != loop1) {
                    cur = cur.next;
                    temp++;
                }
                cur = head2;
                while (cur != loop1) {
                    cur = cur.next;
                    temp--;
                }
                if (temp >= 0) {
//                head1走到差值的地方
                    while (temp > 0) {
                        head1 = head1.next;
                        temp--;
                    }
                } else {
                    temp = Math.abs(temp);
                    while (temp > 0) {
                        head2 = head2.next;
                        temp--;
                    }
                }
                //此时的head1和head2是同一起跑线
                while (head1 != loop1 && head2 != loop1) {
                    head1 = head1.next;
                    head2 = head2.next;
                    if (head1 == head2) {
                        return head1;
                    }
                }

            }
//            第二种情况是 两个链表的环的开始节点是不一样的但是也是一个环
            if (loop1 != loop2) {
                cur = loop1;
                while (cur != loop2) {
                    cur = cur.next;
                    if (cur == loop1) {
                        return null;
                    }
                }
                return cur;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        Node n1 = new Node(2);
        Node n2 = new Node(3);
        Node n3 = new Node(4);
        Node n4 = new Node(5);
        Node n5 = new Node(6);
        Node n6 = new Node(7);
        head.next = n1;
        n1.next = n2;
        n2.next = n3;
        n3.next = n4;
        n4.next = n5;
        n5.next = n6;
        n6.next = n3;
        Node head2 = new Node(1);
        Node n11 = new Node(2);
        Node n22 = new Node(3);
        Node n33 = new Node(9);
        head2.next = n11;
        n11.next = n22;
        n22.next = n33;
        n33.next = n5;
        Node node = isxiangjiao(head, head2);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值