LeetCode 链表专项 (141 / 21 / 203 / 206 / 83 / 25 / 面试题02.05)

记录一些基本操作

单链表反转

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode next = null;
        ListNode pre = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

双向链表反转

public ListNode reverseList(ListNode head) {
        ListNode next = null;
        ListNode pre = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head = next;
        }
        return pre;
    }

用链表设计一个队列和栈

Node

public class Node<T> {
    T val;
    Node next;

    public Node() {
    }

    public Node(T val) {
        this.val = val;
        next = null;
    }

    public Node(T val, Node next) {
        this.val = val;
        this.next = next;
    }
}

Stack

public class LinkedStack<T> {
    Node<T> head = null;
    int size = 0;

    public boolean isEmpty() {
        return size == 0;
    }
    public int size() {
        return size;
    }

    public void push(T val) {
        Node<T> node = new Node<>(val);
        if (head == null) {
            head = node;
        } else {
            node.next = head;
            head = node;
        }
        size++;
    }

    public T pop() {
        T res = null;
        if (head != null) {
            res = head.val;
            head = head.next;
            size--;
        }
        return res;
    }

    public T peek() {
        return head == null ? null : head.val;
    }
}

Queue

public class LinkedQueue<T> {
    Node head = null;
    Node tail = null;
    int size = 0;

    // 队列添加元素
    public void offer(T val) {
        Node<T> node = new Node<>(val);
        if (tail == null) {
            head = node;
            tail = node;
        } else {
            tail.next = node;
            tail = node;
        }
        size++;
    }

    // 队列出队
    public T poll() {
        T res = null;
        if (head != null) {
            res = (T) head.val;
            head = head.next;
            size--;
        }
        if (head == null) {
            tail = null;
        }
        return res;
    }

    // 查看对头元素
    public T peek() {
        if (head != null) {
            return (T) head.val;
        }
        return null;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public int size() {
        return size;
    }
}

用双链表设计一个双端队列

class NodeD<V> {
    public V val;
    public NodeD<V> last;
    public NodeD<V> next;

    public NodeD(V val) {
        this.val = val;
        this.last = null;
        this.next = null;
    }
}

public class LinkedDeque<T> {
    NodeD<T> head;
    NodeD<T> tail;
    int size;

    public LinkedDeque() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public int size() {
        return size;
    }

    public void pushHead(T val) {
        NodeD<T> node = new NodeD<>(val);
        if (head != null) {
            node.next = head;
            head.last = node;
            head = node;
        } else {
            head = node;
            tail = node;
        }
        size++;
    }

    public void pushTail(T val) {
        NodeD<T> node = new NodeD<>(val);
        if (tail != null) {
            tail.next = node;
            node.last = tail;
            tail = node;
        } else {
            head = node;
            tail = node;
        }
        size++;
    }

    public T pollHead() {
        T res = null;
        if (head == null) return res;

        if (head == tail) {
             res = head.val;
             head = null;
             tail = null;
        } else {
            res = head.val;
            head = head.next;
            head.last = null;
        }
        size--;

        return res;
    }

