单向/双向 链表——虚拟头结点,递归法,快慢双指针法

目录

1.设置虚拟头结点 (使得对头结点的操作和正常结点相同,避免多余操作)

2. 单向链表,节点定义  ,注意index不能取到size,增删操作要修改size

3. 双向链表,进行增删改查,注意:节点定义  ,前后指针更改

4. 递归法(画图更方便理解)

        确定递归结束条件 和 返回值

        寻找需要操作的最小数据单元,将大问题转化成多个小问题

        确定最小单元上的操作 和 返回值

5.快慢双指针法:寻找  倒数第N个节点、相遇节点


1.设置虚拟头结点 (使得对头结点的操作和正常结点相同,避免多余操作)

203. 移除链表元素:删除链表中所有满足 Node.val == val 的节点(java有GC垃圾回收机制)

步骤:1.找到该节点temp.next的前一个节点temp,

           2.该前一节点的next指针指向后一节点temp.next.next

方法:

  1. 添加一个虚拟头结点,  时间O(n),空间O(1)        删除头结点就不用另做考虑(否则另外考虑头结点情况while(head!=null&&head.val==val){ head=head.next; } )
  2. 递归  注意如何设置返回值  时间O(n),空间O(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 removeElements(ListNode head, int val) {
        //方法一:递归,最后判断头指针
        // if(head == null){
        //     return head;
        // }
        // head.next = removeElements(head.next , val);
        // return head.val == val? head.next:head;

        //方法二:迭代法,创建虚拟头结点
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode temp = dummyHead;
        while(temp.next != null){
            if(temp.next.val == val){
                temp.next = temp.next.next;
            }else{
                temp = temp.next;
            }
        }
        return dummyHead.next;//返回头指针
    }
}

2. 单向链表,节点定义  ,注意index不能取到size,增删操作要修改size

//单链表
class ListNode {
    int val;
    ListNode next;
    ListNode(){}
    ListNode(int val){
        this.val = val;
    }
}
class MyLinkedList {
    int size;
    ListNode head;
    MyLinkedList(){
        size = 0;
        head = new ListNode(0);
    }

    //获取第index个节点的数值
    public int get(int index) {
        if(index < 0 || index >= size){
            return -1;
        }
        ListNode tempNode = head; 
        for(int i = 0; i <= index; i++){
            tempNode = tempNode.next;
        }
        return tempNode.val;
    }

    //在链表最前面插入一个节点
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    //在链表的最后插入一个节点
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    public void addAtIndex(int index, int val) {
        if(index > size){
            return;
        }
        if(index < 0){
            index = 0;
        }
        size++;//注意
        ListNode tempNode = head;
        for(int i = 0; i<index; i++){
            tempNode = tempNode.next;
        }
        ListNode addNode = new ListNode(val);
        addNode.next = tempNode.next;
        tempNode.next = addNode;
        
    }

    //删除第index个节点
    public void deleteAtIndex(int index) {
       if(index < 0 || index >= size){//注意
            return;
        }
        size--;//注意
        ListNode tempNode = head; 
        for(int i = 0; i < index; i++){
            tempNode = tempNode.next;
        }
        tempNode.next = tempNode.next.next;
    }
}

3. 双向链表,进行增删改查,注意:节点定义  ,前后指针更改

class MyLinkedList {
    class ListNode {
        int val;
        ListNode next,prev;
        ListNode(int x) {val = x;}
    }

    int size;
    ListNode head,tail;//Sentinel node

    /** Initialize your data structure here. */
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
        tail = new ListNode(0);
        head.next = tail;
        tail.prev = head;
    }
    //index序列为0~size-1
    public int get(int index) {
        if(index < 0 || index >= size){return -1;}
       
        ListNode cur;
        // 通过判断 index < (size - 1) / 2 来决定是从头结点还是尾节点遍历,提高效率
        if(index < (size - 1) / 2){
            //左侧距离为0到index,为index+1步
             cur = head;
            for(int i = 0; i <= index; i++){
                cur = cur.next;
            }            
        }else{
            //右侧距离为size-1到index,为size-1-index步
            cur = tail;
            for(int i = 0; i <= size - index - 1; i++){
                cur = cur.prev;
            }
        }
        return cur.val;
    }
    //头结点之后
    public void addAtHead(int val) {
        ListNode cur = head;
        ListNode addNode = new ListNode(val);
        addNode.next = cur.next;
        addNode.prev = cur;
        cur.next.prev = addNode;
        cur.next = addNode;
        size++;
    }
    //尾结点之前   注意区分
    public void addAtTail(int val) {
        ListNode cur = tail;
        ListNode addNode = new ListNode(val);
        addNode.prev = cur.prev;
        addNode.next = cur;
        cur.prev.next = addNode;
        cur.prev= addNode;
        size++;
    }
    //index节点之后
    public void addAtIndex(int index, int val) {
        if(index > size){
            return;
        }
        if(index < 0){
            index = 0;
        }
        ListNode temp = head;
        for(int i = 0; i<index; i++){
            temp = temp.next;
        }
        ListNode addNode = new ListNode(val);
        addNode.next = temp.next;
        temp.next.prev = addNode;
        addNode.prev = temp;
        temp.next = addNode;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size){
            return;
        }
        ListNode temp =head;
        for(int i = 0; i<index; i++){
            temp = temp.next;
        }
        temp.next = temp.next.next;
        temp.next.prev = temp;
        size--;
    }
}

