提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:
本章节全部基于牛客网的题库中的在线编程,面试必刷TOP101:01-链表,总共十六道题目。
提示:以下是本篇文章正文内容,下面案例可供参考
题目描述:
给定一个单链表的头结点,长度为n,反转该链表后,返回新链表的表头。
- 0≤n≤1000
- 要求空间复杂度O(1)、时间复杂度O(1)
代码如下:
public class BM1 {
/**
* @param head : 给定一个链表的头节点
* @return 返回反转链表后的新的头节点
*/
public ListNode ReverseList(ListNode head) {
// base case
if (head == null || head.next == null) {
return head;
}
// 利用指针(pre,记录之前遍历过的节点,cur为当前正在操作的节点,next保存原始链表顺序
ListNode pre = null, cur = head, next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
总结:
其实,这道题很经典但是在设置指针的时候很容易出错,需要熟练使用指针。确保将一个指针记录之前操作过的链表节点,一个指针为正在操作的节点,一个指针保存原始链表顺序。
题目描述:
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(N),空间复杂度O(1)。
给出的链表为 :1→2→3→4→5→NULL, m=2,n=4,
返回 :1→4→3→2→5→NULL.
代码实现:
public class BM2 {
/**
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
public ListNode reverseBetween(ListNode head, int m, int n) {
// write code here
// 首先找到要开始翻转的节点 定义一个虚拟头节点
ListNode fakeHead = new ListNode(0);
fakeHead.next = head;
int length = n - m + 1;// 记录需要反转的节点个数
// 记录原始链表中,不需要被反转的的最后一个节点
ListNode last = fakeHead, cur = head;
while (m > 1) {
cur = cur.next;
last = last.next;
m--;
}
// 此时的 cur 节点为需要反转的头节点 pre 为原始链表中不需要被翻转的最后一个节点
// lastOne 记录被反转链表的头节点,也就是之后会成为尾节点
ListNode pre = null, next = null, lastOne = cur;
while (length > 0) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
length--;
}
last.next = pre;
lastOne.next = cur;
return fakeHead.next;
}
}
思路:
这道题开始有点晃到我了,但是仔细分析指针移动的原则,该记录就需要记录,细心一点。
题目描述:
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表。如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样。你不能更改节点中的值,只能更改节点本身。
给定的链表是 1→2→3→4→5:
- 对于 k=2 , 你应该返回 :2→1→4→3→5;
- 对于 k=3 , 你应该返回 :3→2→1→4→5;
代码实现:
public class BM3 {
/**
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode reverseKGroup(ListNode head, int k) {
// write code here
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode last = dummy;// 已经翻转的链表的最后一个节点
ListNode pre = null, cur = head, next = null;
// 每次找到下次要翻转的节点(如果为空就不翻转)
while (cur != null) {
// 判断是否需要翻转本轮次
ListNode hasNext = cur;
for (int i = 1; i < k; i++) {
hasNext = hasNext.next;
if (hasNext == null) {
return dummy.next;
}
}
ListNode lastOne = cur;
for (int i = 0; i < k; i++) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
last.next = pre;
last = lastOne;
lastOne.next = next;
}
return dummy.next;
}
}
题目描述:
代码实现:
public class BM4 {
/**
* @param list1:链表头1
* @param list2:链表头2
* @return 返回新的头节点
*/
public ListNode Merge(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(0);// 采用虚拟头节点
// 采用双指针遍历
ListNode p1 = list1, p2 = list2, cur = dummy;
while (p1 != null && p2 != null) {
if (p1.val <= p2.val) {
cur.next = p1;
p1 = p1.next;
} else {
cur.next = p2;
p2 = p2.next;
}
cur = cur.next;
}
if (p1 != null) {
cur.next = p1;
}
if (p2 != null) {
cur.next = p2;
}
return dummy.next;
}
}
题目描述:
代码实现:
public class BM5 {
/**
* 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
*
* @param lists :一个包含k个升序链表头节点的arrayList
* @return
*/
public ListNode mergeKLists(ArrayList<ListNode> lists) {
ListNode dummy = new ListNode(0);// 虚拟头节点
// 利用一个优先级队列
PriorityQueue<ListNode> queue = new PriorityQueue<>((ListNode a, ListNode b) -> {
return a.val - b.val;
});
// 利用指针
ListNode cur = dummy;
// 将arrayList中的头节点全部加入到优先级队列中
for (ListNode listNode : lists) {
if (listNode != null) {
queue.add(listNode);
}
}
// 遍历
while (!queue.isEmpty()) {
ListNode poll = queue.poll();
cur.next = poll;
if ((poll = poll.next) != null) {
queue.add(poll);
}
cur = cur.next;
}
return dummy.next;
}
}
题目描述:
代码实现:
public class BM6 {
/**
* 判断给定的链表中是否有环。如果有环则返回true,否则返回false。
*
* @param head : 链表的头节点
* @return 返回真就是有环,false无环
*/
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
// 利用快慢指针法则
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
}
题目描述:
代码实现:
public class BM7 {
public ListNode EntryNodeOfLoop(ListNode pHead) {
// base case
if (pHead == null || pHead.next == null) {
return null;
}
// 还是利用快慢指针
ListNode fast = pHead, slow = pHead;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
fast = pHead;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
题目描述:
代码实现:
public class BM8 {
/**
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail(ListNode pHead, int k) {
if (pHead == null) {
return null;
}
// write code here
ListNode dummy = new ListNode(0);
dummy.next = pHead;
ListNode slow = dummy, fast = dummy;
while (k > 0) {
fast = fast.next;
k--;
if (fast == null) {
return null;
}
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
题目描述:
代码实现:
public class BM9 {
/**
* @param head ListNode类
* @param n int整型
* @return ListNode类
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
// write code here
ListNode dummy = new ListNode(0);
dummy.next = head;// 虚拟头节点
ListNode pre = dummy, cur = head, fast = head;
while (n > 0) {
fast = fast.next;
n--;
}
while (fast != null) {
fast = fast.next;
cur = cur.next;
pre = pre.next;
}
pre.next = cur.next;
return dummy.next;
}
}
描述:
代码实现:
public class BM10 {
/**
* @param pHead1
* @param pHead2
* @return 返回链表1和链表2的第一个公共节点
*/
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
int length1 = getLength(pHead1);
int length2 = getLength(pHead2);
// 获取两个链表的长度差值
int n = length1 >= length2 ? length1 - length2 : length2 - length1;
// 长短链表
ListNode longHead = length1 >= length2 ? pHead1 : pHead2;
ListNode shortHead = longHead == pHead1 ? pHead2 : pHead1;
// 让长的链表先走
while (n > 0) {
longHead = longHead.next;
n--;
}
while (longHead != null && shortHead != null) {
if (longHead == shortHead) {
return longHead;
}
longHead = longHead.next;
shortHead = shortHead.next;
}
return null;
}
// 得到链表的长度
int getLength(ListNode head) {
int size = 0;
while (head != null) {
size++;
head = head.next;
}
return size;
}
}
题目描述:
看到的第一眼没思路:
代码实现:
public class BM11 {
/**
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
public ListNode addInList(ListNode head1, ListNode head2) {
// write code here
// 翻转两个链表得到新的头节点
ListNode reverseHead01 = reverse(head1);
ListNode reverseHead02 = reverse(head2);
// 新建链表
int carry = 0, sum = 0;
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
while (reverseHead01 != null || reverseHead02 != null) {
sum = 0;
if (reverseHead01 != null) {
sum += reverseHead01.val;
reverseHead01 = reverseHead01.next;
}
if (reverseHead02 != null) {
sum += reverseHead02.val;
reverseHead01 = reverseHead02.next;
}
sum += carry;
cur.next = new ListNode(sum % 10);
cur = cur.next;
carry = sum / 10;
}
if (carry == 1) {
cur.next = new ListNode(1);
}
return reverse(dummy.next);
}
ListNode reverse(ListNode head) {
ListNode pre = null, cur = head, next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
题目描述:
思路:
代码实现:
public class BM12 {
/**
* @param head ListNode类 the head node
* @return ListNode类
*/
public ListNode sortInList(ListNode head) {
// write code here
// 获取链表的长度,申请一个大小为链表长度的数组空间,存放链表中的所有元素
int size = getSize(head);
ListNode[] nodes = new ListNode[size];
ListNode cur = head;
for (int i = 0; i < size; i++) {
nodes[i] = cur;
cur = cur.next;
}
mergeSort(nodes, 0, size - 1);
for (int i = 0; i < size - 1; i++) {
nodes[i].next = nodes[i + 1];
}
nodes[size - 1].next = null;
return nodes[0];
}
void mergeSort(ListNode[] arr, int start, int end) {
if (start < end) {
int mid = start + (end - start) / 2;
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
merge(arr, start, mid, end);
}
}
void merge(ListNode[] arr, int left, int mid, int right) {
ListNode[] help = new ListNode[right - left + 1];
int index = 0;
int i = left, j = mid + 1;
while (i <= mid && j <= right) {
if (arr[i].val <= arr[j].val) {
help[index++] = arr[i++];
} else {
help[index++] = arr[j++];
}
}
while (i <= mid) {
help[index++] = arr[i++];
}
while (j <= right) {
help[index++] = arr[j++];
}
for (index = 0; index <= right - left; index++) {
arr[left + index] = help[index];
}
}
// 获取链表的长度
int getSize(ListNode head) {
int size = 0;
while (head != null) {
size++;
head = head.next;
}
return size;
}
}
但是其实因为是链表,所以可以使用快慢指针。
代码实现:
// 优美代码(拒绝暴力求解)
// 返回以head为头节点的原始链表,排好序后的链表新的头节点
public ListNode sortInListNice(ListNode head) {
// write code here
// base case
if (head == null || head.next == null) {
return head;
}
// 利用快慢指针进行找到中间位置,将链表一分为二
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 断掉(分成两段链表)
ListNode rightHead = slow.next;
slow.next = null;
// 分别将两段链表进行排序
ListNode left = sortInListNice(head);
ListNode right = sortInListNice(rightHead);
// 合并两个分别有序的链表
return merge(left, right);
}
ListNode merge(ListNode left, ListNode right) {
// 指针
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
while (left != null && right != null) {
if (left.val <= right.val) {
cur.next = left;
left = left.next;
} else {
cur.next = right;
right = right.next;
}
cur = cur.next;
}
if (left != null) {
cur.next = left;
}
if (right != null) {
cur.next = right;
}
return dummy.next;
}
题目:
思路:
代码实现:
public class BM13 {
/**
* @param head ListNode类 the head
* @return bool布尔型
*/
public boolean isPail(ListNode head) {
// write code here
// 利用快慢指针,将链表一分为二
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 斩断链表的联系
if (fast != null) {
slow = slow.next;
}
fast = reverse(slow);
slow = head;
while (slow != null && fast != null) {
if (slow.val != fast.val) {
return false;
}
fast = fast.next;
slow = slow.next;
}
return true;
}
// 翻转链表,返回头节点
public ListNode reverse(ListNode head) {
ListNode pre = null, cur = head, next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
题目:
代码实现:
public class BM14 {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode oddEvenList(ListNode head) {
// write code here
if (head == null || head.next == null) {
return head;
}
//
ListNode evenHead = head.next;
ListNode odd = head, even = head.next;
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
}
题目:
代码实现:
public class BM15 {
/**
* @param head ListNode类
* @return ListNode类
*/
public ListNode deleteDuplicates(ListNode head) {
// write code here
if (head == null || head.next == null) {
return head;
}
ListNode pre = head, cur = head.next;
while (cur != null) {
while (cur != null && cur.val == pre.val) {
cur = cur.next;
}
pre.next = cur;
pre = cur;
}
return head;
}
}
题目:
思路:
代码实现:
public class BM16 {
/**
* @param head ListNode类
* @return 返回原始链表,删除重复出现过的元素后的新的链表的头节点
*/
public ListNode deleteDuplicates(ListNode head) {
// write code here
if (head == null) {
return null;
}
// 如果当前链表头节点和后面一个节点元素相等
if (head.next != null && head.val == head.next.val) {
while (head.next != null && head.val == head.next.val) {
head = head.next;
}
return deleteDuplicates(head.next);// 需要删除当前节点
}
head.next = deleteDuplicates(head.next);
return head;
}
}
写了一下链表章节的题目,对出现的十六道题目进行总结如下: