前言
如果想进大厂工作,除了基础知识,项目经验,还有一些非常重要的能力需要锻炼,其中最重要的就是算法。算法题千千万,但是类型也就几十种,接下来的系列文章,我将面试中常见的算法题类型进行归纳。
链表面试题01. 移除重复节点
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点
示例1:
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]
示例2:
输入:[1, 1, 1, 1, 2]
输出:[1, 2]
提示: 链表长度在[0, 20000]范围内
链表元素在[0, 20000]范围内
进阶:
如果不得使用临时缓冲区,该怎么解决?
代码
我们在给定的链表上使用两重循环,其中第一重循环从链表的头节点开始,枚举一个保留的节点,这是因为我们保留的是「最开始出现的节点」。第二重循环从枚举的保留节点开始,到链表的末尾结束,将所有与保留节点相同的节点全部移除。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
ListNode nodeA = head;
if (nodeA == null) {
return head;
}
while (nodeA != null) {
ListNode nodeB = nodeA;
while (nodeB.next != null) {
if (nodeB.next.val == nodeA.val) {
nodeB.next = nodeB.next.next;
} else {
nodeB = nodeB.next;
}
}
nodeA = nodeA.next;
}
return head;
}
}
链表面试题02. 返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4
说明: 给定的 k 保证是有效的。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int kthToLast(ListNode head, int k) {
// 链表长度
int count = 0;
// 结果
int result = 0;
// 初始化一个链表临时节点
ListNode temp = head;
// 计算出链表长度
while (temp.next != null) {
temp = temp.next;
count++;
}
temp = head;
for (int i = 0; i <= count - k; i++) {
temp = temp.next;
}
return temp.val;
}
}
链表面试题03. 删除中间节点
实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点),假定你只能访问该节点。
示例: 输入:单向链表a->b->c->d->e->f中的节点c
结果:不返回任何数据,但该链表变为a->b->d->e->f
分析:
一道抖机灵的题
脑筋急转弯
将下一个节点值赋值给当前节点
将当前节点的next指针指向下下个节点
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
// 链表4 → 8 → 9 → 2 → 7
// 删除9 得到链表4 → 8 → 2 → 7
// 如果知道该链表的头结点,可以遍历,找到该结点并且删除
// 但是不知道这个结点的地址,并且还不知道该链表的头结点
// 可以更换该这个结点的数据为下一个结点,然后删除下一个结点
// 例如把9替换为2,链表4 → 8 → 2 → 2 → 7
// 然后删除2,得到4 → 8 → 2 → 7
// 将这个结点的数据替换为下一个结点
node.val = node.next.val;
// 删除下一个结点
node.next = node.next.next;
}
}
链表面试题04. 分割链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。
提示:
链表中节点的数目在范围 [0, 200] 内
-100 <= Node.val <= 100
-200 <= x <= 200
第一种解法:
思路:
初始化两个集合
遍历链表,将小于x的存入一个集合,将大于或者等于x的存入另外一个集合
将两个集合的数据存入一个新的链表,返回这个新链表的第一个元素
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode nodeA = head;// 这个head是链表的的第一个元素
ArrayList<Integer> listA = new ArrayList<>();
ArrayList<Integer> listB = new ArrayList<>();
while (nodeA != null) {
if (nodeA.val < x) {
listA.add(nodeA.val);
} else {
listB.add(nodeA.val);
}
nodeA = nodeA.next;
}
ListNode other = new ListNode(0);
ListNode otherHead = other;
for (int i = 0; i < listA.size(); i++) {
ListNode tempA = new ListNode(listA.get(i));
other.next = tempA;
other = other.next;
}
for (int i = 0; i < listB.size(); i++) {
ListNode tempB = new ListNode(listB.get(i));
other.next = tempB;
other = other.next;
}
return otherHead.next;
}
}
第二种解法:
代码:
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode small = new ListNode(0);
ListNode smallHead = small;
ListNode large = new ListNode(0);
ListNode largeHead = large;
while (head != null) {
if (head.val < x) {
small.next = head;
small = small.next;
} else {
large.next = head;
large = large.next;
}
head = head.next;
}
large.next = null;
small.next = largeHead.next;
return smallHead.next;
}
}