LeetCode部分习题解答记录-链表篇

本文详细介绍了多种链表操作的算法实现,包括链表的反转、删除排序链表中的重复元素、分隔链表、奇偶链表、两数相加、移除链表元素、合并两个有序链表、两两交换节点、插入排序、排序链表、删除倒数第N个节点、旋转链表和重排链表。这些算法涉及递归、迭代和双指针等技巧,是链表操作的经典问题。
摘要由CSDN通过智能技术生成

206.反转链表

  • 方法1:可以理解为将每个指针反转.
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
	/*
	pre:指向已翻转节点的最后一个节点
	cur:指向待反转节点的第一个节点
	temp:在反转时记录反转节点的后一个节点,避免节点丢失。
	*/
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode temp = null;   
        while(cur != null){
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;     
    }
}
  • 方法2:递归实现
class Solution {
    //递归实现
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        //p节点为反转后的头节点,若1->2->3->4->5->null,反转后头节点为5
        //p是每次递归的返回值,最终一次返回值为5指向了p,因此每次都是返回5
        ListNode p = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return p;
    }
}

92.反转链表II

  • 方法1:迭代法,先找到开始需要反转的位置。然后进行依次反转,进行头插。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        //哨兵节点,确保head加入反转并不影响整体逻辑
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        int index = n - m;
        ListNode pre = sentry;
        ListNode tail = head;
        //找到需要反转的位置
        while(m > 1){
            pre = tail;
            tail =  tail.next;
            m--;
        }
        //依次头插
        while(index > 0){
            ListNode next = tail.next;
            tail.next = next.next;
            next.next = pre.next;
            pre.next = next;
            index--;
        }   
        return sentry.next;
    }
}
  • 方法2:递归
class Solution {
 	// 后驱节点,需要定义为全局变量,定义在reverseN中,每次都为设为null
    ListNode successor = null;
    //递归实现,找到需要反转的起始位置
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (m == 1) {
            return reverseN(head, n);
        }
        // 前进到反转的起点触发 base case
        head.next = reverseBetween(head.next, m - 1, n - 1);
        return head;     
    }
	//反转部分节点
    public ListNode reverseN(ListNode head, int n) {
        if (n == 1) { 
            // 记录第 n + 1 个节点
            successor = head.next;
            return head;
        }
         // 以 head.next 为起点,需要反转前 n - 1 个节点
        ListNode last = reverseN(head.next, n - 1);
        head.next.next = head;
        // 让反转之后的 head 节点和后面的节点连起来
        head.next = successor;
        return last;
    }    
}

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

  • 方法1:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null){
            return head;
        }
        ListNode pre = head;
        while(pre.next != null){
            ListNode cur = pre.next;
            if(pre.val == cur.val){
                pre.next = cur.next;
                cur.next = null;
            }else{
                pre = cur;
                cur = cur.next;
            }
        }
        return head;
    }
}

86.分隔链表

  • 方法1:将链表分割以给定值为界限两个链表,最终拼接链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode smallHead = new ListNode(0);
        ListNode small = smallHead;
        ListNode bigHead = new ListNode(0);
        ListNode big = bigHead;
        while(head != null){
            if(head.val < x){
                small.next = head;
                small = small.next;
            }else{
                big.next = head;
                big = big.next;
            }
            head = head.next;
        }
        big.next = null;
        small.next = bigHead.next;
        return smallHead.next;
    }
}

328.奇偶链表

  • 分析:86题类似,可以按照奇偶分为两个链表,最终拼接。
  • 代码1:稍显麻烦
class Solution {
     public ListNode oddEvenList(ListNode head) {
         ListNode smallHead = new ListNode(0);
         ListNode small = smallHead;
         ListNode bigHead = new ListNode(0);
         ListNode big = bigHead;
         int index = 1;
         while(head != null){
             if(index % 2 == 1){
                 small.next = head;
                 small = small.next;
             }else{
                 big.next = head;
                 big = big.next;
             }
             head = head.next;
             index++;
         }
         big.next = null;
         small.next = bigHead.next;
         return smallHead.next;
     }
}
  • 代码2:直接在原始链表中判断奇偶数,主要是奇后必为偶,偶后必为奇。
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null){
            return head;
        }
        ListNode odd = head;
        ListNode even = head.next;
        ListNode evenHead = even;
        while(even != null && even.next != null){
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;   
        }
        odd.next = evenHead;
        return head;
    }
}