    public T pollTail() {
        T res = null;
        if (tail == null) return res;

        if (head == tail) {
            res = tail.val;
            head = null;
            tail = null;
        } else {
            res = tail.val;
            tail = tail.last;
            tail.next = null;
        }
        size--;
        return res;
    }
}

141. 环形链表

在这里插入图片描述

思路

经典解法就是⽤两个指针,⼀个跑得快,⼀个跑得慢。如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针⼀圈,和慢指针相遇,说明链表含有环。

代码实现(java)

/**
 * 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 || head.next == null) return false;

        ListNode fastIndex = head;
        ListNode slowIndex = head;
        while(fastIndex != null && fastIndex.next != null) {
            slowIndex = slowIndex.next;
            fastIndex = fastIndex.next.next;
            if(slowIndex == fastIndex) return true;
        }
        return false;
    }
}

已知链表有环,返回环的起点位置

思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现(java)

ListNode detectCycle(ListNode head) {
	ListNode fast, slow;
	fast = slow = head;
	while (fast != null && fast.next != null) {
		fast = fast.next.next;
		slow = slow.next;
		if (fast == slow) break;
	}
	
	// 上⾯的代码类似 hasCycle 函数
	slow = head;
	while (slow != fast) {
		fast = fast.next;
		slow = slow.next;
	}
	return slow;
}

找到链表的中点

思路

在这里插入图片描述
在这里插入图片描述

代码实现

while (fast != null && fast.next != null) {
	fast = fast.next.next;
	slow = slow.next;
}
// slow 就在中间位置
return slow;

寻找链表的倒数第 k 个元素

思路

在这里插入图片描述

代码实现

ListNode slow, fast;
slow = fast = head;
while (k-- > 0)
	fast = fast.next;
while (fast != null) {
	slow = slow.next;
	fast = fast.next;
}
return slow;

21. 合并两个有序链表

在这里插入图片描述

思路

  1. 设立一个临时头结点dummy(设置它为不存在的节点),让其next指向给出的链表,方便操作以及最终的返回(dummy.next)
  2. 在设立一个cur节点,作为链表的操作节点,指向dummy
  3. 循环遍历两个链表,循环条件就设为l1 != null && l2 != null
  4. 进入循环,每次比较 l1l2两个节点的值的大小,找到较小的那一个进行指针的后移,与链表的链接操作
	if(l1.val < l2.val) {
	    cur.next = l1;
	    cur = l1;
	    l1 = l1.next;
	} else {
	    cur.next = l2;
	    cur = l2;
	    l2 = l2.next;
	}
  1. 一直遍历知道一个链表为空,最后再判断不为空的链表,直接与结果链表相连
  2. 返回dummy.next就是最终的结果

代码实现(java)

/**
 * 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 l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                cur.next = l1;
                cur = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                cur = l2;
                l2 = l2.next;
            }
        }

        if(l1 == null) {
            cur.next = l2;
        } else {
            cur.next = l1;
        }
        return dummy.next;
    }
}

203. 移除链表元素

在这里插入图片描述

思路

  1. 设立一个临时头结点dummy(设置它为不存在的节点),让其next指向给出的链表,方便操作以及最终的返回(dummy.next)
  2. 在设立一个cur节点,作为链表的操作节点,指向dummy
  3. 循环遍历给出链表,以cur.next作为遍历的链表元素,判断cur.next.val 是否等于 val,如果等于,就将cur.next = cur.next.next;,相当于将cur.next跳过;否则继续向后遍历cur = cur.next
  4. 最终返回dummy.next

代码实现(java)

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

206. 反转链表

在这里插入图片描述

思路

1 -> 2 -> 3 -> 4 -> null
null <- 1 <- 2 <- 3 <- 4

  1. 定义两个节点,prev = null 、cur = head 分别表示前一个节点和当前节点
  2. 遍历链表,每一步将当前节点的 next 指向 prev ,然后将 prev 和 cur 分别后移一位
  3. 以此类推,最后返回 prev 也即反转后链表的头结点

代码实现(java)

/**
 * 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) {
        ListNode prev = null;  // 前节点
        ListNode cur = head;    // 当前节点
        while(cur != null) {
            ListNode next = cur.next;  // 临时节点,指向当前节点的后一个
            cur.next = prev;  // 让当前节点的next指针指向前一个节点
            prev = cur; // 前节点后移到当前节点
            cur = next; // 当前节点移到下一个节点
        }
        return prev;
    }
}

83. 删除排序链表中的重复元素

在这里插入图片描述

思路

  1. 记录两个指针,pre 是指向前一个节点的指针,next是寻找后一个与 pre 不同的指针
  2. 遍历链表并寻找,当next.val == pre.val,说明是重复的节点,就将pre的next指针指向 next的next,然后next后移;当next.val != pre.val,说明不是重复节点,就将pre指向next,然后next后移
  3. 以此类推,最后返回head即可

代码实现(java)

/**
 * 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 deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode pre = head;
        ListNode next = head.next;
        while(next != null) {
            if(next.val == pre.val) {
                pre.next = next.next;
            } else {
                pre = next;
            }
            next = next.next;
        }
        return head;
    }
}

25. K个一组反转链表

K个一组反转链表

面试题 02.05. 链表求和

链表求和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值