代码随想录一刷-Day04
LeetCode24. 两两交换链表中的节点
尝试了使用 stack 来辅助处理,虽然代码可能简洁一点,但是性能远远不足,毕竟 栈 的存取还挺耗时的
public ListNode swapPairs(ListNode head) {
if (head == null) {
return head;
}
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
Stack<ListNode> stack = new Stack<>();
stack.push(dummyHead);
ListNode cur = head;
while (cur != null || !stack.isEmpty()) {
if (stack.size() > 2) {
ListNode n2 = stack.pop();
ListNode n1 = stack.pop();
ListNode pre = stack.pop();
n2.next = n1;
n1.next = cur;
pre.next = n2;
stack.push(n1);
}
if (cur != null) {
stack.push(cur);
cur = cur.next;
} else {
stack.clear();
}
}
return dummyHead.next;
}
卡哥的题解:
public ListNode swapPairs(ListNode head) {
ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
dumyhead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode cur = dumyhead;
ListNode temp; // 临时节点,保存两个节点后面的节点
ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
while (cur.next != null && cur.next.next != null) {
temp = cur.next.next.next;
firstnode = cur.next;
secondnode = cur.next.next;
cur.next = secondnode; // 步骤一
secondnode.next = firstnode; // 步骤二
firstnode.next = temp; // 步骤三
cur = firstnode; // cur移动,准备下一轮交换
}
return dumyhead.next;
}
时间复杂度:O(n)
空间复杂度:O(1)
LeetCode19.删除链表的倒数第N个节点
利用数组的随机读取特性可实现一趟扫描
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode[] arr = new ListNode[32];
int size = 0;
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
// 直接将虚拟头节点也一起放入数组中
arr[size++] = dummyHead;
ListNode cur = dummyHead;
while (cur != null) {
// 通过遍历,将链表的节点放入数组
arr[size++] = cur;
cur = cur.next;
}
// 要删除的节点下标就是 size - n,因为 size 实际上是 总长度 + 1
int delIndex = size - n;
// 剩下的就是删除操作了
ListNode preNode = arr[delIndex - 1];
ListNode delNode = arr[delIndex];
preNode.next = delNode.next;
return dummyHead.next;
}
时间复杂度: O(n)
空间复杂度: O(1)
面试题 02.07. 链表相交
可行但不高效:Set 集合
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// Set集合?
HashSet<ListNode> set = new HashSet<>();
ListNode cur = headA;
while (cur != null) {
set.add(cur);
cur = cur.next;
}
cur = headB;
while (cur != null) {
if (set.contains(cur)) {
return cur;
}
set.add(cur);
cur = cur.next;
}
return null;
}
看了 Carl 哥的思路之后:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 相交链表的特性 —— 从相交点开始,两条链表的尾长是一样的
// 思路:求出两个链表的长度,将两个指针指向最短链表的起点以及更长链表的尾长与短链表相等的节点
// (求出两个链表长度的差值,然后让curA移动到,和curB末尾对齐的位置)
// 同步遍历,找到两个相同的节点,即两个链表的相交点
// 求两个链表的长度
int lenA = 0, lenB = 0;
ListNode cur = headA;
while (cur != null) {
lenA++;
cur = cur.next;
}
cur = headB;
while (cur != null) {
lenB++;
cur = cur.next;
}
ListNode longList = null, shortList = null;
int longLen = 0, shortLen = 0;
if (lenA > lenB) {
longList = headA;
shortList = headB;
longLen = lenA;
shortLen = lenB;
} else {
longList = headB;
shortList = headA;
longLen = lenB;
shortLen = lenA;
}
// 两条链表长度之差
int gap = longLen - shortLen;
// 将长链表指针移动到第 gap 个节点
while (gap > 0) {
longList = longList.next;
gap--;
}
// 同步遍历,指针相同则返回
while (longList != null && shortList != null) {
if (longList == shortList) {
return longList;
}
longList = longList.next;
shortList = shortList.next;
}
return null;
}
时间复杂度:O(n + m)
空间复杂度:O(1)
LeetCode142.环形链表II
判断链表的环,只记得需要用到快慢指针,但是对细节的记忆有错误
首先,判断链表中是否存在环的方法:快慢指针从头节点出发,快指针一次走两步,慢指针一次走一步,如果两个指针相遇,则链表中存在环
然后,获取环的入口节点的方法:在上一步的基础上,将一个指针指向快慢指针相遇的节点,另一个指针指向头节点,两个指针同步遍历(一次一步),相遇的节点就是环的入口
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
// 判断链表是否有环,我记得需要使用快慢指针
// 快指针一次走两步,慢指针一次走一步,如果相遇,则有环
ListNode fast = head.next.next, slow = head.next;
ListNode meetNode = null;
while (slow != null) {
if (fast == slow) {
meetNode = fast;
break;
}
if (fast.next == null || fast.next.next == null) {
break;
}
fast = fast.next.next;
slow = slow.next;
}
if (meetNode == null) {
return null;
}
// 从相遇节点开始,一个指针从 meetNode,另一个从 head,同步遍历,相遇的节点就是环的入口
slow = head;
fast = meetNode;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)