算法学习笔记----链表

目录

单链表反转:

判断回文:

O(N)空间

使用一半空间

不使用额外空间

将一个链表划分为小于等于大于区域

复制链表

返回两个链表相交的节点


单链表反转:
public static Node reverse(Node head) {
        
        if(head == null) return null;
        
        //第一个节点
        Node node = head.next;
        head.next = null;
        while(node != null) {
            Node temp = node.next;
            node.next = head.next;
            head.next = node;
            node = temp;
        }
        
        return head;
    }

判断回文:

判断链表是否是回文结构: 1、遍历链表将节点压栈,再遍历链表进行逐一比对 2、利用快慢指针。快指针走两步,慢指针走一步,快指针不能时慢指针来到中点位置,再压栈可省二分之一空间。 快指针条件为while(fast.next != null && fast.next.next != null) 让慢指针少走一步 3、不用额外空间:利用快慢指针使慢指针走到中间位置,反转后面的链表,从两头逐一比对,返回前反转回来

O(N)空间
//整个放入栈 第二遍遍历依次比对
    public static boolean is1(Node head) {
        
        Stack<Node> stack = new Stack<>();
        Node node = head;
        while(node != null) {
            stack.push(node);
            node = node.next;
        }
        node = head; 
        while(node != null) {
            if(node.data != stack.pop().data) {
                return false;
            }
            node = node.next;
        }
        return true;
    }

使用一半空间
//利用快慢指针将后一半放入栈 比较一半
    public static boolean is2(Node head) {
        
        Node p1 = head;
        Node p2 = head;
        
        while(p2 != null && p2.next != null) {
            p1 = p1.next;
            p2 = p2.next.next;
        }
        
        Stack<Node> stack = new Stack<>();
        while(p1 != null) {
            stack.push(p1);
            p1 = p1.next;
        }
        while(!stack.isEmpty()) {
            if(head.data != stack.pop().data) {
                return false;
            }
            head = head.next;
        }
        return true; 
    }

不使用额外空间
//利用快慢指针找到中点位置 将后半逆序再比对 比对完后复原
    public static boolean is3(Node head) {
        
        if(head == null) {
            return true;
        }
​
        Node p1 = head;
        Node p2 = head;
        
        while(p2 != null && p2.next != null) {
            p1 = p1.next;
            p2 = p2.next.next;
        }
        
        Node next = p1.next;
        Node temp;
        p1.next = null;
        while(next != null) {
            temp = next.next;
            next.next = p1;
            p1 = next;
            next = temp;
        }
        
        Node record = p1;
        p2 = head;
        boolean b = true;
        
        while(p1 != null && p2 != null) {
            if(p1.data != p2.data) {
                b = false;
                break;
            }
            p1 = p1.next;
            p2 = p2.next;
        }
        
        p1 = record;
        next = p1.next;
        p1.next = null;
        while(next != null) {
            temp = next.next;
            next.next = p1;
            p1 = next;
            next = temp;
        }
        return b;
    }

将一个链表划分为小于等于大于区域

将链表划分为小于等于大于某个数的格式: 1、将链表存入节点数组,利用快排方式分区(不保证稳定性),最后遍历数组串起来 2、不使用额外空间:定义六个变量分别为小于等于大于的头尾,遍历链表分别加入各自区域,在最后返回前串起来

package com.wtp.链表;
​
public class 将一个链表划分小于等于大于区域 {
​
    public static void main(String[] args) {
        
        
    }
    
    public static Node listPartition(Node head,int pivot) {
        Node sH = null;//小于区域头
        Node sT = null;//小于区域尾
        Node eH = null;//等于区域头
        Node eT = null;//等于区域尾
        Node mH = null;//大于区域头
        Node mT = null;//大于区域尾
        
        Node next = null;
        while(head != null) {
            next = head.next;
            head.next = null;//将当前节点摘下来
            
            if(head.data < pivot) {
                if(sH == null) {
                    sH = head;
                    sT = head;
                }else {
                    sT.next = head;
                    sT = head;
                }
            }else if(head.data == pivot) {
                if(eH == null) {
                    eH = head;
                    eT = head;
                }else {
                    eT.next = head;
                    eT = head;  
                }
            }else {
                if(mH == null) {
                    mH = head;
                    mT = head;
                }else {
                    mT.next = head;
                    mT = head;
                }
            }
            head = next;
        }
        
        if(sT != null) {//如果有小于区域
            sT.next = eH;
            eT = eT == null ? sT : eT;
        }
        if(eT != null) {//如果小于区域和等于区域不是都没有
            eT.next = mH;
        }
        
        return sH != null ? sH : (eT == null ? mH : eH);
    }
}
​
复制链表

给定一个链表,链表节点有一个指向任意节点的指针(Node rand),写一个方法复制这个链表并返回头节点

用额外空间:准备一张哈希表(key-旧节点,value-新节点),先遍历一遍链表设置旧节点对应新节点,再遍历一遍链表通过旧链表节点取出新链表节点设置新节点的rand

