这是阿茄的LeetCode学习之路将介绍数据结构中链表相关的问题的解法,本讲总共整理3题。
160.相交链表[简单]
说明:
编写一个程序,找到两个单链表相交的起始节点。
- 可假定整个链表结构中没有循环。
- 如果两个链表没有交点,返回null。
- 返回结果后,尽可能不改变链表原有结构。
- 要求时间复杂度为 O(n),空间复杂度为 O(1)。
- 这里的链表是末尾链相同,前端链不同;
示例:
解答:
解法一:利用集合来存储链表A的值,然后通过链表B对象和集合中的对象对比,来判断是否存在交点。但是,这种方法的空间复杂度为O(n)。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set s = new HashSet();
ListNode p = headA;
ListNode q = headB;
while(p!=null) {
s.add(p);
p = p.next;
}
while(q!=null) {
//比较的对象的地址
if(s.contains(q)) {
return q;
}
q = q.next;
}
return null;
}
}
解法二:双指针法,将长链表的指针位置变化,使两指针指向的位置能够在指针之后的节点的个数使一样多。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
int lenA = 0; int lenB = 0;
ListNode a = headA, b = headB;
//计算链表A,B的长度
while (a != null) {
a = a.next;
len1++;
}
while (b != null) {
b = b.next;
len2++;
}
a = headA;b = headB;
//将两链表指针位置改变,使后面的节点数一样多
if (len1 > len2) {
int num = len1 - len2;
while (num > 0) {
a = a.next;
num--;
}
} else {
int num = len2 - len1;
while (num > 0) {
b = b.next;
num--;
}
}
while (a != null) {
if (a == b) return a;
else {
a = a.next;
b = b.next;
}
}
return null;
}
}
解法三:双指针,利用两个链表的长度和求解。当链表A遍历到末尾节点返回链表B的开头遍历,同理,当链表B遍历到末尾节点返回链表A的开头遍历,最终两个指针会在交点处相遇。即,a+c+b=b+c+a。(a:A链独有节点,b:B链独有节点,c:共有节点)
public class Solution{
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA, B = headB;
while (A != B) {
A = (A == null) ? headB : A.next;
B = (B == null) ? headA : B.next;
}
return A;
}
}
注:
java中很多方法利用object类的equals方法比较,而在没有重写equals方法的情况下,默认的equals方法是**比较两个对象的地址(即,不是比较两个对象的属性值)。内置的equals方法源码如下:
public boolean equals(Object o) {
return this == o;
}
所以,如果要比较两个对象的属性值,需要重写equals方法:如下:
@Override
public boolean equals(Object o) {
return super.equals(o);
}
206.反转链表[简单]
说明:
反转一个单链表。进阶:迭代或递归反转。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解答:
解法一:直接存储,调用内置函数。ArrayList存储链表属性,然后reverse方法反转后,再遍历写入链表中(该方法思路很直接,就不写了)
解法二:迭代法(头插法),设置哨兵节点并维护指向节点的指针。
public class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead = new ListNode(-1);//哨兵节点
ListNode pre = newHead;//指针
while (head != null) {
ListNode next = head.next;
head.next = pre.next;
pre.next = head;
head = next;
}
return newHead.next;
}
}
迭代法:(结合动图理解)
class Solution {
public ListNode reverseList(ListNode head) {
//申请节点,pre和 cur,pre指向null
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
//记录当前节点的下一个节点
tmp = cur.next;
//然后将当前节点指向pre
cur.next = pre;
//pre和cur节点都前进一位
pre = cur;
cur = tmp;
}
return pre;
}
}
迭代法动图图示[2]:
解法三:递归法
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode next = head.next;
ListNode newHead = reverseList(next);
next.next = head;
head.next = null;
return newHead;
}
}
递归法动图图示[2]:
注:
这里简单讲解一下迭代和递归的区别。
递归(recursion):递归常被用来描述以自相似方法重复事物的过程,指的是在函数定义中使用函数自身的方法。递归是一个树结构。
迭代(iteration):重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。迭代是一个环结构[1]。
21.合并两个有序链表[简单]
说明:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
解答:
解法一:递归法,这个递归还算好理解一点,时间和空间复杂度均为O(n+m)(递归需要消耗栈空间,栈空间的消耗和递归深度有关)。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
else if (l2 == null) {
return l1;
}
else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
解法二:迭代法(尾插法),首先,我们设定一个哨兵节点 newhead
,这可以在最后让我们比较容易地返回合并后的链表。我们维护一个 pre
指针,我们需要做的是调整它的 next
指针。时间复杂度为O(n+m),空间复杂度为O(1)。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode newhead = new ListNode(-1);
ListNode pre = newhead;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
pre.next = l1;
l1 = l1.next;
}
else{
pre.next = l2;
l2 = l2.next;
}
pre = pre.next;
//合并剩下的节点项
}
pre.next = (l1==null) ? l2 : l1;
return newhead.next;
}
}
参考文章:
[1]https://www.jianshu.com/p/32bcc45efd32
[2]https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-fan-zhuan-lian-biao-by-user74/
文中部分图片使用上述链接文图片,侵删。
文章内容会在个人微信公众号同步更新,欢迎关注“敲代码的阿茄”!!!