2.两数相加

  • 分析:将两个链表看成是相同长度的进行遍历,如果一个链表较短则在前面补0,比如987 + 23 = 987 + 023 = 1010。同时,还需要记录每一次的进位与下一次的两数相加,若最终有进位还需进行处理。
  • 代码演示:
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
       ListNode pre = new ListNode(0);
        ListNode cur = pre;
        int carry = 0;
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum  > 9 ? 1 : 0;
            sum = sum % 10;
            cur.next = new ListNode(sum);
            cur = cur.next;
            
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry == 1) {
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

445.两数相加二

  • 分析:可以通过第二题的思路,先将链表反转再就转为第二题,但是如果题目要求不可更改原链表的话,需要借助数据结构:栈。
  • 代码1:借助哨兵节点,新节点始终插入到哨兵与链表的头节点之间,最终返回哨兵节点的next,也就是链表的头节点。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int carry = 0;
        ListNode pre = new ListNode(0);
        pre.next = null;
        Stack<Integer> s1 = new Stack<>();
        Stack<Integer> s2 = new Stack<>();
        while(l1 != null){
            s1.push(l1.val);
            l1 = l1.next;
        }
        while(l2 != null){
            s2.push(l2.val);
            l2 = l2.next;
        }
        ListNode ans = null;
        while(!s1.empty() || !s2.empty()){
            int v1 = s1.empty() ? 0 : s1.pop();
            int v2 = s2.empty() ? 0 : s2.pop();
            int sum = v1 + v2 + carry;
            System.out.println(sum);
            carry = sum > 9 ? 1 : 0;
            sum = sum % 10;
            ListNode cur = new ListNode(sum);
            cur.next = pre.next;
            pre.next = cur;
        }
        if(carry == 1){
            ListNode cur = new ListNode(carry);
            cur.next = pre.next;
            pre.next = cur;
        }
        return pre.next;
    }
}
  • 代码2:在代码1的基础上做一些优化,不需要哨兵节点。新节点直接指向原链表的头节点即可。同时将carry != 0放在while循环中,若最终出现carry == 1的情况还会执行循环体,简化代码。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //解法1:链表反转加两个相加第二题
        //解法2:使用栈数据结构
        int carry = 0;
        Stack<Integer> s1 = new Stack<>();
        Stack<Integer> s2 = new Stack<>();
        while(l1 != null){
            s1.push(l1.val);
            l1 = l1.next;
        }
        while(l2 != null){
            s2.push(l2.val);
            l2 = l2.next;
        }
        ListNode ans = null;
        while(!s1.empty() || !s2.empty() || carry != 0){
            int v1 = s1.empty() ? 0 : s1.pop();
            int v2 = s2.empty() ? 0 : s2.pop();
            int sum = v1 + v2 + carry;
            System.out.println(sum);
            carry = sum > 9 ? 1 : 0;
            sum = sum % 10;
            ListNode curnode = new ListNode(sum);
            curnode.next = ans;
            ans = curnode;
        }
        return ans;
    }

203.移除链表元素

  • 方法1:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){
            return null;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = sentry;
        while(cur.next != null){
            if(cur.next.val == val){
                ListNode del = cur.next;
                cur.next = del.next;
                del.next = null;
            }else{
                cur = cur.next;
            }
        }
        return sentry.next;
    }
}

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

  • 方法1:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        if(head == null){
            return head;
        }
        ListNode pre = sentry;
        while(pre.next != null && pre.next.next != null){
            if(pre.next.val == pre.next.next.val){
                ListNode cur = pre.next;
                //记录当前重复节点的最后一个位置
                while(cur.next != null && cur.val == cur.next.val){
                    cur = cur.next;
                }
                pre.next = cur.next;
            }else{
                pre = pre.next;
            }
        }
        return sentry.next;
    }
}

