算法-链表操作

49 阅读4分钟

链表的定义和内存存储方式

链表使用非连续的内存空间来存储数据,通过next指针将内存块串联在一起

解题技巧

链表相关问题都会涉及遍历,核心是通过画图举例来确定遍历的三要素

  1. 遍历的结束条件: p==null or p.next == null
  2. 指针的初始值:p=head or 。。。
  3. 遍历的核心逻辑: 需要根据题目要求来定

特殊情况需要处理:空链表、头结点、尾节点等;

引入虚拟节点:是否可以通过添加虚拟节点来简化编程(需要看下头结点是否需要特殊处理,来判断是否需要引入头结点)

头插尾插 链表反转用的头插,大部分情况用尾插;

  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; }
  }
 // 虚拟节点
ListNode newHead = new ListNode();
 newHead.next = head;

例题

203 移除链表元素

思考:这个题目只用到了一个指针去做,需要保留待删除前面的指针,故使用prev.next.val 去做判断;

/**
 * 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 null;
        ListNode newHead = new ListNode();
        newHead.next = head;
        ListNode p = newHead;
        while(p.next != null){
            if(p.next.val == val){
                p.next = p.next.next;
            }else{
                p = p.next;
            }
        }
        return newHead.next;
    }
}

876 链表中间节点

思考:这个题目没有用到虚拟头结点 解题思路:分成奇数和偶数的情况去判断,最后合并

image.png

/**
 * 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 middleNode(ListNode head) {
        ListNode p1 = head;
        ListNode p2 = head;
        while(p1 != null && p1.next != null ){
            p1 = p1.next.next;
            p2 = p2.next;
        }
        return p2;

    }
}

21合并两个有序链表

思路:新开一个头结点,用尾插的方式插入数据

/**
 * 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 mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode newHead = new ListNode();
        ListNode tail = newHead;
        tail.next = null;
        ListNode p1 = list1;
        ListNode p2 = list2;
        while(p1 !=null && p2 !=null){
            if(p1.val < p2.val){
                tail.next = p1;
                tail = p1;
                p1 = p1.next;
            }else{
                tail.next = p2;
                tail = p2;
                p2 = p2.next;
            }
        }

        if(p1 == null){
            tail.next = p2;
        }else{
            tail.next = p1;
        }


        return newHead.next;

    }
}

206反转链表

 /**
 * 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 reverseList(ListNode head) {
        if(head == null) return null;
        ListNode newhead = null;// newhead 为指针非节点
        ListNode p = head;
        ListNode cur;
        while(p !=null){
            cur = p.next;// 临时节点存储以防丢
            p.next = newhead;// 头插核心
            newhead = p;// 头插核心
            p = cur;
        }
        return newhead;
    }
}

234回文链表

思路:和反转链表类似,可以现将链表进行反转,然后在遍历;需要注意的是,链表反转过程中需要重新new ListNode

/**
 * 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 boolean isPalindrome(ListNode head) {
        ListNode newHead = null;
        ListNode p = head;
        while (p != null){
            ListNode cur = new ListNode(p.val);    
            cur.next = newHead;
            newHead = cur;
            p = p.next;
        }

        ListNode p1 = newHead,p2 = head;
        while(p1!=null){
            if(p1.val == p2.val){
                p1 = p1.next;
                p2 = p2.next;
            }else{
                return false;
            }
        }
        return true;
    }
}

328奇偶链表

思路:新建两个头结点,分别奇偶链入自己的结点后面,注意:新加结点后的next需要置为null,否则会出现环

/**
 * 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 oddEvenList(ListNode head) {
        if(head == null) return head;
        ListNode newHead1 = new ListNode();
        ListNode newHead2 = new ListNode();

        ListNode p = head,p1 = newHead1,p2 = newHead2;
        int num = 1;
        while(p != null ){
            ListNode tmp = p.next;
            if(num%2==1){
                p1.next = p;
                p1 = p;
                p1.next = null;  // important            
            }else{
                p2.next = p;
                p2 = p;
                p2.next = null; // important  
            }
            p = tmp;
            num++;
        }
        p1.next = newHead2.next;
        return newHead1.next;
    }
}

141环形链表

思路:快慢指针,如果出现环形的化,快慢指针指定相遇,需要注意特殊点: 1.链表是否为空。 2.如果只有一个节点如何操作,可以开头快指针先走一个格,用于最终判断是否相等。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null) return false;
        ListNode fast = head.next,slow = head;
        while(fast != null && fast.next !=null && fast !=slow){
            slow = slow.next;
            fast = fast.next.next;
        }
        if(fast == slow){
            return true;
        }else{
            return false;
        }
    }
}

142环形链表2

思路:快慢指针,两者相遇判断有环,然后一个从头一个原位重新开始,直至相遇即为环的开始节点;

   /**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next ==null) return null;
        ListNode fast = head,slow = head;
        boolean flag = false;
        while(fast !=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                flag = true;
                break;
            }
        }
        if(flag){
            slow = head;
            while(slow != fast){
                slow =slow.next;
                fast = fast.next;
            }
            return slow;
        
        }else{
            return null;
        }
        
    }
}

image.png

image.png