nvm中下载node后npm命令找不到

Hello Word(Java)

  返回  

Leetcode刷题系列java版-----链表(简单)

2021/7/21 0:49:46 浏览:

我写的这个系列的博客解题的算法不一定是时间和空间复杂度最优的解,一切以能AC和算法模板普适性为原则解题

首先是链表的增删改查,所有问题都是以链表的增删改查为核心去变种的,改和查按照条件去遍历链表即可,下面直接看leetcode原题:


1.移除链表元素(链表删除节点的模板题)

 要点:

①进来一定要先记得判空,在访问next的时候也一定要注意代码是否会造成空指针异常,如果一个类比如ListNode题中的两个属性val和next,如果当前节点node为null,那么访问val和next的时候都会报空指针异常,也就是只有当node!=null时才能调用其属性 

②构造一个节点指向头节点

③遍历链表不断判断root.next的值找到要删除的值,找到了就让root.next指向root后面的后面那个节点

④保存根节点的备份,最后返回

链表删除算法的时候复杂度为O(n),空间复杂度为常数

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null)return null;//一定要记得先判空
        ListNode root = new ListNode();
        ListNode r = root;
        root.next = head;//新建一个新的头节点指向真正的头节点
        while(root.next!=null){//遍历链表
            ListNode last = root.next;
            if(last.val==val){
                root.next = last.next;
                continue;
            }
            root =  last;
        }
        return r.next;//因为第一个节点是自己造的,真正的头节点是r.next
        
    }
}

2.合并两个有序链表 (链表的遍历和新增)

 这题也是做法很多种,递归非递归都能做,但是往往容易因为没处理好next的指向而导致出错,下面的算法空间复杂度和时间复杂度都是O(m+n),m和n是两个链表长度,空间复杂度虽然高,但是好写好理解

做法:构造一个节点第三条链表的头节点,这个链表是要返回的,题目给的两个链表已经是升序了,所以直接用双指针一个个节点比较遍历,谁的值小就插入到第三条链表即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* root = new ListNode();//重新构造一个新的节点作为第三条链表的头节点
        ListNode* ans = root;
        while(l1&&l2){//遍历两个链表,当两个链表节点都不为空的时候不断比较两个链表的值,有点像相当于双指针,哪个小就插入到第三条链表的节点种
            root->next = new ListNode();
            root=root->next;
            if(l1->val<l2->val){
                root->val = l1->val;
                l1 = l1->next;
            }else{
                root->val = l2->val;
                l2 = l2->next;
            }
        }
        //其中一条链表剩余部分可以直接让root指过去即可
        if(l1)root->next = l1;
        else root->next = l2;
        return ans->next;//返回第三条链表的头节点
    }
};

3.环形链表

 第二种用Set的做法要背一下,因为当链表中遇到相同值的不同节点时,就是要通过set的方法来判断两个节点的地址是否相等,这个需求经常会碰到

(160.相交链表也是可以用类似的手段来解),传送门:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
/*第一种做法(取巧的方法):因为节点的值范围有给出来,所以设置一个较大的数作为非法值,访问过后就把节点值设成非法值,所以只要遍历到有非法值的点说明存在环*/
public class Solution {
    public boolean hasCycle(ListNode head) {
        while(head!=null){
            if(head.val==100010){
                return true;
            }
            head.val=100010;
            head = head.next;
        }
        return false;
    }
}


/*
第二种:直接用set集合的contains方法来判断是否有节点重复加入set,节点已存在会返回false
*/
public class Solution {
    public boolean hasCycle(ListNode head) {
        Set<ListNode> set = new HashSet<ListNode>();
        while (head != null) {
            if (set.contains(head)) {
                return true;
            }
            set.add(head);
            head = head.next;
        }
        return false;
    }
}
/*第三种:快慢指针,详见题解,原理就是两个速度不一样的指针如果遇到了环,循环到最后一定会相遇*/
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}

4.反转链表(很重要!)

反转链表的模板要背一下,也是构造一个新的链表,每次分别操作两个节点,ne是当前要处理的节点,root时刻直线反转后链表的头节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode root = new ListNode();
        ListNode ne = new ListNode();
        if(head==null)return head;
        root = new ListNode(head.val);
        head = head.next;
        while(head!=null){
            ne = new ListNode(head.val);
            ne.next = root;
            root = ne;
            head = head.next;
        }
        return root;
    }
}

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号