/**
 * 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);
 */

4. 递归法(画图更方便理解)

  1. 确定递归结束条件 和 返回值

  2. 寻找需要操作的最小数据单元,将大问题转化成多个小问题

  3. 确定最小单元上的操作 和 返回值

4. 反转链表(206反转链表206()

方法一:双指针法,每次都保存 前一节点 和 当前节点,(中间暂存下一节点,用于节点更新)

方法二:递归法   将一个发问题转化成个小问题:f(1+2+3+4)=f(1)+f(2+3+4)

1.递归结束条件(到达边界时)2. 拼接处理

处理流程:

class Solution {
    public ListNode reverseList(ListNode head) {
    // 递归:从后向前
        if(head == null){return null;}
        if(head.next == null){return head;}
        ListNode last =  reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return last;    

    // 双指针法:从前向后
    //    ListNode prev = null;
    //    ListNode cur = head;
    //    ListNode temp = null;
    //    while(cur != null){
    //        temp = cur.next;//先另存下一节点
    //        cur.next = prev;
    //        prev = cur;
    //        cur = temp;
    //    }
    //    return prev;
    }  
}

24. 两两交换链表中的节点

class Solution {
    public ListNode swapPairs(ListNode head) {
        //迭代的终止条件
        if(head == null || head.next == null){
            return head;
        }
        ListNode newSubhead = swapPairs(head.next.next);
        ListNode next = head.next;
        head.next.next = head;
        head.next = newSubhead;
        return next;
    }
}

5.快慢双指针法:寻找  倒数第N个节点、相遇节点

19. 删除链表的倒数第 N 个结点:删除链表的倒数第 n 个结点,并且返回链表的头结点。

快指针先行n+1步,然后快慢同行,确定链尾和删除节点位置

public ListNode removeNthFromEnd(ListNode head, int n) {
        //快慢双指针法:快指针先行n+1步,然后快慢同行,确定链尾和删除节点位置
         //虚拟头结点,使得删除头结点和普通结点操作相同
        ListNode dummyNode = new ListNode(0); 
        dummyNode.next = head;
        ListNode fastNode = dummyNode;
        ListNode slowNode = dummyNode;
        for(int i = 0; i < n && fastNode != null; i++){
            fastNode = fastNode.next;
        }
        while(fastNode.next != null){
             fastNode = fastNode.next;
             slowNode = slowNode.next;
        }
        slowNode.next = slowNode.next.next;
        return dummyNode.next;
}

面试题 02.07. 链表相交:找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

一个链表对应一个指针,计算两个链表长度,将尾端和指针对齐(即长链表指针先行),进行比较判断

方法一:快慢双指针法
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        ListNode getnode = null;
        int lenA = 0, lenB = 0;
        while(curA != null){
            curA = curA.next;
            lenA++;
        }
        while(curB != null){
            curB = curB.next;
            lenB++;
        }
        if(lenA > lenB){
            getnode = getIntersection(headA,headB,lenA-lenB);
        }else{
            getnode = getIntersection(headB,headA,lenB-lenA);
        }
        return getnode;
    }

    public ListNode getIntersection(ListNode lhead,ListNode shead,int len){
        ListNode curl = lhead;
        ListNode curs = shead;
        for(int i = 0; i<len; i++){
            curl = curl.next;
        }
        while(curl != curs){
            curl = curl.next;
            curs = curs.next;
            if(curs == null || curl == null){
                return null;
            }
        }
        return curl;
    }
}

142. 环形链表 II:快慢双指针:快指针2步,慢指针1步,只要存在环,两者才相遇;

public ListNode detectCycle(ListNode head) {
        //快慢双指针:快指针2步,慢指针1步,只要存在环,两者才相遇;
        ListNode fast = head;
        ListNode slow = head;
       while(fast != null && fast.next !=null){
           fast = fast.next.next;
           slow = slow.next;
           if(fast == slow){
                ListNode meet = fast;
                ListNode start = head;
                while(meet != start){
                    meet = meet.next;
                    start = start.next;
                }
                return meet;
           }
       }
       return null;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值