3.链表(三)
题目汇总https://leetcode-cn.com/tag/linked-list/
206. 反转链表简单[✔]
234. 回文链表简单[✔]
237. 删除链表中的节点简单[✔]
328. 奇偶链表中等[✔]
430. 扁平化多级双向链表中等(没做)
445. 两数相加 II中等[✔]
707. 设计链表中等(抄的题解)
206. 反转链表简单[✔]
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
思路一:迭代+双指针
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户//2020/08/04
public ListNode reverseList(ListNode head) {
ListNode pre = null;//预先指针节点
ListNode cur = head;//当前指针节点
while(cur != null){
ListNode nextTmp = cur.next;//临时节点,暂存当前节点的下一节点,用于后移
cur.next = pre;//将当前节点指向它前面的节点
pre = cur;//前指针后移
cur = nextTmp;//当前指针后移
}
return pre;
}
}
思路二:递归
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode cur = reverseList(head.next);
head.next.next = head;
head.next = null;
return cur;
}
}
234. 回文链表简单[✔]
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
思路:双指针+反转链表
使用快慢指针找到链表中点,反转链表后半部分, 比较两个半长链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:1 ms, 在所有 Java 提交中击败了99.70%的用户,2020/08/04
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null)
return true;
ListNode slow = head;
ListNode fast = head;
// 根据快慢指针,找到链表的中点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode newHalf = reverse(slow);
while(newHalf != null){// 两个半长链表的比较 遍历两个半长链表
if(head.val != newHalf.val){
return false;
}
newHalf = newHalf.next;
head = head.next;
}
return true;
}
//206题反转链表
private ListNode reverse(ListNode head) {
// 递归到最后一个节点,返回新的新的头结点
if (head.next == null) {
return head;
}
ListNode newHead = reverse(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
237. 删除链表中的节点简单[✔]
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
示例 1:
输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
提示:
链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。
思路:
把要删除的节点的值替换为它后面节点中的值,然后删除它之后的节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户,2020/08/04
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
328. 奇偶链表中等[✔]
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->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 {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户,2020/08/04
public ListNode oddEvenList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode odd = head;//奇链表尾结点
ListNode even = head.next;//偶链表尾结点
ListNode evenHead = even;//偶链表头结点
while(odd.next != null && even.next != null){
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
}
445. 两数相加 II中等
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7
类似题目 2. 两数相加中等
思路:
逆序处理所有数位,使用栈。把两个链表的元素分别压入两个栈,再依次取出相加,计算过程中需要注意进位的情况。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:5 ms, 在所有 Java 提交中击败了63.13%的用户,2020/08/05
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
while(l1 != null){
stack1.push(l1.val);
l1 = l1.next;
}
while(l2 != null){
stack2.push(l2.val);
l2 = l2.next;
}
int carry = 0;//进位值
ListNode pre = null;//设置预先指针
while(!stack1.isEmpty() || !stack2.isEmpty() || carry > 0){
int sum = carry;
sum += stack1.isEmpty()? 0:stack1.pop();
sum += stack2.isEmpty()? 0:stack2.pop();
ListNode node = new ListNode(sum % 10);//实际存入链表的值
node.next = pre;
pre = node;
carry = sum / 10;
}
return pre;
}
}
707. 设计链表中等
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:
val
和next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性prev
以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
- get(index):获取链表中第
index
个节点的值。如果索引无效,则返回-1
。- addAtHead(val):在链表的第一个元素之前添加一个值为
val
的节点。插入后,新节点将成为链表的第一个节点。- addAtTail(val):将值为
val
的节点追加到链表的最后一个元素。- addAtIndex(index,val):在链表中的第
index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。- deleteAtIndex(index):如果索引
index
有效,则删除链表中的第index
个节点。
示例:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3
提示:
- 所有
val
值都在[1, 1000]
之内。- 操作次数将在
[1, 1000]
之内。- 请不要使用内置的 LinkedList 库。
思路:
https://leetcode-cn.com/problems/design-linked-list/solution/she-ji-lian-biao-by-leetcode/
class MyLinkedList {//执行用时:12 ms, 在所有 Java 提交中击败了72.56%的用户,2020/08/06
int size;
ListNode head;
/** Initialize your data structure here. */
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
public int get(int index) {
if(index < 0 || index >= size) return -1;
ListNode cur = head;
for(int i = 0; i < index + 1; ++i){
cur = cur.next;
}
return cur.val;
}
/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
public void addAtHead(int val) {
addAtIndex(0, val);
}
/** Append a node of value val to the last element of the linked list. */
public void addAtTail(int val) {
addAtIndex(size, val);
}
/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
public void addAtIndex(int index, int val) {
if(index > size) return;
if(index < 0) index = 0;
++size;
ListNode pre = head;
for(int i = 0; i < index; ++i){
pre = pre.next;
}
ListNode addNode = new ListNode(val);
addNode.next = pre.next;
pre.next = addNode;
}
/** Delete the index-th node in the linked list, if the index is valid. */
public void deleteAtIndex(int index) {
if(index >= size || index < 0) return;
size--;
ListNode pre = head;
for(int i = 0; i < index; ++i){
pre = pre.next;
}
pre.next = pre.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);
*/