前言
所有题目均来自力扣题库中的hot 100,之所以要记录在这里,只是方便后续复习
2.两数相加
题目:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
解题思路:
【链表遍历】根据题意,我们同时遍历两个链表,将对应位置的值相加即可,可以初始化一个头节点用来结果返回,一个尾节点依次根据遍历的对应位置得到的值追加节点。需要注意几点:遍历结束应该是最长的链表,若对应位置的另一链表为空时可以默认该位置值为0;每个位置的值应该是两个链表对应值以及上一次遍历的进位值的和除以十取余,所以要记录每个位置相加后的进位值,进位值是两个链表对应值以及上一次遍历的进位值的和除以十取整;在遍历结束后要检查进位制是否大于0,若大于0要再追加一个尾节点
代码(python):
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
# 记录生成链表的头位置,用来结果返回
head = None
# 记录生成链表的尾位置,用于追加尾节点
tail = head
# 记录进位值,用来下一位运算
c = 0
# 当俩个链表有一个未遍历完时
while l1 is not None or l2 is not None:
# 如果两个列表遍历位置为none时,默认该值为0
if l1 is None:
v1 = 0
else:
v1 = l1.val
if l2 is None:
v2 = 0
else:
v2 = l2.val
# 计算对应位相加和 注意别忘了上次的进位
sum = v1 + v2 + c
# 判断头节点是否为空,如果为空,为头尾节点赋值,注意头尾节点分别赋值;若不为空,在尾节点后追加新节点且更新尾节点
if head is None:
head = ListNode(sum % 10)
tail = head
else:
tail.next = ListNode(sum % 10)
tail = tail.next
# 跟新进位值,为下一次循环准备
c = sum / 10
# 跟新两个列表节点,达到遍历效果
if l1 is not None:
l1 = l1.next
if l2 is not None:
l2 = l2.next
# 千万别忘了,遍历后检查一下是否有进位值,若有则需在尾节点后再追加一个节点
if c > 0:
tail.next = ListNode(c)
return 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 addTwoNumbers(ListNode l1, ListNode l2) {
int carry = 0;
ListNode head = null;
ListNode tail = null;
while (l1 != null || l2 != null){
int v1 = l1 != null ? l1.val : 0;
int v2 = l2 != null ? l2.val : 0;
if (head == null) {
head = new ListNode((v1 + v2 + carry) % 10);
tail = head;
}else{
tail.next = new ListNode((v1 + v2 + carry) % 10);
tail = tail.next;
}
carry = (v1 + v2 + carry) / 10;
if(l1 != null){
l1 = l1.next;
}
if(l2 != null){
l2 = l2.next;
}
}
if(carry != 0){
tail.next = new ListNode(carry);
}
return head;
}
}
知识点:
- 在该题中,java和python中除法取余均是%,向下取整均是/。
原题链接:两数相加
21.合并两个有序链表
题目:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
解题思路:
【链表遍历】根据题意,我们分别在两个链表头处定义一个指针,对比两个指针的值,选出较小值,并定义新节点追加到结果链表尾部,之后再滑动对应链表的指针即可。注意:当其中一个链表指针为空时,只能选取另外一个链表的指针值;当两个指针都为空时,说明两个链表遍历完成,返回结果链表即可
代码(python):
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if l1 is None:
return l2
if l2 is None:
return l1
head = None
tail = head
while l1 is not None and l2 is not None:
if l1.val <= l2.val:
value = l1.val
l1 = l1.next
else:
value = l2.val
l2 = l2.next
if head is None:
head = ListNode(val=value)
tail = head
else:
tail.next = ListNode(val=value)
tail = tail.next
if l1 is not None:
tail.next = l1
if l2 is not None:
tail.next = l2
return 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 mergeTwoLists(ListNode list1, ListNode list2) {
// 初始化头节点,用来结果返回
ListNode head = null;
// 初始化尾节点,用来每次在尾部追加新节点
ListNode tail = null;
//当两个节点 有一个节点不是空时
while(list1 != null || list2 != null){
// 如果节点1是空节点 或 节点1和节点2都不是空且节点1的值大于节点2的值,选择节点2进行追加
if((list1 == null) || (list1 != null && list2 != null && list1.val > list2.val)){
// 如果头节点是空的,初始化头节点并且将尾节点也初始化成头节点
if(head == null){
head = new ListNode(list2.val);
tail = head;
// 如果头节点不为空,在尾节点后面追加一个新节点并且更新尾节点为新追加的节点
}else{
tail.next = new ListNode(list2.val);
tail = tail.next;
}
list2 = list2.next;
// 其他情况选择节点1追加即可
}else{
if(head == null){
head = new ListNode(list1.val);
tail = head;
}else{
tail.next = new ListNode(list1.val);
tail = tail.next;
}
list1 = list1.next;
}
}
// 返回头节点即可
return head;
}
}
知识点:
- 无
原题链接:合并两个有序链表
141.环形链表
题目:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
解题思路:
【快慢指针】定义两个指针:快指针和满指针,快指针一次走两个节点,慢指针一次走一个节点,如果两个指针相遇则代表有环,如果快指针走完整个链表即快指针为空或者快指针下个节点为空时,则代表没环
代码(python):
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
# 定义快慢指针
fast = head
slow = head
# 只要 快指针不为空时
while fast:
# 如果 快指针下个节点为空,说明有尽头,没有环;如果不为空,直接让快指针走两次
if fast.next:
fast = fast.next.next
else:
return False
# 满指针走一次
slow = slow.next
# 如果快慢指针相遇了,代表有环
if fast == slow:
return True
# 如果由于fast为空跳出来循环,说明有尽头,没环
return False
代码(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) {
Set<ListNode> hashSet = new HashSet<>();
while (head != null){
if (hashSet.contains(head)){
return true;
}
hashSet.add(head);
head = head.next;
}
return false;
}
}
知识点:
- 无
原题链接:环形链表
142.环形链表Ⅱ
题目:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
解题思路:
【快慢指针】定义两个指针:快指针和满指针,快指针一次走两个节点,慢指针一次走一个节点,当快指针走到尽头时,则代表没环返回null;当两个节点相遇时,代表有环,此时让快指针从头开始,变成两个指针每次都走一个节点,再次相遇即为第一个成环的节点
代码(python):
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head is None:
return
fast = head
slow = head
slow = slow.next
if fast.next is None:
return
else:
fast = fast.next.next
while fast and fast.next and fast != slow:
fast = fast.next.next
slow = slow.next
if fast == slow:
new_slow = head
while new_slow != slow:
slow = slow.next
new_slow = new_slow.next
return slow
else:
return
代码(java):
/**
* 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;
ListNode slow = head;
// 只要fast 不为空就一直循环
while(fast != null){
// 如果头的下一个节点为空,代表有尽头,没环
if (fast.next != null){
// 快指针一次移动一个节点,满指针一次移动两个节点
fast = fast.next.next;
slow = slow.next;
// 当两个指针相遇时,跳出训话
if(fast == slow){
break;
}
}else{
return null;
}
}
// 判断如果因为快指针为空跳出的循环则没有环
if(fast == null){
return null;
}
// 如果不是因为快指针为空,就是因为两个节点相遇了,此时让快指针从头开始走,两个指针每次都走一个节点,再次相遇即为第一个入环的节点
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
知识点:
- 无
原题链接:环形链表Ⅱ
160.相交链表
题目:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
解题思路:
【双指针】分别定义两个指针指向两个头位置,指针A和指针B如果不为空则让它们向后移动一个节点,如果A指针为空,让它指向B链表的头位置;如果B指针为空,让它指向A链表的头位置;直到两个指针相遇位置,返回任意指针即可
代码(python):
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
# 如果两个节点有一个节点为空,则没有相交
if not headA or not headB:
return
# 定义两个指针 指向两个头
node_a = headA
node_b = headB
# 只要两个指针不相遇
while node_a != node_b:
# 如果指针A为空就让它从B头开始;不为空让它移动到下一个
if not node_a:
node_a = headB
else:
node_a = node_a.next
# 如果指针B为空就让它从A头开始;不为空让它移动到下一个
if not node_b:
node_b = headA
else:
node_b = node_b.next
# 直到两个指针相遇,返回任意指针即可,结果是相交位置或者空
return node_a
代码(java):
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
int ALength = getLength(headA);
int BLength = getLength(headB);
if (ALength < BLength){
for(int i = 0; i < (BLength - ALength); i++){
headB = headB.next;
}
}else{
for(int i = 0; i < (ALength - BLength); i++){
headA = headA.next;
}
}
while (headA != null && headB != null){
if (headA == headB){
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
public int getLength(ListNode head){
int result = 0;
ListNode newHead = head;
while (newHead != null){
result ++ ;
newHead = newHead.next;
}
return result;
}
}
知识点:
- 无
原题链接:相交链表