不用额外空间:将链表加工成旧-新-旧-新...的形式,新节点的rand指向为旧节点rand指向的下一个节点,最后分离新老链表

package com.wtp.链表;
​
import java.util.HashMap;
import java.util.Map;
​
public class 复制链表 {
​
    public static void main(String[] args) {
        
        
    }
    
    //不用额外空间
    public static Node copy2(Node head) {
        
        if(head == null) {
            return null;
        }
        
        Node cur = head;
        Node next ;
        while(cur != null) {
            next = cur.next;
            Node node = new Node(cur.data);
            node.next = next;
            cur.next = node;
            cur = next;
        }
        
        cur = head;
        while(cur != null) {
            next = cur.next.next;
            cur.next.rand = cur.rand != null ? cur.rand.next : null;
            cur = next;
        }
        
        cur = head;
        head = head.next;
        while(cur != null) {
            next = cur.next;
            cur.next = next != null ? next.next : null;
            cur = next;
        }
        
        return head;
    }
    
    //用额外空间
    public static Node copy1(Node head) {
        
        Map<Node,Node> map = new HashMap<>();
        Node cur = head;
        while(cur != null) {
            Node node = new Node(cur.data);
            map.put(cur,node);
            cur = cur.next;
        }
        cur = head;
        while(cur != null) {
            Node node = map.get(cur);
            node.setRand(map.get(cur.getRand()));
            node.setNext(map.get(cur.getNext()));
            cur = cur.next;
        }
        
        return map.get(head);
    }
}
​

返回两个链表相交的节点

两个链表可能有环,给定两个链表的头返回两个相交的节点

判断链表是否有环: 1、遍历链表时判断哈希表中是否有该节点,若有则该节点为环的起点 2、利用快慢指针,快指针停下时重新赋值为head,快慢指针各自走一步,相遇时该节点为环的起点

判断两条无环链表是否相交: 先判断最后节点是否相等,不相等不相交,先移动指向长链表的指针,到相同位置后两链表逐个比对节点,相等就返回

一个有环一个无环的链表不可能相交

package com.wtp.链表;
​
public class 返回两个链表相交的节点 {
​
    public static void main(String[] args) {
        
        
    }
    
    public static Node process(Node head1,Node head2) {
        
        if(head1 == null || head2 == null) {
            return null;    
        }
        
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        
        //两条链表都无环时平行或相交
        if(loop1 == null && loop2 == null) {
            return noLoop(head1,head2);
        }else if(loop1 != null && loop2 != null){//两条链表都有环
            return bothLoop(head1,loop1, head2,loop2);
        }
        return null;
    }
    
    //两条链表都有环时 判断
    public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2) {
        
        Node p1 = head1;
        Node p2 = head2;
        
        //情况1 两链表不相交
        
        //情况2 两条链表的第一个相交的节点在入环节点外面 有相同的入环节点
        if(loop1 == loop2) {
            int n = 0;
            
            while(p1 != loop1) {
                p1 = p1.next;
                n++;
            }
            while(p2 != loop2) {
                p2 = p2.next;
                n--;
            }
            
            p1 = n < 0 ? head2 : head1;
            p1 = p1 == head1 ? head2 : head1;
            n = Math.abs(n);
            
            while(n-- > 0) {
                p1 = p1.next;
            }
            while(p1 != p2) {
                p1 = p1.next;
                p2 = p2.next;
            }
            return p1;
        }else {
            //情况3 两条链表的第一个相交的节点在环上 (相交节点为head1或head2的入环节点)
            p1 = loop1.next;
            while(p1 != loop1) {
                //如果在一条链的环中遇到了另一条链的入环节点则相交
                if(p1 == loop2) {
                    return p1;
                }
            }
            //没有遇到入环节点 不相交
            return null;
        }
        
        
    }
    
    //两条链表都无环时 判断
    public static Node noLoop(Node head1,Node head2) {
        
        if(head1 == null || head2 == null) {
            return null;    
        }
        
        int n = 0;
        Node p1 = head1;
        Node p2 = head2;
        
        while(p1.next != null) {
            n++;
            p1 = p1.next;
        }
        while(p2.next != null) {
            n--;
            p2 = p2.next;
        }
        
        //如果两条链表最后的节点不同则不相交
        if(p1 != p2) {
            return null;
        }
        
        //p1为长链 p2为短链
        p1 = n < 0 ? head2 : head1;
        p2 = p1 == head1 ? head2 : head1;
        
        n = Math.abs(n);
        while(n-- > 0) {
            p1 = p1.next;
        }
        
        while(p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
        }
        
        return p1;
    }
     
    //链表可能有环也可能无环 返回入环节点
    public static Node getLoopNode(Node head) {
        
        if(head == null || head.next == null || head.next.next == null) {
            return null;
        }
        
        Node p1 = head.next;
        Node p2 = head.next.next;
        
        while(p1 != p2) {
            if(p2.next == null || p2.next.next == null) {
                return null;
            }
            p1 = p1.next;
            p2 = p2.next.next;
        }
        
        p2 = head;
        while(p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
    
}
​
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值