目录
2. 两数相加 中等
19. 删除链表的倒数第N个节点 中等
24. ;两两交换链表中的节点 中等
61. 旋转链表 中等
82. 删除排序链表中的重复元素 II 中等
86. 分隔链表 中等
92. 反转链表 II 中等
109. 有序链表转换二叉搜索树 中等
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
public ListNode addTwoNumbers (ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode p = dummy;
int pre = 0;
while (l1 != null || l2 != null || pre != 0) {
int num1 = l1 == null ? 0 : l1.val;
int num2 = l2 == null ? 0 : l2.val;
int sum = num1 + num2 + pre;
pre = sum / 10;
ListNode node = new ListNode(sum % 10);
node.next = p.next;
p.next = node;
p = p.next;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
return dummy.next;
}
解析:创建一个新的链表,先创建一个哨兵节点dummy,临时指针p指向dummy,设置进位值pre
当l1不为空或者l2不为空,或进位pre不为0的时候进入while循环,
计算l1与l2的和加上进位得到总数sum,计算当下的进位pre为sum/10,
创建当前节点node,node的当前节点的值为sum%10,
将当前node节点的next指针指向p的next节点,将p的next指针指向当前node节点
p移动到当前节点位置
如果l1不为空,l1移动到l1的下一个节点
如果l2不为空,l2移动到l2的下一个节点
条件满足,继续循环
循环结束后,返回的哨兵节点dummy的下一个节点即为新链表的头结点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
解析:创建一个哨兵节点dummy,将哨兵dummy的next指针指向链表头结点head
创建快慢指针fast和slow,指向dummy哨兵节点
让快指针fast向后走n+1步
如果快指针指向的节点不为空,则快慢指针同时向后移动,直到快指针fast为空
当快指针fast指向为空的时候,此时慢指针slow的next指针所指向的节点即为需要删除的节点
删除目标节点
返回哨兵节点dummy的下一个节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定1->2->3->4
, 你应该返回2->1->4->3
//递归
public ListNode swapPairs(ListNode head) {
//递归终止条件,如果当前这一对节点为空或只有一个节点,则不需要交换
if (head == null || head.next == null) {
return head;
}
//当前这一对需要交换的两个节点
ListNode first = head;
ListNode second = head.next;
//第一个节点的next指针指向下一对需要交换节点的头
first.next = swapPairs(second.next);
//第二个节点的next指针指向第一个节点
second.next = first;
//返回当前这一对节点的新的头
return second;
}
解析:每次传入一对节点
确定递归终止条件,如果传入的当前对为空或者只有一个节点,则不需要交换,终止递归
找出当前对中需要进行交换的两个节点
当前第一个节点的next指针指向下一对节点的新头
当前第二个节点的next指针指向当前第一个节点
返回当前节点的新头,即当前对的第二个节点
//迭代
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode preNode = dummy;
while (head != null && head.next != null) {
//找出当前需要交换的两个节点
ListNode first = head;
ListNode second = head.next;
//交换两个节点
preNode.next = second;
first.next = second.next;
second.next = first;
//将前驱节点preNode的next指针指向更新后当前对节点的最后一个节点
preNode.next = first;
//将head节点指向下一对节点的头节点
head = first.next;
}
//返回当前链表的头结点,即哨兵节点dummy的下一个节点
return dummy.next;
}
解析:创建一个哨兵节点dummy,哨兵节点dummy的next指针指向链表的头结点head
创建一个preNode指针指向哨兵节点dummy
当当前对有两个节点时,进入循环
找出当前需要交换的两个节点first和second
交换两个节点
更新preNode指针和head指针
当当前对节点为空或者只有一个节点时,退出循环
返回当前链表的头结点,即哨兵节点dummy的下一个节点
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4 输出:2->0->1->NULL
解释: 向右旋转 1 步: 2->0->1->NULL 向右旋转 2 步: 1->2->0->NULL 向右旋转 3 步:0->1->2->NULL
向右旋转 4 步:2->0->1->NULL
public ListNode rotateRight(ListNode head, int k) {
//如果没有节点或者只有一个节点,则直接返回
if (head == null || head.next == null) {
return head;
}
//将原链表收尾相连,形成一个环
ListNode old_tail = head;
int n = 1;//n用来确定链表的总长度
while (old_tail.next != null) {
old_tail = old_tail.next;
n++;
}
old_tail.next = head;
//断开链表
ListNode new_tail = head;
//将倒数第k个节点之前的链表断开
for (int i = 0; i < n - k % n - 1; i++) {
new_tail = new_tail.next;
}
ListNode new_head = new_tail.next;
new_tail.next = null;
return new_head;
}
解析:先判断链表节点是否少于两个,如果是,则直接返回
将链表首尾相连,形成一个环形链表,并计算出链表的总长度n
在链表的倒数第k+1个位置和倒数第k个位置断开链表
倒数第k个位置为新链表的头结点
倒数第k+1个位置为新链表的尾节点
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3
public ListNode deleteDuplicates(ListNode head) {
//如果当前链表的节点数小于两个,则直接返回
if (head == null || head.next == null) {
return head;
}
//生成一个新的链表,节点dummy作为新链表的哨兵节点
ListNode dummy = new ListNode(0);
ListNode tail = dummy;//新链表的尾节点
for (ListNode l = head, r = head; l != null; l = r) {
//如果为重复节点,则指针r后移
while (r != null && r.val == l.val) {
r = r.next;
}
//如果l的下一个节点就是r,则说明当前节点和下一个节点不是重复节点
if (l.next == r) {
//尾节点指向l
tail.next = l;
//尾节点后移一位,移动到当前链表的尾节点
tail = l;
//当前链表的尾节点置空
tail.next = null;
}
}
//返回新链表的头结点
return dummy.next;
}
解析:如果当前链表的节点数目小于两个,则直接返回 (因为此时不可能有重复节点)
生成一个新的链表,节点dummy作为新链表的哨兵节点
生成新链表的尾节点tail,指向哨兵节点
生成左右指针指向head,当左指针不为空的时候,进入循环
当右指针r不为空且与左指针l的值相同时,右指针r不断后移,直到左右指针的值不同为止
如果左指针l的下一个节点不指向右指针r时,则说明他们是相邻的,即当前节点与下一个节点的值不同
此时,尾节点的next指针指向当前左指针l指向的节点
当前尾节点后移一位,也就是当前l的位置
当前尾节点的下一个节点置空,否则可能会出现环
重复上述过程,直到左指针指向为空,退出循环
返回新链表的头结点
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
public ListNode partition(ListNode head, int x) {
ListNode beforeDummy = new ListNode(0);
ListNode beforeNode = beforeDummy;
ListNode afterDummy = new ListNode(0);
ListNode afterNode = afterDummy;
while (head != null) {
if (head.val < x) {
beforeNode.next = head;
beforeNode = beforeNode.next;
} else {
afterNode.next = head;
afterNode = afterNode.next;
}
head = head.next;
}
afterNode.next = null;
beforeNode.next = afterDummy.next;
return beforeDummy.next;
}
解析:生成一个前驱哨兵节点链表beforeDummy和一个后继哨兵节点链表afterDummy
如果当前节点小于x,则将当前节点连接到前驱链表后
如果当前节点大于x,则将当前节点连接到后继链表后
当前节点后移
后继链表的next指针置空
前驱链表的next指针指向后继链表头节点
返回前驱链表头结点
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
public ListNode reverseBetween(ListNode head, int m, int n) {
//如果链表中少于两个节点,则直接返回
if (head == null || head.next == null) {
return head;
}
//前驱节点
ListNode preNode = null;
//后继节点
ListNode curNode = head;
//让preNode走到需要反转的开始节点位置之前一个位置
//让curNode走到需要反转的开始节点位置
while (m > 1) {
preNode = curNode;
curNode = curNode.next;
m--;
n--;
}
//con节点为需要开始反转节点的前一个位置
ListNode con = preNode;
//tail节点为需要开始反转节点的位置
ListNode tail = curNode;
//开始反转链表
while (n > 0) {
ListNode next = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = next;
n--;
}
if (con != null) {//不是从第一个位置开始反转
con.next = preNode;
} else {//从第一个位置开始反转
head = preNode;
}
tail.next = curNode;
return head;
}
解析:如果链表中少于两个节点,则直接返回,无需反转
让前驱节点走到需要开始反转的节点之前的一个节点
让当前节点走到需要开始反转的节点
用con指针记录当前前驱节点指针所指向的节点,也即需要反转的链表段的前驱节点
用tail指针记录当前当前节点指针所指向的节点,也即当前需要反转的链表段的第一个节点
然后开始反转
反转结束后
前驱节点preNode指向的节点为需要反转的链表中的最后一个节点
当前节点curNode指向的节点为需要反转的链表中的最后一个节点的后继节点
如果不是从第一个节点开始反转,则con的next指针指向需要反转链表中的最后一个节点preNode
如果是从第一个节点开始反转,则head指向需要反转链表中的最后一个节点preNode
将需要反转链表段的第一个节点的next指针指向当前节点,也即需要反转链表段的之后的节点
返回链表的头结点
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
public TreeNode sortedListToBST(ListNode head) {
//如果链表为空,则直接返回
if (head == null) {
return null;
}
//找到链表的中间元素
ListNode mid = findMidElement(head);
//生成当前树的根节点
TreeNode node = new TreeNode(mid.val);
//如果链表中只有一个节点,则直接返回当前树的根节点
if (head == mid) {
return node;
}
//生成树的左右子树
node.left = sortedListToBST(head);
node.right = sortedListToBST(mid.next);
//返回当前树的根节点
return node;
}
//寻找链表的中间节点
private ListNode findMidElement(ListNode head) {
//用于断开链表
ListNode preNode = null;
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
preNode = slow;
slow = slow.next;
fast = fast.next.next;
}
if (preNode != null) {
preNode.next = null;
}
return slow;
}
解析:如果链表为空,则直接返回
寻找链表的中间节点
创建当前这课树的根节点,中间节点的值作为当前这课树的根节点的值
如果链表只有一个节点,则直接返回这棵树的根节点(如果不这样,就会出现无限循环)
递归生成当前这棵树的左右子树
返回当前这棵树的根节点