链表

LeetCode链表

2. 两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dump = new ListNode(-1);
        ListNode temp = dump;
        //代表进位
        int carry = 0;
        
        while(l1 != null || l2 != null) {
            int sum = carry;
            if(l1 != null) {
                sum += l1.val;
                l1 = l1.next;
            }
            if(l2 != null) {
                sum += l2.val;
                l2 = l2.next;
            }
            //如果当前位数大于等于10进位,反之不进位
            if(sum >= 10) {
                carry = 1;
                sum -= 10;
            }else {
                carry = 0;
            }
            temp.next = new ListNode(sum);
            temp = temp.next;
        }
        if(carry == 1) {
            temp.next = new ListNode(1);
        }
        return dump.next;
    }

445. 两数相加Ⅱ

给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7

思路:这题和第2题很类似,可以把这题利用栈转为第二题,主要这题输出的是正向链表,所以可以利用头插法来添加链表,可以少去一次链表反转的操作。

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Stack<Integer> stack1 = new Stack<>();
        Stack<Integer> stack2 = new Stack<>();
        ListNode dump =  new ListNode(-1);

        while (l1 != null) {
            stack1.push(l1.val);
            l1 = l1.next;
        }
        while (l2 != null) {
            stack2.push(l2.val);
            l2 = l2.next;
        }

        int carry = 0;
        while (!stack1.isEmpty() || !stack2.isEmpty()) {
            int sum = carry;
            if(!stack1.isEmpty()) {
                sum += stack1.pop();
            }
            if(!stack2.isEmpty()) {
                sum += stack2.pop();
            }
            //如果大于等于10,进位
            if(sum >= 10) {
                carry = 1;
                sum -= 10;
            }else {
                carry = 0;
            }

            //使用头插法插入
            ListNode next = dump.next;
            dump.next = new ListNode(sum);
            dump.next.next = next;
        }
        //说明还有进位
        if(carry == 1) {
            ListNode next = dump.next;
            dump.next = new ListNode(1);
            dump.next.next = next;
        }
        return  dump.next;
    }

876. 链表的中间节点(快慢指针)

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
思路:快慢指针,慢指针跑一步,快指针跑两步

public ListNode middleNode(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode fast = head;
        ListNode slow = head;

        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

19. 删除链表的倒数第N个节点(双指针)

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?

public ListNode removeNthFromEnd(ListNode head, int n) {
        if(n <= 0) return null;
        ListNode dump = new ListNode(-1);
        dump.next = head;
        ListNode fast = dump;
        ListNode slow = dump;
     
     //向前移动n+1步 
while(n >= 0) { if(fast == null) { throw new RuntimeException("参数不合法"); } fast = fast.next; n--; } while(fast != null) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return dump.next; }

 

141. 环形链表(快慢指针)

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

mark

示例 2:
输入: head = [1,2], pos = 0
输出: true
解释: 链表中有一个环,其尾部连接到第一个节点。

mark

public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null) {
            return false;
        }

        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) {
                return true;
            }
        }
        return false;

    }

 

141. 环形链表Ⅱ(快慢指针)

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

说明: 不允许修改给定的链表。 ps:上题的基础上返回入环的第一个节点

public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        //如果是有环,则fast走了2nb步, slow走了nb步, 其中b为环的长度(假设时间为t,一个环的长度为b f=2t,s=t,f=s+nb)
        while(true) {
            if(fast == null || fast.next == null) {
                return null;
            }
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                break;
            }
        }
        fast = head;
        while(fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

234. 回文链表(快慢指针)

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) {
            return true;
        }

        ListNode pre = null;
        ListNode fast = head;
        ListNode slow = head;
        ListNode next = null;
        
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            next = slow.next;
            slow.next = pre;
            pre = slow;
            slow = next;
            
        }
        //如果是偶数节点,fast = null, slow = 中间的后一个节点
        //如果是基数节点,fast.next = null slow = 中间的一个节点
        if(fast != null) slow = slow.next;
        while(slow != null) {
            if(pre.val != slow.val) {
                return false;
            }
            pre = pre.next;
            slow = slow.next;
        }
        return true;
    }

 

160. 相交链表(双指针)

编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
mark

在节点 c1 开始相交。

输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) {
            return null;
        }

        ListNode pA = headA;
        ListNode pB = headB;
        while(pA != pB) {
            if(pA == null) {
                pA = headB;
            }else if(pB == null) {
                pB = headA; 
            }else {
                pA = pA.next;
                pB = pB.next;
            } 
        }
        return pA;
    }

 

328. 奇偶链表(双指针)

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

示例 2:

输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL

 public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null || head.next.next == null) {
            return head;
        }
        //分别指向2条链表
        ListNode odd = head, even = head.next;
        while(even != null && even.next != null) {
            ListNode tmp = odd.next;
            odd.next = even.next;
            even.next =  even.next.next;
            odd.next.next = tmp;

            odd = odd.next;
            even = even.next;
        }
        return head;
    }

 

206. 反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

解法一:递归解法

 public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode nextHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return nextHead;
    }

解法二:迭代解法

public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }

        ListNode cur = head.next;
        ListNode pre = head;
        head.next = null;
        
        while(cur != null) {
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

92. 反转链表Ⅱ

反转从位置 mn 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ mn ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

解法一:递归解法

   ListNode successor = null;
    //逆转前n个节点
    public ListNode reverseN(ListNode head, int n) {
        if(n == 1) {
            successor = head.next;
            return head;
        }
        ListNode last = reverseN(head.next,n - 1);
        head.next.next = head;
        head.next = successor;
        return last;
    }
   
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if(m == 1) {
            return reverseN(head,n);
        }
        head.next = reverseBetween(head.next,m - 1, n - 1);
        return head;
    }

 

