LeetCode刷题之链表
链表(Linked List)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
由于不必须按顺序存储,链表在插入的时候可以达到 O(1)的复杂度,比另一种线性表 —— 顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要 O(n)的时间,而顺序表相应的时间复杂度分别是 O(log/ n)和O(1)。
链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
各题通用节点类:
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
237. 删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
示例 1:
输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
题解:
class Solution {
/**
* 思路:
* 杀不掉自己,就杀掉下一个
* 当前节点不删除,将node后一个节点的值保存在该node中,将该node的next指向下下一个,删除下一个节点
* @param node
*/
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
1290. 二进制链表转整数
给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。
示例 1:
输入:head = [1,0,1]
输出:5
解释:二进制数 (101) 转化为十进制数 (5)
题解:
class Solution {
/**
* 思路:
* 遍历链表,将链表的值存入栈中,遍历栈,依次弹出栈,每个数字✖️2的pos次方,达到链表反转的功能
* 然后按照该栈的顺序将二进制转十进制
* @param head
* @return
*/
public int getDecimalValue(ListNode head) {
Stack stack = new Stack<>();
stack.push(head.val);
//链表值存入栈
while (head.next != null) {
stack.push(head.next.val);
head = head.next;
}
int pos = 0;
int total = 0;
while (!stack.isEmpty()) {
//每个数字在对应pos位置乘以2的pos次方
total += (stack.pop() << pos);
pos++;
}
return total;
}
}
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
题解:
class Solution {
/**
* 思路:
* 双指针法:用former指针指向head,用latter指针指向第k个节点,使former和latter间隔k个位置。
* 循环往后走,former指针和latter指针都依次往后移,当latter指针的next为空时,later指向最后一个,
* 那么此时former正好指向的是倒数第k个位置。
* @param head
* @param k
* @return
*/
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head;
ListNode latter = head;
//将latter指向第k个节点
for (int i=0;i
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
题解:
class Solution {
/**
* 思路:
* 遍历链表存到栈中,遍历栈弹出栈中元素,保存到数组中
* @param head
* @return
*/
public int[] reversePrint(ListNode head) {
Stack stack = new Stack<>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] array = new int[size];
for (int i = 0; i < size; i++) {
array[i] = stack.pop().val;
}
return array;
}
}
206. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
题解:
class Solution {
/**
* 思路: 双指针迭代
* 我们可以申请两个指针,第一个指针叫 pre,最初是指向 null 的。
* 第二个指针 cur 指向 head,然后不断遍历 cur。
* 每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。
* 都迭代完了(cur最后一次会被置为null),pre 就是最后一个节点了。
* @param head
* @return
*/
public ListNode reverseList(ListNode head) {
ListNode preNode = null;
ListNode curNode = head;
while (curNode != null) {
//注意:此处的curNode.next只需要暂存起来,因为后面curNode.next会改变
ListNode temp = curNode.next;
curNode.next = preNode;
//preNode/curNode节点往后移动
preNode = curNode;
curNode = temp;
}
return preNode;
}
}
反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
题解:
class Solution {
/**
* 根据上图思路:
* 1.需要快进到反转区间左位置
* 2.使用变量记住区间前一个位置和第一个位置节点
* 3.使用经典四步反转left到right区间的节点
* 4.将反转后的链表与原链表进行拼接
*/
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode pre = null, cur = head;
//cur在pre的前一个位置
for (int i=0;i
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
题解:
class Solution {
/**
* 思路:
* 创建一个新头节点preNode,比较l1和l2,preNode指向小的节点
* 然后将小的节点往后移动一个,将preNode往后移动一个,继续比较l1和l2
* 直到l1或者l2某一个为null,那么一种一个合并完,然后将preNode指向另一个非空节点
* @param l1
* @param l2
* @return
*/
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode preNode = new ListNode(-1);
ListNode head = preNode;
while (l1!=null && l2!=null) {
if (l1.val <= l2.val) {
//取l1的节点作为新节点的首节点
preNode.next = l1;
//往后移动节点
l1 = l1.next;
} else {
//取l2的节点作为新节点的首节点
preNode.next = l2;
//往后移动节点
l2 = l2.next;
}
//preNode往后移以用来保存新的节点
preNode = preNode.next;
}
//当某个链表为空,非空链表剩下节点拼接在head链表的末尾
preNode.next = l1==null?l2:l1;
return head.next;
}
}
相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
题解:
public class Solution {
//创建两个指针 pApA 和 pBpB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
//当 pApA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当 pBpB 到达链表的尾部时,将它重定位到链表 A 的头结点。
//若在某一时刻 pApA 和 pBpB 相遇,则 pApA/pBpB 为相交结点。
//为什么这种方法是可行的,因为当A、B都走到公共节点的时候,正好走了自己的路,又走了对方的路,两个指针走的距离相等
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode n1 = headA;
ListNode n2 = headB;
while(n1 != n2) {
if(n1 != null) {
//有后面节点,继续往后移动
n1 = n1.next;
} else {
n1 = headB;
}
if(n2 != null) {
n2 = n2.next;
} else {
n2 = headA;
}
}
//当n1==n2了,即各自走了一圈半到了第一个相交节点,返回n1或n2都可以
return n1;
}
}
回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
题解:
public boolean isPalindrome(ListNode head) {
//思路:采用双指针法:将链表的各个值存放到集合中,
//启动start、end位置,前后对应对比数值看是否相等,再依次往中间靠,直到start、end相等
ArrayList data = new ArrayList();
while(head != null) {
data.add(head.val);
head = head.next;
}
int start = 0, end = data.size()-1;
while(start < end) {
if(data.get(start) == data.get(end)) {
start++;
end--;
} else {
return false;
}
}
return true;
}
共有 0 条评论