链表的相关方法

1.虚拟头节点的设置

在链表问题中,我们直接控制节点往往不好操作,往往需要设置一个虚拟头节点来模拟操作。

       ListNode dummy = new ListNode(-1,head);//连接两个链表,且当前链表头值为-1
        ListNode pre =dummy;//指针指向dummy 

移除链表元素

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode(-1,head);//连接两个链表,且当前链表头值为-1
        ListNode pre =dummy;//指针指向dummy               

        while(pre.next!=null){
            if(pre.next.val!=val){
                pre=pre.next;
            }else{
                pre.next=pre.next.next;//连接pre节点的后两个节点
            }
        }
        return dummy.next;

    }
}

2.设计链表

涉及到列表的增删(双链表,单链表)

设计链表

单链表

/*
*  实体类
*/
class ListNode{
    int val;//当前节点的值
    ListNode next;//指向下一个节点的指针
    ListNode(){}//无参构造方法
    ListNode (int val){
        this.val=val;
    }
}


/*
*  工具类
*/
class MyLinkedList {
    int size;//size存储链表元素的个数
    ListNode head;//设置虚拟头节点
    //初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);//虚拟头节点,并不是真正的头节点
    }
    //获取链表中第 index 个节点的值。
    public int get(int index) {
        //如果index非法返回-1
        if(index<0||index>=size){
            return -1;
        }
        ListNode currentNode = head;
        for(int i =0;i<index+1;i++){
            currentNode=currentNode.next;
        }
        return currentNode.val;
    }
     //在链表的第一个元素之前添加一个值为 val 的节点。
    public void addAtHead(int val) {
        addAtIndex(0,val);

    }
     //将值为 val 的节点追加到链表的最后一个元素。
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    //在链表中的第 index 个节点之前添加值为val的节点。
    public void addAtIndex(int index, int val) {
        if(index>size){
            return;
        }
        if(index<0){
            index=0;
        }
        size++;//要插入,所以要加一
        ListNode pred = head;
        for(int i =0;i<index;i++){//得到index的前一个节点
            pred=pred.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = pred.next;
        pred.next = toAdd;
    }
     //deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
    public void deleteAtIndex(int index) {
         if (index < 0 || index >= size) {
            return;
        }
        size--;
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }
}
/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

3.删除链表的倒数第n个节点

思路:
1.删除链表,链表反转等问题一般需要设置虚拟头节点
2.快慢指针处理:从虚拟头节点开始,快指针先向前移动n个节点,然后快慢节点一起移动,这样就保证了,慢指针所处的位置时链表的倒数第n个节点。

删除链表的倒数第n个节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next=head;
        ListNode slow = dummy;//慢指针
        ListNode fsst = dummy;//快指针

        //快指针移动向前移动n个节点
        for(int i =0;i<n;i++){
            fsst = fsst.next;
        }
        //快指针移动到链表尾部,同时慢指针也移动
        while(fsst.next!=null){
            fsst=fsst.next;
            slow=slow.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
}

4.面试题 02.07. 链表相交

思路:
1.分别得到a,b链表的长度
2.然后将两个链表尾部对齐
3.从头对齐处开始遍历,如果两个节点相等,返回该节点。否则返回null

面试题 02.07. 链表相交

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA =0;//链表A的长度
        int lenB =0;//链表B的长度

        //链表A的长度
        while(curA!=null){
            curA=curA.next;
            lenA++;
        }
        //链表B的长度
        while(curB!=null){
            curB = curB.next;
            lenB++;
        }

        curA=headA;
        curB=headB;

        //让curA成为最长链表的头,lenA为它的长度
        if(lenB>lenA){
            int temp = lenA;
            lenA =lenB;
            lenB =temp;

            ListNode tempNode = curA;
            curA =curB;
            curB = tempNode;
        }
        //求长度差
        int gap = lenA-lenB;
        //两个链表末尾位置对齐
        while(gap!=0){
            curA=curA.next;
            gap--;
        }
        //遍历curA和curB,遇到相同的就直接返回
        while(curA!=null){
            if(curA==curB){
                return curA;
            }
            curA=curA.next;
            curB =curB.next;
        }
        return null;
    }
}

5.环形链表II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
   //1.先用快慢指针判断是否有环,倘若二者能够相遇说明存在环
    //2.x为头节点到环形入口的节点,y为环形入口的节点到相遇位置的节点,z为相遇位置到环形入口的节点。
    //3.我们有(x+y)*2=x+y+n(y+z),化解得到x=(n-1)z
    //4.这个式子说明了,当头节点和相遇节点的位置,各自向前走一步,相遇位置就是环的入口
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;

        while(fast!=null&&fast.next!=null){
            slow=slow.next;//慢指针移动一位
            fast=fast.next.next;//快指针移动两位
            
            if(slow==fast){//有环
                ListNode index1 =head;//起始位置
                ListNode index2 = slow;//相遇位置
                //两个指针,从头节点和相遇节点开始,各走一步,直到形象与,相遇即为环入口
                while(index1!=index2){
                    index1=index1.next;
                    index2=index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值