简单题
21.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
eg:输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路:每次都取两个中较小的一个,直到其中一个遍历到链表结尾结束遍历。如果这个时候还是剩下的元素,肯定比之前的元素都大,直接添加到链表结尾。
1 | /** |
83. 删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
解题思路:如果是重复的,我们更改当前结点的 next 指针,以便它跳过下一个结点并直接指向下一个结点之后的结点。
1 | /** |
141. 环形链表
给定一个链表,判断链表中是否有环。
hash法
我们遍历所有结点并在哈希表中存储每个结点的引用(或内存地址)。如果当前结点为空结点 null(即已检测到链表尾部的下一个结点),那么我们已经遍历完整个链表,并且该链表不是环形链表。如果当前结点的引用已经存在于哈希表中,那么返回 true(即该链表为环形链表)。
1 | public boolean hasCycle(ListNode head) { |
双向链表
通过使用具有不同速度的快、慢两个指针遍历链表,空间复杂度可以被降低至O(1)O(1)。慢指针每次移动一步,而快指针每次移动两步。
如果列表中不存在环,最终快指针将会最先到达尾部,此时我们可以返回 false。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//查询是否有环(双向链表)
public boolean hasCycle1(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
160. 相交链表
编写一个程序,找到两个单链表相交的起始节点。
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
解题思路:如果A和B是两个长度不同的链表,他们相交前的长度一定是不同的,计算链表长度差额n,让长的链表指针先开始移动n,两个指针再一起移动,指针相遇时就是链表结点的相交处
1 | public class Solution { |
206. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
迭代法
1 | /** |
递归法
解题思路:
假设1->2->3->4->5->null
已经变成1->2<-3<-4<-5<-null
2是head.next
2的后继是head.next.next
我们希望2的后继指向head所以是head=head.next.next
再去除1->2的环head.next=null
1 | /** |
中等题
2. 两数相加
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
非递归解法
1 | public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { |
递归写法
1 | class Solution { |
基础回顾
ListNode的类结构1
2
3
4
5
6
7
8public class ListNode
{
int val;//定义val变量值,存储节点值
ListNode next;//定义next指针,指向下一个节点,维持节点连接
public ListNode(int x){
val=x;
}
- 注意注意val只代表当前指针的值,比如p->val表示p指针的指向的值;而p->next表示链表下一个节点,也是一个指针。
- 构造函数包含两个参数 _value 和 _next ,分别用来给节点赋值和指定下一节点