《算法通关村第一关-链表白银挑战-经典问题之‘第一个公共子节点’、‘部分删除链表’元素笔记》

前言:

  • 第一关是链表的基本问题,第二关是链表反转以及拓展问题。
  • 主要内容是:链表高频面试算法题

重点:

  • 上一篇针对链表分别学了一个创建还有增删改查,接下来的重点是开拓思路/思维,思考解题思想,以及可以自己写出来。

要点:

  • 遇到算法时,思路可以是怎么样的,总结出自己的处理方式,并且小结上手代码后的经验

小结:

1、两个链表第一个公共子节点代码:

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;


@SuppressWarnings("all")
public class FindFirstCommentNode_ {
    public static void main(String[] args) {
        LinkNode headA = new LinkNode(1);
        LinkNode B = new LinkNode(2);
        LinkNode C = new LinkNode(2);
        LinkNode D = new LinkNode(2);
        LinkNode E = new LinkNode(2);
        headA.next = B;
        B.next = C;
        C.next = D;
        D.next = E;
        hi(headA);
        System.out.println(headA);
    }
    public static void hi(LinkNode headA){
        headA = headA.next;
    }
    //用HashMap找第一个公共节点
    public static LinkNode userHash(LinkNode headA,LinkNode headB){
        //注意,因为后续不再需要返回(/使用)head,所以不用担心狗熊掰棒子的问题
        //先把链表A存到hashset里面
        Set<LinkNode> linkNodesA = new HashSet<>();
        while (headA != null){
            linkNodesA.add(headA);
            headA = headA.next;
        }
        //依次判断链表B是否由于A相同的节点
        while (headB != null){
            if(linkNodesA.contains(headB)){
                return headB;
            }
            headB = headB.next;
        }
        return null;
    }
    //用栈找第一个公共节点
    public static LinkNode userStack(LinkNode headA,LinkNode headB){
        Stack<LinkNode> stackA = new Stack<>();
        Stack<LinkNode> stackB = new Stack<>();
        while (headA != null){
            stackA.push(headA);
            headA = headA.next;
        }
        while (headB != null){
            stackB.push(headB);
            headB = headB.next;
        }
        LinkNode temp = null;
        while (stackA.size() > 0 && stackB.size() > 0){
            if(stackA.peek() == stackB.peek()){
                temp = stackA.pop();
                stackB.pop();
            }else {
                //从这以下,反回结果都一样,所以可以简化
                break;
            }
        }
        return temp;
    }
    //通过拼接的方式找第一个公共节点
    public LinkNode useJoint(LinkNode headA,LinkNode headB){
        if(headA == null || headB == null){
            return null;
        }
        LinkNode flagA = headA;
        LinkNode flagB = headB;
        while (flagA != null && flagB != null){
            flagA = flagA.next;
            flagB = flagB.next;
        }
        //先拼接短的,再拼接长的
        if(flagA == null){
            flagA = headB;
            while (flagB != null){
                flagB = flagB.next;
                flagA = flagA.next;
            }
            flagB = headA;
        }
        if(flagB == null){
            flagB = headA;
            while (flagA != null){
                flagA = flagA.next;
                flagB = flagB.next;
            }
            flagA = headB;
        }
        //已经等长了,一直判断到结束即可
        //这里可以简化,因为AB都为null时,自然要退出循环,且当AB
        //相同(不为空)时,也要退出循环,所以可以把这两种情况写在一起
        //所以可以知道,分析while循环的时候,需要详细的把什么时候开始,和什么时候结束搞明白
        while (flagA != flagB){
            flagA = flagA.next;
            flagB = flagB.next;
        }
        return flagA;
    }
    //通过求差,找第一个公共节点(涉及双指针)
    public LinkNode useDiffe(LinkNode headA,LinkNode headB){
        int lengthA = 0;
        int lengthB = 0;
        LinkNode flagA = headA;
        LinkNode flagB = headB;
        //求两链表长度
        while(flagA != null) {
            flagA = flagA.next;
            lengthA++;
        }
        while(flagB != null) {
            flagB = flagB.next;
            lengthB++;
        }
        int dif = lengthA > lengthB ? lengthA -lengthB : lengthB - lengthA;
        //求让长的先走
        if(lengthA > lengthB){
            while (dif > 0){
                headA = headA.next;
                dif--;
            }
        }else {
            while (dif > 0){
                headB = headB.next;
                dif--;
            }
        }
        //一起走
        while (headA != headB){
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }

}

class LinkNode{
    public int val;
    public LinkNode next;

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

