剑指offer链表树复习

大傻子复习之前写的代码 蠢蛋

逆转链表递归就写了一小时,真的不知道之前自己怎么写的题目

package offer;

import java.util.*;

/*
复习链表+树的习题+力扣相关习题刷
1链表从尾到头打印链表
2逆转链表 三种方法 递归 栈 双指针
3删除表中重复的节点
4链表中倒数第k个节点
5合并两个排序的链表
6两个链表的第一个公共节点
 */
class Nodes {
    int val;
    Nodes next;

    public Nodes(int val) {
        this.val = val;
    }

    public Nodes(int val, Nodes next) {
        this.val = val;
        this.next = next;
    }

    //打印
    public void print() {
        Nodes cur = this;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }
}

public class Review {
    //逆序打印链表
    public static void reversePrint(Nodes node) {
        //递归上一层出口
        if (node == null) return;
        reversePrint(node.next);
        System.out.print(node.val + " ");
    }

    //逆转链表

    /**
     * 递归逆转链表
     * 找到倒数第二个把指针逆转
     * 返回一个新链表 创建新空间
     *
     * @param head
     */
    public static Nodes transNodes(Nodes head) {
//        Nodes newhead = null;
        //情况 只有一个节点或者节点为空 递归返回条件
        if (head == null || head.next == null) return head;
        //递归往下走 找到倒数第二个节点
        //为什么head4 cur 5
        Nodes cur = transNodes(head.next);
        head.next.next = head;
        //最后一个指向空
        head.next = null;
        return cur;

    }

    /**
     * 非递归写反转链表
     *
     * @param head
     * @return
     */
    public static Nodes transNodes2(Nodes head) {
        //有一个或者为空时候
        if (head == null || head.next == null) return head;
        Nodes pre = null;
        Nodes cur = head;
        //建立一个零时变量
        Nodes tmp = null;
        while (cur != null) {

            tmp = cur.next;
            cur.next = pre;

            pre = cur;
            cur = tmp;
        }
        //要从第一个节点返回
        return pre;

    }