32.合并两个有序链表

  • 方法1:
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null){
            return l2;
        }
        if(l2 == null){
            return l1;
        }

        ListNode newHead = new ListNode(0);
        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;
    }
}

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

在这里插入图片描述

  • 方法1:
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode pre = sentry;
       
        while(pre.next != null && pre.next.next != null){
            ListNode node1 = pre.next;
            ListNode node2 = node1.next;
            ListNode next = node2.next;
            node2.next = node1;
            node1.next = next;
            pre.next = node2;
            pre = node1;
        }
        return sentry.next;
    }
}

147.对链表进行插入排序

  • 方法1:先找到不符合排序顺序的节点,然后从头节点可以,依次比较,找到合适的位置插入
lass Solution {
    public ListNode insertionSortList(ListNode head) {
        if(head == null){
            return head;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = head.next;
        ListNode pre = head;
        while(cur != null){
            if(pre.val < cur.val){
                pre = cur;
                cur = cur.next;
            }
            else{
                ListNode p = sentry; 
                //找到比cur大的元素,前指针为p
                while(p.next.val < cur.val){ //p.next != null 可以去掉,因为时从开始找最多找到的情况到不了从cur,因此肯定不为空
                    p = p.next;
                }
                pre.next = cur.next;
                cur.next = p.next;
                p.next = cur;
                cur = pre.next;
            }
        }
        return sentry.next;
    }
}

148.排序链表

  • 方法1:使用归并排序
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }

        ListNode mid = middleNode(head);
        ListNode rightHead = mid.next;
        mid.next = null;

        ListNode left = sortList(head);
        ListNode right = sortList(rightHead);

        return merge(left,right);
    }
    public ListNode middleNode(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
       	//若链表为1->2->3->null,则mid=1
        ListNode slow = head, fast = head.next.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public ListNode merge(ListNode l1, ListNode l2){
        ListNode sentry = new ListNode(-1);
        ListNode curr = sentry;

        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }

            curr = curr.next;
        }

        curr.next = l1 != null ? l1 : l2;
        return sentry.next;
    }
}

19.删除链表中的倒数第N个节点

  • 方法1(麻烦):总体思路为先确定链表的长度,然后根据索引找到倒数第n个元素,删除即可
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return head;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = head;
        ListNode pre = sentry;
        int index = 0;
        int len = 0;
        while(cur != null ){
            cur = cur.next;
            len++;
        } 
        cur = head;
        while(cur != null){
            if(len - index != n){
                pre = cur;
                cur = cur.next;
                index++;
            }else{
                pre.next = cur.next;
                break;
            }
        }
        return sentry.next;   
    }
}
  • 方法2:利用双指针,pre指针与last指针之间的索引相差n+1,当last指针指向null时,pre指针指向删除节点的前一个节点,即可进行删除。
class Solution {
   public ListNode removeNthFromEnd(ListNode head, int n) {
       ListNode sentry = new ListNode(0);
       sentry.next = head;
       int index = 0;
       ListNode pre = sentry;
       ListNode last = sentry;
       while(last != null){
           if(index <= n){
               index++;
           }else{
               pre = pre.next;
           }
           last = last.next;
       }
       pre.next = pre.next.next;
       return sentry.next;
   }
  • 方法3:与方法2相同的逻辑,只是写法有所区别。
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode pre = sentry;  
        ListNode last = sentry;
        for(int i = 0; i < n+1;i++){
            last = last.next;
        }
        while(last != null){
            last = last.next;
            pre = pre.next;
        }
        pre.next = pre.next.next;
        return sentry.next;
    }

}

