LeetCode刷题之路:链表(1)

这是阿茄的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/
文中部分图片使用上述链接文图片,侵删。

文章内容会在个人微信公众号同步更新,欢迎关注“敲代码的阿茄”!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值