    /**
     * 删除表中所有重复的节点
     * 链表有序 1222345
     * 下一个节点的数值与上一个相同
     * 首先找到重复的再删除 删除一个留一个
     *
     * @param head
     */
    public static Nodes deleteSame(Nodes head) {
        if (head == null || head.next == null) return head;
        //简便的方法 只用一个指针
        Nodes cur = head;
//边界条件cur != null && cur.next != null
        while (cur.next != null) {
            //前后数值的值相等
            if (cur.val == cur.next.val) {
                //删除相等的元素
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }

    /**
     * 删除倒数第K个节点
     * 两个指针 一个先走k个节点 然后再一起走 一个到了末尾 另一个就是第K个节点返回
     * 快慢指针
     *
     * @param head
     * @return
     */
    public static Nodes deleteK(Nodes head, int k) {
        if (head == null || k < 0) return head;
        Nodes fast = head;
        Nodes slow = head;
        //p1先走K个节点
        for (int i = 0; i < k; i++) {
            //没有走到最后一个
            if (fast != null)
                fast = fast.next;
            else return null;
        }
        //两个一起走
        while (fast != null) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;

    }

    /**
     * 合并两个排序链表
     * 判断情况 1两个链表为空 2一个为空一个不为空 3 两个都不为空
     * 不为空的情况下 比较数值进行插入
     * 1 2 3 4 5
     * 4 5 6 7 8
     *
     * @param nodesA
     * @param nodesB
     * @return
     */
    public static Nodes mergeNodes(Nodes nodesA, Nodes nodesB) {
        //为空的三种情况
        if (nodesA == null && nodesB == null) return null;
        if (nodesA == null) return nodesB;
        if (nodesB == null) return nodesA;
        //新联表设置头结点
        Nodes nhead = new Nodes(-1);
        //设置一个可以移动的指针
        Nodes newhead = nhead;
        //两个都存在的情况
        while (nodesA != null && nodesB != null) {
            if (nodesA.val <= nodesB.val) {
                newhead.next = nodesA;
                nodesA = nodesA.next;
            } else {
                newhead.next = nodesB;
                nodesB = nodesB.next;
            }
            newhead = newhead.next;
        }
        //多出的节点 如果A还多余 把后面的数值全部放上
        if (nodesA != null)
            newhead.next = nodesA;
        if (nodesB != null)
            newhead.next = nodesB;
        return nhead.next;
    }

    /**
     * 从尾节点开始遍历 后进先出  找到第一个不相等的元素弹出
     * 链表结点值A  123467 B 467
     * 栈A 764321 栈B 764 相同则退栈764 commo存储最后一个相等的元素
     * 时间空间O(n+m)
     *
     * @param pHead1
     * @param pHead2
     */
    public static Nodes findFirstSame(Nodes pHead1, Nodes pHead2) {
        // 异常
        if (pHead1 == null || pHead2 == null) {
            return null;
        }

        // JDK建议双向队列Deque优先于Stack
        Deque<Nodes> s1 = new ArrayDeque<>();
        Deque<Nodes> s2 = new ArrayDeque<>();
        Nodes commonNode = null;
        // 链表入栈
        while (pHead1 != null) {
            s1.push(pHead1);
            pHead1 = pHead1.next;
        }
        while (pHead2 != null) {
            s2.push(pHead2);
            pHead2 = pHead2.next;
        }
        // 逐个弹出栈顶元素比较,commonNode储存同节点
        while (!s1.isEmpty() && !s2.isEmpty()) {
            if (s1.peek().val == s2.peek().val) {
                commonNode = s1.peek();
            }
            s1.pop();
            s2.pop();
        }
        //
        return commonNode;

    }

    /**
     * 齐头并进 相同位置进行后移
     *
     * @param pHead1
     * @param pHead2
     * @return
     */
    public static Nodes findFirstSame2(Nodes nodesA, Nodes nodesB) {
        //改进写的很乱也很杂 也很蠢
        //进行判空
        if (nodesA == null || nodesB == null) return null;
        //用指针来进行
        Nodes pHead1 = nodesA;
        Nodes pHead2 = nodesB;
        //求两个链表长度 计算长度写成函数不会更改当前的链表指针位置 不然会报错
        int length1 = getLength(nodesA);
        int length2 = getLength(nodesB);

        //移动差值 判断哪个是正数 1长1先遍历
        if (length1 >= length2) {
            int len = length1 - length2;
            while (len > 0) {
                pHead1 = pHead1.next;
                len--;
            }
        } else if (length1 < length2) {
            int len = length2 - length1;
            while (len > 0) {
                pHead2 = pHead2.next;
                len--;
            }
        }
        //舍去了长的位置开始一起走

        while (pHead1.val != pHead2.val) {
            pHead1 = pHead1.next;
            pHead2 = pHead2.next;
        }
        //找到第一个相等的链表节点
        return pHead1;
    }

    public static int getLength(Nodes pHead) {
        int length = 0;

        Nodes current = pHead;

        while (current != null) {
            length++;
            current = current.next;
        }

        return length;
    }


    public static void main(String[] args) {
        //测试用例
        Nodes nodes6 = new Nodes(7, null);
        Nodes nodes5 = new Nodes(6, nodes6);
        Nodes nodes4 = new Nodes(4, nodes5);
        Nodes nodes3 = new Nodes(3, nodes4);
        Nodes nodes2 = new Nodes(2, nodes3);
        Nodes nodes1 = new Nodes(1, nodes2);

        Nodes nodes9 = new Nodes(7, null);
        Nodes nodes8 = new Nodes(6, nodes9);
        Nodes nodes7 = new Nodes(4, nodes8);
//        nodes1.print();
//        reversePrint(nodes1);
//        Nodes nodes = deleteK(nodes1, 2);
//        nodes1.print();
//        System.out.println();
//        Nodes nodes = deleteK(nodes1, 2);
//        //输出倒数第二个节点的数值
//        System.out.println(nodes.val);
//        Nodes nodes = mergeNodes(nodes1, nodes7);
//        nodes.print();
        Nodes nodes = findFirstSame2(nodes1, nodes7);
        System.out.println(nodes.val);
    }
}
package offer;

import java.util.HashSet;

/*
链表环的入口节点
首先解决判断链表是否是个环
边界条件为一个节点或者 只有一个节点都不是环形链表

找到环入口的第一个节点
哈希表
快慢指针
 */
class RoundRoundNodes {
    int val;
    RoundRoundNodes next;

    public RoundRoundNodes(int val) {
        this.val = val;
    }

    public RoundRoundNodes(int val, RoundRoundNodes next) {
        this.val = val;
        this.next = next;
    }

}

public class Test42 {
    /**
     * 判断链表是否有环
     * 方法一 快慢指针
     * 一个每次走两步  走两步走到null 么有环
     * 一个每次都一步 走到相遇 有环
     *
     * @param head
     * @return
     */
    public static boolean containRound(RoundRoundNodes head) {
        //判空
        if (head == null || head.next == null) return false;
        /*
        //定义两个指针
        RoundRoundNodes fast = head.next.next;
        RoundRoundNodes slow = head;
        while (fast != slow) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == null) {
                return false;
            }
        }
        return true;

         */
        //修改更简洁的算法
        RoundRoundNodes fast = head;
        RoundRoundNodes slow = head;
        //判断条件
        //保证不是最后一个的情况下
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            //两个指针相遇
            if (fast == slow)
                return true;
        }
        return false;
    }

    /**
     * 方法二 用set记录数值 没往后走一个判断一个set里面是否有当前节点
     *
     * @param head
     * @return
     */
    public static boolean containRound2(RoundRoundNodes head) {
        //判空
        if (head == null || head.next == null) return false;
        HashSet<RoundRoundNodes> set = new HashSet<>();
        RoundRoundNodes cur = head;
        while (cur != null) {
            if (!set.contains(cur)) {
                set.add(cur);
                cur = cur.next;
            } else {
                return true;
            }
        }
        return false;
    }

    /**
     * 哈希表实现找到 环入口
     * 报错不知道原因
     *
     * @param head
     * @return
     */
    public static RoundRoundNodes getFisrt(RoundRoundNodes head) {
        if (head == null) return null;
        //哈希表每次存储 放不进去就返回
        RoundRoundNodes cur = head;
        HashSet<RoundRoundNodes> set = new HashSet<>();
        while (cur != null) {
            //不要看包含 要看是否添加成功
            //set.contains(cur) 不对
            boolean flag = set.add(cur);
            if (!flag) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    /**
     * 双指针的方法 找到环第一个入口
     * 先第一次返回一个节点 算整个环的长度 两个指针走相差节点的数值
     *
     * @param head
     * @return
     */
    public static RoundRoundNodes getFisrt2(RoundRoundNodes head) {
        //为空或者为一个节点的情况
        if (head == null || head.next == null) return null;
        //找到一个环节点 构建两个指针
        RoundRoundNodes fast = head;
        RoundRoundNodes slow = head;
        //判断条件 随便找到一个 在中间 不是第一个
        //保证不是最后一个的情况下
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            //两个指针相遇
            if (fast == slow) {
                while (slow != head) {
                    slow = slow.next;
                    head = head.next;
                }
                return head;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        //测试用例
        RoundRoundNodes RoundNodes5 = new RoundRoundNodes(5);
        RoundRoundNodes RoundNodes4 = new RoundRoundNodes(4, RoundNodes5);
        RoundRoundNodes RoundNodes3 = new RoundRoundNodes(3, RoundNodes4);
        RoundRoundNodes RoundNodes2 = new RoundRoundNodes(2, RoundNodes3);
        RoundRoundNodes RoundNodes1 = new RoundRoundNodes(1, RoundNodes2);
        RoundNodes5.next = RoundNodes3;
//        System.out.println(containRound2(RoundNodes1));
        RoundRoundNodes nodes = getFisrt2(RoundNodes1);
        System.out.println(nodes.val);
    }
}

java offer里面八道链表写了一天。。。没谁了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值