链表

1 链表

链表线性存储的一种结构,每一个节点存储了值对象,以及引用对象(前区节点和后节点)

java定义的基本结构

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

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", next=" + next +
                '}';
    }
}

2 链表经典

2.1 链表反转

已知链表12345,求反转54321

其根本思想是:

遍历旧的链表,将下一个节点指向其上一个节点,中间利用临时变量存储。

public class NodeDemo {

    @Test
   public void testRevertNode(){
        Node node=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,null)))));
        System.out.println(node.toString());
        Node revertNode=revertNode(node);
        System.out.println(revertNode.toString());

   }
   public Node revertNode(Node oldNode){
         Node newNode=null;//新的头结点
         while (oldNode!=null){//循环遍历旧的节点
             Node next=oldNode.next;//保存下一个节点到临时变量
             oldNode.next=newNode;//将旧的当前节点指向新的节点(可以想象旧的链表遍历第一次的时候,旧的第一个元素,反转为最后一个元素,next肯定为null,第二次的时候,旧的第二个元素,next应该指向旧的第一个元素(也就是已经赋值的newNode))
             newNode=oldNode;//新的节点,为当前节点
             oldNode=next;//旧的节点,赋值为下个节点
         }
       return newNode;
   }

}
2.2 链表区间反转(m,n)

已知链表12345,求反转0,3

结果:32145

其核心思想是,根据m找到节点的前驱,然后根据n-m+1反转链表,在head指针移动的过程中,保存记录变量,待反转完成之后,将前驱指针下一个节点指向新的链表,并综合考虑临界值的情况

具体的实现代码如下:

    @Test
    public void testRevertNodeMN(){
        Node node=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,null)))));
        System.out.println(node.toString());
        Node revertNode=reverNodeMN(node,0,3);
        System.out.println(revertNode.toString());

    }
   public Node  reverNodeMN(Node head,int m,int n){
       int length=n-m+1;
       Node preHead=null;
       Node result=head;
       while (head!=null&&m>0){
           preHead=head;
           head=head.next;
           m--;
       }
       Node modifyHead=head;//当前的节点反转之后会变成最后一个,暂时记录一下
       Node newHead=null;
       while (head!=null&&length>0){
           Node next=head.next;
           head.next=newHead;
           newHead=head;
           head=next;
           length--;
       }
       modifyHead.next=head;//head移动到这里之后,把之前的暂时记录的next指向当前的指针
       if(preHead!=null){
           preHead.next=newHead;//如果前驱不指向新的链表
       }else {
           result=newHead;//前驱为空直接重新赋值
       }
       return result;

   }
2.3 单链表倒数第k个元素

思路分析:

可以遍历链表,设置两个指针节点,fist和second first先移动k步,second后移动
循环遍历frst到结束,second指针的位置即为第k个节点

具体实现:

    @Test
    public void testFindNodeK(){
        Node node=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,null)))));
        System.out.println(node.toString());
        Node kNode=findKInNode(node,1);
        System.out.println(kNode.toString());

    }
   public Node findKInNode(Node head,int k){
       Node first=head;
       Node second=head;
       for(int i=0;i
2.4 两个链表求交点

判断两个链表中是否存在交点,如果存在则返回交点

思路1 空间换时间

利用HashSet,遍历链表A将节点放入集合,然后遍历链表B,判断在集合总是否已存在,若存在则返回节点

   @Test
   public void testFindJDNode(){
       Node k=new Node(6,null);
       Node nodeA=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,k)))));
       Node nodeB=new Node(1,new Node(3,k));
       Node kNode=findJDNode(nodeA, nodeB);
       System.out.println(kNode.toString());
   };
   public  Node findJDNode(Node headA,Node headB){
       Set nodes=new HashSet<>();
       while (headA.next!=null){
           nodes.add(headA);
           headA=headA.next;
       }
       nodes.add(headA);
       while (headB!=null){
           if(nodes.contains(headB)){
               return headB;
           }
           headB=headB.next;
       }
       return null;
   }

思路二 时间换空间

先判断两个链表的长度 ,然后将链表长的移动到和短的同等位置,然后循环遍历A和B链表,若出现相等即为交点,具体实现如下

    @Test
    public void testFindJDNode2(){
        Node k=new Node(6,null);
        Node nodeA=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,k)))));
        Node nodeB=new Node(1,new Node(3,k));
        Node kNode=findJDNode2(nodeA, nodeB);
        System.out.println(kNode.toString());
    }
    public Node findJDNode2(Node headA,Node headB){
       int a=getNodeLength(headA);
       int b=getNodeLength(headB);
       int count=0;
       if(a>b){//则指针移动b-a
           count=a-b;
           while (count>0){
               headA=headA.next;
               count--;
           }
       }else if(a0){
               headB=headB.next;
               count--;
           }
       }
       while (headA!=null&&headB!=null){
           if(headA==headB){
               return headA;
           }
           headA=headA.next;
           headB=headB.next;
       }
       return null;
   }
    private int getNodeLength(Node node){
       int count=0;
       while (node!=null){
           count++;
           node=node.next;
       }
       return count;
   }
2.5 链表求环

检测链表是否存在环路,如果存在,即返回链表的首次环节点

思路一: 可以参考双向链表的交点的思路,同样利用hashSet集合,一边遍历,一边判断集合中是否存在,若存在则返回该节点,该节点即为环入口节点。

    @Test
    public void testFindCircle(){
        Node k=new Node(6,null);
        Node nodeA=new Node(1,new Node(2,new Node(3,new Node(4,new Node(5,k)))));
        k.next=nodeA;
        Node kNode=findCirCle(nodeA);
        System.out.println(kNode.toString());
    }
    public Node findCirCle(Node node){
        Set nodes=new HashSet<>();
        while (node!=null){
            if(nodes.contains(node)){
                return node;
            }else {
                nodes.add(node);
            }
            node=node.next;
        }
        return  null;
    }

思路二 : 快慢指针赛跑

设置一个快指针和一个慢指针,遍历快指针,当快指针和慢指针相等,则说明存在环,记录该交点,同事遍历头指针和交点相等返回相遇点即为初始的环入点

具体实现如下

    @Test
    public void testFindCircle2(){
        Node node1=new Node(1,null);
        Node node2=new Node(2,null);
        Node node3=new Node(3,null);
        Node node4=new Node(4,null);
        Node node5=new Node(5,null);
        Node node6=new Node(6,null);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node6;
        node6.next=node2;
        Node kNode=findCirCle2(node1);
        System.out.println(kNode.toString());
    }
    public Node findCirCle2(Node node){
        Node fast=node.next.next;
        Node slow=node.next;
        Node meet=null;
        while (fast!=null){
            if(fast==slow){
                System.out.println(fast);
                meet=fast;
                break;
            }
            fast=fast.next.next;
            slow=slow.next;
        }
        while (node!=null&&meet!=null){
            if(node==meet){
                return node;
            }
            node=node.next;
            meet=meet.next;
        }
        return  null;
    }

你可能感兴趣的:(链表)