1.两个链表第一个公共子节点
链表节点定义:
class ListNode {
public int val;
public ListNode next;
ListNode(int val) {
this.val = val;
next = null;
}
}
1.1 哈希和集合
先将一个链表元素全部存进map,再遍历另一链表,同时检测值是否存在map中。
本题用集合实现更合适,且思路一致
public ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
While(headA != null) {
set.add(headA);
headA = headA.next;
}
while(headB != null) {
if(set.contain(headB)) {
return headB;
}
headB = headB.next;
}
return null;
}
1.2 使用栈
先将两个链表压入两个栈中,再分别出栈进行,如果相等就继续出栈,最晚出栈的相同元素即为交点。这种方法需要两个O(n),仅作练手
import java.util.Stack;
public ListNode findFirstCommonNodeByStack(ListNode headA, ListNode headB) {
Stack<ListNode> stackA = new Stack();
Stack<ListNode> stackB = new Stack();
while(headA != null ) {
stackA.push(headA);
headA = headA.next;
}
while(headb != null) {
stackB.push(headB);
headB = headB.next;
}
ListNode tmp = new ListNode():
while(stackA.size() > 0 && stackB.size() > 0 {
if(stackA.peek() == stackB.peek()) {
tmp = stackA.pop();
stackB.pop();
} else {
break;
}
}
return tmp;
}
2 判断链表是否为回文序列
LeetCode234 判断一个链表是否为回文链表
方法1:将链表元素都赋值到数组中,然后可以从数组两端向中间对比。这种方法会被视为逃避链表,面试
不能这么干。
方法2:将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较两者元素值,只要有一个不相
等,那就不是。
方法3:优化方法2,先遍历第一遍,得到总长度。之后一边遍历链表,一边压栈。到达链表长度一半后就
不再压栈,而是一边出栈,一边遍历,一边比较,只要有一个不相等,就不是回文链表。这样可以节省
半的空间。
方法4:优化方法3:既然要得到长度,那还是要遍历一次涟表才可以,那是不是可以一边遍历一边全部压
栈,然后第二遍比较的时候,只比较一半的元素呢?也就是只有一半的元素出栈,链表也只遍历一半,当
然可以。
方法5:反转链表法,先创建一个链表newList,将原始链表oldList的元素值逆序保存到newList中,然后
重新一边遍历两个链表,一遍比较元素的值,只要有一个位置的元素值不一样,就不是回文链表。
方法6:优化方法5,我们只反转一半的元素就行了。先遍历一遍,得到总长度。然后重新遍历,到达一半
的位置后不再反转,就开始比较两个链表。
方法7:优化方法6,我们使用双指针思想里的快慢指针,fast一次走两步,slow一次走一步。当fast到达
表尾的时候,Sow正好到达一半的位置,那么接下来可以从头开始逆序一半的元素,或者从sow开始逆序
半的元素,都可以。
方法8:在遍历的时候使用递归来反转一半链表可以吗?当然可以,再组合一下我们还能想出更多的方法,
解决问题的思路不止这些了,此时单纯增加解法数量没啥意义了。
压栈方法
public boolean isPalindrome(ListNode head) {
ListNode tmp = head;
Stack<ListNode> stack = new Stack();
while(head != null) {
stack.push(head.val);
head = head.next;
}
while(stack.size() > 0) {
if(stack.peek() != tmp.val) {
return false;
}
stack.pop();
tmp = tmp.next;
}
return true;
}
快慢指针实现:
TODO
3. 合并有序链表
3.1 合并两个有序链表
LeetCode21 将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的。
思路:新建一个链表,分别遍历两个链表,每次比较选择小的节点,插入新链表。
public ListNode mergeTwoLists (ListNode list1, ListNode list2) {
ListNode newHead = new ListNode(-1);
ListNode res = neaHead;
while(list1 != null && list2 != null) {
if(list1 != null && list2 != null) {
if (list1.val > list2.val) {
res.next = list2;
list2 = list2.next;
} else if (list1.val < list2.val) {
res.next = list1;
list1 = list1.next;
} else if (list1.val == list2.val) {
res.next = list2;
list1 = list1.next;
res = res.next;
res.next = list2;
list2 = list2.next;
}
res = res.next;
} else if (null == list1 && list2 != null) {
res.next = list2;
list2 = list2.next;
res = res.next
} else if (list1 != null && list2 == null) {
res.next = list1;
list1 = list1.next;
res = res.next;
}
}
return newHead.next;
}
优化:
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode preNode = new ListNode(-1);
ListNode prev = preNode;
while(list1 != null && list2 != null) {
if(list1.val <= list2.val) {
prev.next = list1;
list1 = list1.next;
} else {
prev.next = list2;
list2 = list2.next;
}
prev = prev.next;
}
prev.next = list1 == null?list2:list1;
return preNode.next;
}
3.2 合并K个链表
两两合并
public ListNode mergeKLists(ListNode[] list) {
ListNode res = null;
for(ListNode node : list) {
res = mergeTwoLists(res, node);
}
return res;
}
3.3 好题
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
ListNode pos1 = list1;
ListNode pos2 = list1;
int count = 0;
while(count < a) {
pos1 = pos1.next;
pos2 = pos.next;
count++;
}
pos1.next = list2;
while(count < b) {
pos2 = pos2.next;
count++;
}
while(list2.next != null) {
list2 = list2.next;
}
list2.next = pos2;
return list1;
}
4. 双指针专题
4.1 寻找中间结点
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
for(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
4.2 寻找倒数第k个元素
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
for(int i = 1; i < k; i++) {
fast = fast.next;
}
while(fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
4.3 旋转链表
思路:取最后k位元素放到链表开头
public ListNode rotateRight(ListNode head, int k) {
if(head == null || k <= 0) {
return head;
}
ListNode fast = head;
ListNode slow = head;
int len = 0;
for(int i = 1; i < k; i++) {
len++;
fast = fast.next;
}
while(fast.next !=null) {
len++;
fast = fast.next;
slow = slow.next;
}
ListNode tmp = slow;
slow.next = null;
fast.next = head;
return tmp.next;
}
5. 删除链表元素专题
5.1 删除特定节点
public ListNode removeElements(ListNode head, int val) {
ListNode dummyNode = new ListNode();
dummyNode.next = head;
while(dummyNode.next != null) {
if(dummyNode.next.val == val) {
dummyNode.next = dummyNode.next.next;
}
dummyNode = dummyNode.next;
}
return head;
}
5.2 删除倒数第n个节点
双指针:
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;
for(int i = 0; i < n && fast != null; i++) {
fast = fast.next;
}
while(fast.next != null) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummy.next;
}
5.3 删除重复元素
5.3.1 重复元素保留一个
public ListNode deleteDuplicates(ListNode head) {
ListNode cur = head;
while(cur.next != null) {
if(cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
5.3.2 重复元素都不要
public ListNode deleteDuplicates(ListNode head) {
ListNode cur = head;
while(cur.next != null && cur.next.next != null) {
if(cur.next.val == cur.next.next.val) {
int x = cur.next.val;
while(cur.next.val == x) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return head;
}
6. 再论第一个公共子节点问题
6.1 拼接两个字符串
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode p1 = pHead1;
ListNode p2 = pHead2;
while(p1 != p2) {
p1 = p1.next;
p2 = p2.next;
if(p1 != p2) {
if(p1 == null) {
p1 = pHead2;
}
if(p2 == null) {
p2 = pHead1;
}
}
}
return p1;
}
6.2 差和双指针
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1 == null || pHead2 == null) {
return null;
}
ListNode cur1 = pHead1;
ListNode cur2 = pHead2;
int l1 = 0, l2 = 0;
while(cur1 != null){
cur1 = cur1.next;
l1++;
}
while(cur2 != null) {
cur2 = cur2.next;
l2++;
}
cur1 = pHead1;
cur2 = phead2;
int sub = l1 > l2 ? l1 - l2 : l2 - l1;
if(l1>l2) {
int a = 0;
while (a < sub) {
cur1 = cur1.next;
a++;
}
}
if(l1 < l2) {
int a = 0;
while(a < sub) {
cur2 = cur2.next;
a++;
}
}
while(cur2 != cur1) {
cur2 =cur2.next;
cur1 = cur1,next;
}
return cur1;
}