Java 单向列表的几种操作方式(删除,查找环,环入口)

30 篇文章 0 订阅
2 篇文章 0 订阅
本文详细介绍了Java中链表和二叉树的相关操作,包括删除节点、删除指定数值的节点、删除重复节点、检查链表是否有环以及寻找环的入口。提供了多种算法实现,如使用栈进行节点删除,利用HashSet去除重复节点,通过双指针判断链表环等。此外,还涉及了链表环的检测和入口查找问题,通过不同步速的指针判断环的存在并定位入口节点。
摘要由CSDN通过智能技术生成

Java ⼆叉树操作

本文章的列表为单项列表。

列表的节点类,类的内容包括数据int 和 下一个节点的指向。

    class Node{
        int data;
        Node next;
        public Node(int data,Node next) {
            this.data = data;
            this.next = next;

        }
    }

1.删除节点操作

     1.1是头节点为空

   1.2删除节点在链表的尾部,即删除p4

   1.3删除节点在链表的中间位置 即删除

对应的Java代码如下:

    public static void deleteNode(Node head, Node node) {
        if (head == node) {
            head = null;
        } else if (node.next == null) {
            while (head.next != node)
            {
                head = head.next;
            }
            head.next = null;
        } else {
            Node q = node.next;
            node.data = q.data;
            node.next = q.next;
        }
    }

 2.删除指定数值的节点

  2.1方案一:将数值data不等于num的节点存放到栈中,然后拿到栈的队列,然后进行出栈操作返         回头节点。如图删除data是3的节点。

对应代码

  public Node removeValue(Node head,int num) {
        Stack<Node> stack = new Stack<>();
        while (head != null) {
            if (head.data != num) {
                stack.push(head);
            }
            head = head.next;
        }
        while (!stack.isEmpty()) {
            stack.peek().next = head;
            head = stack.pop();
        }
        return head;
    }

2.2 方案二: 首先判断要删除的数字是否在节点的前几位,如果在节点的头几位,先遍历到要返回的头节点,如果删除的节点,不在头节点,就进行遍历删除操作。图片是删除数值1的节点,head指向了p2 ,之后的节点用过链表遍历删除。

public Node removeValue2(Node head,int num) {
        while(head != null) {
            if (head.data != num) {
                break;
            }
            head = head.next;
        }

        Node pre = head;
        Node cur = head;
        while (cur != null) {
            if (cur.data != num) {
                pre.next = cur.next;
            } else {
                cur = pre;
            }
            cur = cur.next;

        }
        return head;
    }

3.删除重复节点:

方案是利用HasSet的存值不可重复性,开始存储节点的值,如果节点的值在HashSet中存在,即表明此值是重复值。
 

    public void deleteDuplication(Node head) {
        if (head == null) {
            return;
        }
        HashSet<Integer> set = new HashSet<>();
        Node pre = head;
        Node cur = head.next;
        set.add(head.data);
        while (cur != null) {
            if (set.contains(cur.data)) {
                pre.next = cur.next;
            } else {
                set.add(cur.data);
                pre = cur;
            }
            cur = cur.next;
        }
    }

4.检查列表是否有环

  假设链表是一个有环链表,且由P4指向P2构成环。那么  使用两个指针   S 和 L,

 让两指针同时向后遍历  而且L的遍历速度是S的两倍,呢么如果是有环的话,

 S终究会追上L。因此我们可以 以SL是否相遇作为判    断是否有环的必要条件。

如图中的案例,SL在第散步相遇。证明有环的存在

public boolean isLoop(Node head){
        // TODO 定义两个指针,分别是S和L
        Node L= first; 
        Node S= first;
        // TODO 遍历一遍链表,如果最后是null的话就代表没有环
        while ( L != null && L.next != null){
            L = L.next.next;
            S = S.next;
            //如果俩相遇了,代表有环
            if (L == S){
                return true;
            }
        }
        return false;
    }

5.检查查找单列表否的入口

 如图所展示,这是一个有环列表的模型,S、L分别指向头节点,P为环的入口点,K为上个方法查询链表是否为环的相遇点。

接下来我们将环列表由P也就是环的入口处拆开成一条直线进行查看 

 我们将链表分为几个线段,分别是 ,a 是链表头到环入口的长度,b是环入口到S L相遇点的长度,c是相遇点再到P的长都,d是P到P环的长度

我们在查找链表是否是环的问题上利用的是 S走过的长度是L的二倍, 也就是说相同时间 S和L相遇的时候 S的路程是L的路程的两倍。

可得出如下结论:

2 * (a + b) = n * d +b +a ; 其中 n指的是 S节点在列表里面由P到P循环走过的次数 >=1; 

由于 b = d - c;

a +d - c =n * d 

a = c + (n-1) * d

所以 a和c 是差了 (n-1) * d 个 而d就是 p到p的距离,因为n必须是正整数所以 正整数  (n-1) * d可以忽略不计

等式就变成了

a = c

所以要是闭环的情况下S走过P 和L从头节点走过P时必定相遇。

我们将L重新放到头节点然后 将S从相遇节点往后遍历, 当S在 环中循环n次之后,会和L在节点入口处相遇。

具体的代码:

  public ListNode detectCycle(ListNode head) {
        ListNode S= head;
        ListNode L= head;
        while(S!= null && S.next != null){
            S= S.next.next;
            L= L.next;
            if(S== L){
                break;
            }
        }        
        if(S== null || S.next == null){
            return null;
        }
        L= head;
        while(S!= slow){
            L= L.next;
            S= S.next;    
        }
        return L;
        
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值