BM1 反转链表
题目
思路
- 借助栈
- 头插法
- 递归法
代码实现
public ListNode ReverseList1 (ListNode head) {
// write code here
Stack<ListNode> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.next;
}
ListNode newHead = new ListNode(-1);
ListNode cur = newHead;
while (!stack.isEmpty()) {
cur.next = stack.pop();
cur = cur.next;
}
cur.next = null;
return newHead;
}
public ListNode ReverseList2 (ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode cur = head;
ListNode pre = null;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = pre;
pre = cur;
cur = curNext;
}
return pre;
}
public ListNode ReverseList (ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode newHead = ReverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
BM2 链表内指定区间反转
题目
思路
基于上题的基础之上
需要虚拟头结点,来确定返回的新的头结点【因为可能从头开始逆序,头结点就会发生改变】,
具体思路:记录反转开始的前一个节点,然后反转指定区间的节点,最后改变节点的指向【记录节点的next,逆序链表的头结点的next】
代码实现
public ListNode reverseBetween (ListNode head, int m, int n) {
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode front = dummyNode;
int count = n - m + 1;
// 找到要反转节点的前一个节点
while (m - 1 > 0) {
front = front.next;
m--;
}
ListNode cur = front.next;
ListNode pre = null;
while (count > 0) {
ListNode curNext = cur.next;
cur.next = pre;
pre = cur;
cur = curNext;
count--;
}
front.next.next = cur;
front.next = pre;
return dummyNode.next;
}
BM3 链表中的节点每k个一组翻转
题目
思路
递归的方式来进行反转,每趟递归反转一个k长度的链表,每趟递归返回该趟逆序链表的头结点,然后 每趟递归中逆序后,head 就表示该组链表中的尾巴节点,这样就可以通过 head.next = 下一趟递归返回的头结点来进行链表的连接了
代码实现
public ListNode reverseKGroup (ListNode head, int k) {
// write code here
ListNode tail = head;
for (int i = 0; i < k; i++) {
if(tail == null) {
return head;
}
tail = tail.next;
}
// 反转 [head,tail)
ListNode cur = head;
ListNode pre = null;
while (cur != tail) {
ListNode curNext = cur.next;
cur.next = pre;
pre = cur;
cur = curNext;
}
head.next = reverseKGroup(tail,k);
return pre;
}
BM4 合并两个排序的链表
题目
思路
- 遍历合并
- 递归【每一趟递归合并一个数】
代码实现
public ListNode Merge (ListNode pHead1, ListNode pHead2) {
if (pHead1 == null) {
return pHead2;
}
if (pHead2 == null) {
return pHead1;
}
ListNode head = null;
if (pHead1.val > pHead2.val) {
head = pHead2;
pHead2 = pHead2.next;
} else {
head = pHead1;
pHead1 = pHead1.next;
}
head.next = Merge(pHead1,pHead2);
return head;
}
public ListNode Merge1 (ListNode pHead1, ListNode pHead2) {
// write code here
if (pHead1 == null) {
return pHead2;
}
if (pHead2 == null) {
return pHead1;
}
ListNode head = null;
ListNode tail = null;
if (pHead1.val > pHead2.val) {
head = tail = pHead2;
pHead2 = pHead2.next;
} else {
head = tail = pHead1;
pHead1 = pHead1.next;
}
while (pHead1 != null && pHead2 != null) {
if (pHead1.val > pHead2.val) {
tail.next = pHead2;
pHead2 = pHead2.next;
} else {
tail.next = pHead1;
pHead1 = pHead1.next;
}
tail = tail.next;
}
tail.next = pHead1 == null ? pHead2 : pHead1;
return head;
}
BM5 合并k个已排序的链表
题目
思路
多路归并:使用优先级队列来存储每个链表的头结点,然后每次都弹出栈顶元素【弹出后入栈当前元素.next - 进行判空】,最后优先级队列为空即排完
代码实现
public ListNode mergeKLists1 (ArrayList<ListNode> lists) {
// write code here
PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
// 将头结点都放入queue中
for(ListNode node : lists) {
if(node != null) {
queue.offer(node);
}
}
// 开始多路归并
ListNode head = null;
ListNode tail = null;
while (!queue.isEmpty()) {
ListNode node = queue.poll();
if(head == null) {
head = tail = node;
} else {
tail.next = node;
tail = tail.next;
}
if(node.next != null) {
node = node.next;
queue.offer(node);
}
}
if(tail != null) {
tail.next = null;
}
return head;
}
BM6 判断链表中是否有环
题目
思路
快慢指针,同时出发,快的走两步,慢的走一步,如果快的为空则表示没有环,如果两个遇到了就表示有环 a 快的只能走两步:走两步的话相当于每次追1步,如果有环肯定会追到的;但是如果走三步的话,每次追两步可能会错过
代码实现
public boolean hasCycle(ListNode head) {
if(head == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if(slow == fast) {
return true;
}
}
return false;
}
BM7 链表中环的入口结点
题目
思路
快慢指针 + 找规律
代码实现
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode slow = pHead;
ListNode fast = pHead;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
ListNode meet = slow;
while (meet != pHead) {
meet = meet.next;
pHead = pHead.next;
}
return meet;
}
}
return null;
}
BM8 链表中倒数最后k个结点
题目
思路
快指针先走k步,然后快慢指针一起走,快指针到结尾,慢指针就是结果了
防止 k > 链表长度
代码实现
public ListNode FindKthToTail (ListNode pHead, int k) {
// fast先走k步
ListNode slow = pHead;
ListNode fast = pHead;
while (k > 0) {
if(fast == null) {
return null;
}
fast = fast.next;
k--;
}
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
BM9 删除链表的倒数第n个节点
题目
思路
搞一个虚拟节点,然后像上题一样找倒数第k个节点,但是多一个pre节点,记录着慢指针的前一个节点,最后删除
代码实现
public ListNode removeNthFromEnd (ListNode pHead, int k) {
// write code here
if (pHead == null) {
return pHead;
}
if (k == 0) {
return pHead;
}
ListNode dummyNode = new ListNode(-1);
dummyNode.next = pHead;
ListNode fast = pHead;
ListNode slow = pHead;
while (k > 0) {
if (fast == null) {
return null;
}
fast = fast.next;
k--;
}
ListNode pre = dummyNode;
while (fast != null) {
pre = slow;
slow = slow.next;
fast = fast.next;
}
// 删除slow
pre.next = slow.next;
return dummyNode.next;
}