程序员面试金典:面试题 02.01~02.06
面试题 02.01. 移除重复节点
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]
思路:
- 若使用额外内存空间,将节点保存在set中,如果当前节点值已经存在于set中,则移除该节点
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
if (head == null) {
return head;
}
Set<Integer> set = new HashSet<>();
ListNode pre = head;
set.add(head.val);//放入头节点的值
while (pre.next != null) {
ListNode cur = pre.next;
if (set.add(cur.val)) {
pre = pre.next;
} else {
pre.next = pre.next.next;
}
}
return head;
}
}
- 不适用额外空间,则需要两层for循环,依次比较当前节点的值与其后面的n-i个值是否重复,若重复,则移除
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
if (head == null) {
return head;
}
ListNode cur = head;
while (cur != null) {
ListNode temp = cur;
while (temp.next != null) {
if (temp.next.val == cur.val) {
temp.next = temp.next.next;
} else {
temp = temp.next;
}
}
cur = cur.next;
}
return head;
}
}
面试题 02.02. 返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4
思路:两个节点,fast
节点 先走到第k个节点,之后slow
节点和fast
节点一起遍历,当fast
节点为null
时,返回slow
节点的值即可。
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode fast = head;
while (k-- > 0) {
fast = fast.next;
}
ListNode slow = head;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow.val;
}
}
面试题 02.04. 分割链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
思路:根据给出的值x,将链表分为两个链表,小于x的为一个链表small,大于等于x的为另一个链表big,之后将big链表连接到small链表后面即可
class Solution {
public ListNode partition(ListNode head, int x) {
if (head == null) {
return head;
}
ListNode dummySmall = new ListNode(0);
ListNode dummyBig = new ListNode(0);
ListNode curSmall = dummySmall, curBig = dummyBig;
ListNode cur = head;
while (cur != null) {
if (cur.val < x) {
curSmall.next = cur;
curSmall = curSmall.next;
} else {
curBig.next = cur;
curBig = curBig.next;
}
cur = cur.next;
}
curSmall.next = dummyBig.next;
curBig.next = null;
return dummySmall.next;
}
}
面试题 02.05. 链表求和
给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
思路:由于是反向存放,所以可以直接对应位相加即可。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode p1 = l1, p2 = l2;
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
int jinwei = 0;
//当p1,p2都为null且进位为0时,才退出for循环
while (p1 != null || p2 != null || jinwei != 0) {
int x = 0;
int y = 0;
if (p1 != null) {
x = p1.val;
p1 = p1.next;
}
if (p2 != null) {
y = p2.val;
p2 = p2.next;
}
int temp = x + y + jinwei;
cur.next = new ListNode(temp % 10);
cur = cur.next;
jinwei = temp / 10;
}
return dummy.next;
}
}
进阶:思考一下,假设这些数位是正向存放的,又该如何解决呢?
思路:可以先将每个链表翻转,之后相加得到一个链表,在对得到的这个链表翻转即可。
面试题 02.06. 回文链表
编写一个函数,检查输入的链表是否是回文的。
思路:将链表根据int mid = (length + 1) / 2;
拆分为两部分,将后半部分组成的新链表翻转,指定两个指针,依次比较两个链表中相同位置的节点的值是否相等。
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
int length = 0;
ListNode cur = head;
while (cur != null) {
length++;
cur = cur.next;
}
int mid = (length + 1) / 2;
cur = head;
while (mid-- > 0) {
cur = cur.next;
}
ListNode p2 = cur;
p2 = reverse(p2);
cur.next = null;
cur = head;
while (p2 != null) {
if (p2.val != cur.val) {
return false;
}
p2 = p2.next;
cur = cur.next;
}
return true;
}
private ListNode reverse(ListNode node) {
ListNode pre = null;
ListNode cur = node;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}