解法二:迭代解法

利用一个冗余节点,另外头插法插入时候,从链表的后半部开始改。

public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode dump = new ListNode(-1);
        dump.next = head;
        ListNode pre = dump;

        for(int i = 1; i < m; i++) {
            pre = pre.next;
        }
        
        ListNode cur = pre.next;
        //采用头插法
        for(int i = m; i < n; i++) {
            ListNode next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dump.next;
    }

 

24. 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

解法一:递归解法

 public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode next = head.next;
        head.next = swapPairs(next.next);
        next.next = head;
        return next;
    }

解法二:迭代解法

public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode dump = new ListNode(-1);
        ListNode pre = dump, p = head, q = head.next;
        while(q != null) {
            p.next = q.next;
            q.next = p;
            pre.next = q;
            pre = p;
            p = p.next;
            if(p == null) break;
            q = p.next;
        }
        return dump.next;
    }

 

25.K个一组翻转链表

给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5

解法一:递归解法

    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null) {
            return head;
        }
        //找到下一次翻转的头结点
        ListNode tail = head;
        for (int i = 0; i < k; i++) {
            if(tail == null) return head;
            tail = tail.next;
        }
        //翻转前k个元素
        ListNode newHead = reverse(head, tail);
        head.next = reverseKGroup(tail,k);
        return newHead;
    }

    /*
   左闭又开区间
    */
    private ListNode reverse(ListNode head, ListNode tail) {
        ListNode pre = null;
        ListNode next = null;
        while (head != tail) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

 

解法二:迭代解法

public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dump = new ListNode(-1);
        dump.next = head;
        int len = 0;
        int times = 0;

        len = lenListNode(head);
        //统计要循环的次数
        times = len / k;

        ListNode pre = dump;
        ListNode cur = head;
        ListNode next;

        while(times-- > 0) {
            for(int i = 1; i < k; i++) {
                next = cur.next;
                cur.next = next.next;
                next.next = pre.next;
                pre.next = next;
            }
            pre = cur;
            cur = cur.next;
        }
        return dump.next;
    }

    //计算链表的长度
    public int lenListNode(ListNode head) {
        int len = 0;
        while(head != null) {
            len++;
            head = head.next;
        }
        return len;
    }

203. 移除链表元素

删除链表中等于给定值 _val _的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

public ListNode removeElements(ListNode head, int val) {
       if(head==null)
           return null;
        head.next=removeElements(head.next,val);
        if(head.val==val){
            return head.next;
        }else{
            return head;
        }
    }

 

83. 删除排序链表中的重复节点

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode node = deleteDuplicates(head.next);
        if(head.val == node.val) {
            head.next = node.next;
        }
        return head;
    }

 

82. 删除排序链表中的重复元素Ⅱ

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 _没有重复出现 _的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5

public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        //找到重复的元素
        if(head.val == head.next.val) {
            while(head != null && head.next != null && head.val == head.next.val) {
                head = head.next;
            }
            //去重
            return deleteDuplicates(head.next);
        }
        head.next = deleteDuplicates(head.next);
        return head;
    }

21. 合并2个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

解法一:递归解法

 public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null && l2 == null) {
            return  null;
        }
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val <= l2.val) {
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else {
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

 

解法二:迭代解法

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode dump = new ListNode(-1);
        ListNode p = dump;
        while(l1 != null || l2 != null) {
            if(l1 == null) {
                p.next = l2;
                break;
            }
            if(l2 == null) {
                p.next = l1;
                break;
            }
            if(l1.val < l2.val) {
                p.next = l1;
                l1 = l1.next;
            }else {
                p.next = l2;
                l2 = l2.next;
            }
            p = p.next;
        }
        return dump.next;
    }

 

 

148. 排序链表(归并排序)

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }

        //找到中点
        ListNode fast = head.next;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode temp = slow.next;
        slow.next = null;
        ListNode listLeftNode = sortList(head);
        ListNode listRightNode = sortList(temp);
        ListNode h = new ListNode(0);
        ListNode res = h;
        
        while (listLeftNode!=null  && listRightNode != null){
            if(listLeftNode.val <= listRightNode.val){
                h.next = listLeftNode;
                listLeftNode = listLeftNode.next;
                h = h.next;
            }else {
                h.next = listRightNode;
                listRightNode = listRightNode.next;
                h = h.next;
            }
        }
        h.next = listLeftNode == null ? listRightNode : listLeftNode;
        return res.next;

    }

 

23. 合并K个排序链表(归并排序)

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

public ListNode mergeKLists(ListNode[] lists){
        if(lists == null || lists.length == 0){
           return null;
        }
        return merge(lists,0,lists.length-1);
    }

    private ListNode merge(ListNode[] lists, int left, int right) {
        if(left == right) return lists[left];
        int mid = left + (right - right)/2;
        ListNode l1 = merge(lists,left,mid);
        ListNode l2 = merge(lists,mid+1,right);
        return mergeTwoLists(l1,l2);
    }

    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val < l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else {
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

 

posted @ 2020-06-19 10:09  fight12346789  阅读(141)  评论(0编辑  收藏  举报