61.旋转链表

  • 方法1:先找到倒数第k个节点,方式如同题19,利用双指针找到第k-1个节点,然后依次头插第K个节点。
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = head;
        int len = 0;
        while(cur != null){
            cur = cur.next;
            len++;
        }
        int index = k % len;
        
        ListNode pre = sentry;
        ListNode last = sentry;
        for(int i = 0 ; i < index + 1;i++){
            last = last.next;
        }
        while(last != null){
            pre = pre.next;
            last = last.next;
        }
        ListNode p = sentry;
        while(pre.next != null){
            cur = pre.next;
            pre.next = cur.next;
            cur.next = p.next;
            p.next = cur;
            p = p.next;
        }
        return sentry.next;
    }

    public ListNode rotateRight(ListNode head, int k) {
        if(head == null){
            return head;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = head;
        int len = 1;
        while(cur.next != null){
            cur = cur.next;
            len++;
        }
        cur.next = head;
        int index = len - k % len;
        ListNode last = sentry;
        for(int i = 0 ; i < index;i++){
            last = last.next;
        }
        ListNode temp = last.next;
        sentry.next = temp;
        last.next = null;
        return sentry.next;
    }
}
  • 方法2:将链表串环,然后在合适的位置断开链表,则旋转成功。
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null){
            return head;
        }
        ListNode sentry = new ListNode(0);
        sentry.next = head;
        ListNode cur = head;
        int len = 1;
        while(cur.next != null){
            cur = cur.next;
            len++;
        }
        cur.next = head;
        int index = len - k % len;
        ListNode last = sentry;
        for(int i = 0 ; i < index;i++){
            last = last.next;
        }
        ListNode temp = last.next;
        sentry.next = temp;
        last.next = null;
        return sentry.next;
    }
}

143.重排链表

  • 方法1:确定链表长度,确定循环次数。循环体内部逻辑为,每次找到最后一个节点,然后通过头插法插到合适的位置。
class Solution {
    public void reorderList(ListNode head) {
        if(head !=null){
            ListNode last = head;
            ListNode pre = head;
            int len = 1;
            while(last.next != null){
                last = last.next;
                len++;
            }  

            int index = 1;
            int sum = (len - 1) / 2;//循环次数
            while(sum-- > 0){
                last = pre;
                while(last.next.next != null){
                    last = last.next;
                }    
                ListNode temp = last.next;
                temp.next = pre.next;
                pre.next = temp;
                last.next = null;
                pre = temp.next;
            }
        }
    }
}
  • 方法2:如方法1所示,链表每次需要遍历才能找到末尾节点。因此可以借助数组与双指针完成操作。
class Solution {
   public void reorderList(ListNode head) {
        if(head == null){
            return;
        }
        ArrayList<ListNode> list = new ArrayList<>();
        ListNode cur = head;
        while(cur != null){
            list.add(cur);
            cur = cur.next;
        }
        int left = 0;
        int right = list.size() - 1;
        while(left < right){
            list.get(left).next = list.get(right);
            left++;
            if(left == right){
                break;
            }
            list.get(right).next = list.get(left);
            right--;
        }
        list.get(left).next = null;
    }
}

234.回文链表

  • 方法1:与143题方法2类似,通过数组以及双指针依次比较对应的索引的ListNode.val是否相等。
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null){
            return true;
        }
        ArrayList<Integer> list = new ArrayList<>();
        ListNode cur = head;
        while(cur != null){
            list.add(cur.val);
            cur = cur.next;
        }
        int left = 0;
        int right = list.size() - 1;
        while(left < right){
            if(!list.get(left).equals(list.get(right)) ){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}
  • 方法2:找到中间节点,然后反转后半部分链表。此刻,即原始链表变为两个链表,依次比较链表的值,确定是否回文。
class Solution {
	public boolean isPalindrome(ListNode head) {
        ListNode fast = head, slow = head;
        //通过快慢指针找到中点
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //如果fast不为空,说明链表的长度是奇数个
        if (fast != null) {
            slow = slow.next;
        }
        //反转后半部分链表
        slow = reverse(slow);

        fast = head;
        while (slow != null) {
            //然后比较,判断节点值是否相等
            if (fast.val != slow.val)
                return false;
            fast = fast.next;
            slow = slow.next;
        }
        return true;
    }

	//反转链表
    public ListNode reverse(ListNode head) {
        ListNode prev = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值