1.两个链表第一个公共子节点
1.1 可以考虑将链表元素存储到一个集合里,然后一边遍历第二个链表,一边检测集合中是否存在该结点。本题只需要记录一个结点是否存在,而不需要其他的数据,所以使用 HashSet 。
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.contains(headB))
return headB;
headB = headB.next;
}
return null;
}
1.2 可以考虑使用栈保存链表的结点,栈底为链表的头结点,栈顶为链表的尾结点,然后比较两个栈的栈顶元素是否相等,如果相等就一起出栈,直到最后一次相等的元素一起出栈,这个元素就是两个链表的第一个公共子结点。
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 preNode = null;
while (stackB.size() > 0 && stackA.size() > 0) {
if (stackA.peek() == stackB.peek()) {
preNode = stackA.pop();
stackB.pop();
} else {
break;
}
}
return preNode;
}
1.3 将两个链表一前一后拼接在一起,两种拼接方式的长度是一样的,而且两个拼接后的链表最后几位是相同的,也就是说相同的第一位就是公共节点,可以采用双指针。两个指针最终走的长度是相等的。
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
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;
}
1.4 用长的链表的长度减去短的链表的长度,得到两个链表的差值。长的链表可以先走这个差值,然后两个链表一起走,就能同时到达公共节点处。
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2){
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode current1 = pHead1;
ListNode current2 = pHead2;
int l1 = 0, l2 = 0;
while (current1 != null) {
current1 = current1.next;
l1++;
}
while (current2 != null) {
current2 = current2.next;
l2++;
}
current1 = pHead1;
current2 = pHead2;
int sub = l1 > l2 ? l1 - l2 : l2 - l1;
if (l1 > l2) {
int a = 0;
while (a < sub) {
current1 = current1.next;
a++;
}
}
if (l1 < l2) {
int a = 0;
while (a < sub) {
current2 = current2.next;
a++;
}
}
while (current2 != current1) {
current2 = current2.next;
current1 = current1.next;
}
return current1;
}
2.判断链表是否为回文序列
2.1 将链表元素全部压栈,然后一边出栈,一边重新遍历链表。
public boolean isPalindrome(ListNode head){
ListNode temp = head;
Stack<Integer> stack = new Stack();
while (temp != null) {
stack.push(temp.val);
temp = temp.next;
}
while (head != null) {
if (head.val != stack.pop()) {
return false;
}
head = head.next;
}
return true;
}
2.2 也可以使用集合中的 ArrayList 存储每个 val 值,然后使用 get 方法对前后进行比较。
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<Integer>();
ListNode currentNode = head;
while(currentNode != null){
vals.add(currentNode.val);
currentNode = currentNode.next;
}
int front = 0;
int back = vals.size() - 1;
while(front < back){
if(vals.get(front) != vals.get(back)){
return false;
}
front++;
back--;
}
return true;
}
3.合并有序链表
3.1 创建一个新的链表进行迭代合并两个有序链表。
public ListNode mergeTwoLists(ListNode list1, ListNode list2){
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
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 prehead.next;
}
3.2 合并K个链表:先将两个链表合并,然后循环调用该方法将K个链表都合并进去。
//使用上一步的合并的方法
public ListNode mergeKLists(ListNode[] lists){
ListNode res = null;
for (ListNode list : lists) {
res = mergeTwoLists(res, list);
}
return res;
}
3.3 用一个链表替换掉另一个链表的某一部分:找到对应的结点。
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2){
ListNode pre1 = list1, post1 = list1, post2 = list2;
int i = 0, j = 0;
while (pre1 != null && post1 != null && j < b) {
if (i != a - 1) {
// pre1停在list1中下标为a-1的地方
pre1 = pre1.next;
i++;
}
if (j != b) {
post1 = post1.next;
j++;
}
}
// 刚出循环的post1指向的正是list1的下标为b的地方
// post1.next即b的下一位
post1 = post1.next;
while (post2.next != null) {
// 找到list2的尾结点
post2 = post2.next;
}
pre1.next = list2;
post2.next = post1;
return list1;
}
4.双指针
4.1 寻找中间结点:快指针走两步,慢指针走一步。本写法遇到偶数的时候,慢指针最后会移动到中间偏后的位置,如果循环条件改为fast.next.next是否为空,就可以让慢指针最后移动到中间偏前的位置。
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
4.2 寻找倒数第K个元素:用fast指针提前走,和slow指针形成一个间隔为K个结点的区间,然后一起移动。
public ListNode getKthFromEnd(ListNode head, int k){
ListNode fast = head;
ListNode slow = head;
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
4.3.1 旋转链表:还是卡一个区间,一起移动。
public ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
ListNode temp = head;
ListNode fast = head;
ListNode slow = head;
int len = 0;
while (head != null) {
head = head.next;
len++;
}
if (k % len == 0) {
return temp;
}
//这里不存在链表的长度小于K的问题,所以循环条件不需要给fast判空
while ((k % len) > 0) {
fast = fast.next;
k--;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
ListNode res = slow.next;
slow.next = null;
fast.next = temp;
return res;
}
4.3.2 旋转链表:使用链表反转。【持续更新】。
5.删除链表元素
5.1 删除特定结点:使用虚拟节点dummyHead。
public ListNode removeElements(ListNode head, int val){
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
5.2 删除倒数第n个结点:同样使用双指针卡区间,只不过这次是删除结点。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummt.next = head;
ListNode fast = head;
ListNode slow = dummy;
for (int i = 0; i < n; i++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
5.3.1 删除重复元素:重复元素保留一个,发现下一个元素与当前元素相同就cur.next = cur.next.next将其删除。
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
ListNode cur = head;
while (cur.next != null) {
// 一直遇到重复的就一直比较,直到不重复了才将cur移动到下一位不重复的地方
if (cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
5.3.2 删除重复元素:重复元素都不要,需要创建一个dummy,比较下一个和下一个的下一个的val是否相同,相同则从下一个开始
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val == cur.next.next.val) {
int x = cur.next.val;
while (cur.next != null && cur.next.val == x) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return dummy.next;
}
如果对您有帮助,请点赞关注支持我,谢谢!❤
如有错误或者不足之处,敬请指正!❤