    @Override
    public String toString() {
        LinkNode i = this;
        String s = "";
        while (i != null){
            s += i.val + "";
            i = i.next;
        }
        return s;
    }
}

2、删除链表元素代码: 

2.1、删除倒数元素代码:

import java.util.Stack;

//删除倒数第K个元素
//只展示了使用栈实现的方式
@SuppressWarnings("all")
public class DeleteReciprocalKNode {
    //使用栈
    public LinkNode useStack(LinkNode head, int k){
        if(head ==null){
            return head;
        }
        Stack<LinkNode> linkNodes = new Stack<>();
        LinkNode cur = head;
        LinkNode dummy = new LinkNode(0);
        dummy.next = head;
        //添加到栈中
        while (dummy != null){
            linkNodes.push(dummy);
            dummy= dummy.next;
        }
        //判断长度
        if((k > linkNodes.size() - 1) || k < 1){
            return head;
        }
        //开始删除
        while (k > 0){
            linkNodes.pop();
            k--;
        }
        LinkNode node = linkNodes.pop();
        node.next = node.next.next;
        return node;
    }


    static class LinkNode{
        public int val;
        public LinkNode next;
        public LinkNode(int val) {
            this.val = val;
        }

        @Override
        public String toString() {
            LinkNode i = this;
            String s = "";
            while (i != null){
                s += i.val + "";
                i = i.next;
            }
            return s;
        }
    }
}

2.2、删除重复元素代码:

public class DeleteDuplateNode_ {
    /**
     * 重复元素保留一个
     *
     * @param head
     * @return 头指针
     */
    public static ListNode deleteDuplicate(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode cur = head;
        while (cur.next != null) {
            if (cur.val == cur.next.val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }

    /**
     * 重复元素都不要
     *
     * @param head
     * @return 头指针
     */
    public static ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return head;
        }

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while (cur.next != null && cur.next.next != null) {
            if (cur.next.val == cur.next.next.val) {
                int x = cur.next.val;
                while (cur.next != null && cur.next.val == x) {
                    cur.next = cur.next.next;
                }
            } else {
                cur = cur.next;
            }
        }

        return dummy.next;
    }


    static class LinkNode{
        public int val;
        public LinkNode next;
        public LinkNode(int val) {
            this.val = val;
        }
        @Override
        public String toString() {
            LinkNode i = this;
            String s = "";
            while (i != null){
                s += i.val + "";
                i = i.next;
            }
            return s;
        }
    }
}

问答:

1、为什么老师的代码使用到了静态内部类?内部类和静态内部类的区别是?

答:因为将节点类放在内部类的位置后,如果其他的静态方法要创建这节点对象,那就得将其设置为静态的才不会报错。另外,静态内部类不依赖与引用。

其他/心得:

1、确定好处理的方式后,在思路分析时,可以考虑从整体或细节方面分析,比如:整体分析,就是较整体的角度思考代码的运行流程。整体分析的好处就是代码看起来更简洁,相对的,细节分析的好处就是,运行的时间复杂度会好一些。具体代码的例子就是,在通过拼接的方式来实现寻找两个链表第一个公共子节点代码中,可以明显看出思路考虑的方向;

2、这些高频题目,实际上最后是会落实到增删改查的,所以这个基础需要记得;

3、分析while循环的时候,可以具体的分析把什么时候开始,和什么时候结束的情况列举出来,这样可以简写些自己一时间看不出来的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值