【算法题-分类型总结】链表相关的算法题

1、环形链表(是否有环)

思路:快慢指针

/**

 * 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) {

        ListNode slow = head;

        ListNode fast = head;

        //快指针走的快,只需要判断快指针即可,也可以写成slow != null && fast != null && fast.next != null

        while(fast != null && fast.next != null) {

            slow = slow.next;

            fast = fast.next.next;

            if(slow == fast) {

                return true;

            }

        }

        return false;

    }

}

 

2、合并有序链表

思路:遍历两个链表+虚拟节点返回

/**

 * 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 mergeTwoLists(ListNode l1, ListNode l2) {

        ListNode dummy = new ListNode(0);

        ListNode cur = dummy;

        while(l1 != null && l2 != null) {

            if(l1.val < l2.val) {

                cur.next = l1;

                l1 = l1.next;

                cur = cur.next;

            } else {

                cur.next = l2;

                l2 = l2.next;

                cur = cur.next;

            }

        }

        if(l1 != null) {

            cur.next = l1;

        }

        if(l2 != null) {

            cur.next = l2;

        }

        return dummy.next;

    }

}

 

3、两链表值相加

思路:用两个栈实现

import java.util.*;

 

/*

 * public class ListNode {

 *   int val;

 *   ListNode next = null;

 * }

 */

 

public class Solution {

    /**

     *

     * @param head1 ListNode类

     * @param head2 ListNode类

     * @return ListNode类

     */

    public ListNode addInList (ListNode head1, ListNode head2) {

        ListNode head = new ListNode(-1);

        Stack<Integer> stack1 = new Stack<>();

        Stack<Integer> stack2 = new Stack<>();

        while(head1 != null) {

            stack1.push(head1.val);

            head1 = head1.next;

        }

        while(head2 != null) {

            stack2.push(head2.val);

            head2 = head2.next;

        }

        int carry = 0;

        while(!stack1.isEmpty() && !stack2.isEmpty()) {

            int val = stack1.pop() + stack2.pop() + carry;

            int num = val % 10;

            carry = val / 10;

            ListNode node = new ListNode(num);

            node.next = head.next;

            head.next = node;

        }

        while(!stack1.isEmpty()) {

            int val = stack1.pop() + carry;

            int num = val % 10;

            carry = val / 10;

            ListNode node = new ListNode(num);

            node.next = head.next;

            head.next = node;

        }

        while(!stack2.isEmpty()) {

            int val = stack2.pop() + carry;

            int num = val % 10;

            carry = val / 10;

            ListNode node = new ListNode(num);

            node.next = head.next;

            head.next = node;

        }

        if(carry != 0) {

            ListNode node = new ListNode(carry);

            node.next = head.next;

            head.next = node;

        }

        return head.next;

    }

}

 

4、相交节点/公共节点

思路:set/同一长度寻找相同节点

1)方法1:从相同长度开始

/**

 * Definition for singly-linked list.

 * public class ListNode {

 *     int val;

 *     ListNode next;

 *     ListNode(int x) {

 *         val = x;

 *         next = null;

 *     }

 * }

 */

public class Solution {

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        int lenA = 0, lenB = 0;

        ListNode pA = headA, pB = headB;

        while(pA != null) {

            pA = pA.next;

            lenA++;

        }

        while(pB != null) {

            pB = pB.next;

            lenB++;

        }

        if(lenA > lenB) {

            lenA ^= lenB;

            lenB ^= lenA;

            lenA ^= lenB;

            ListNode temp = headB;

            headB = headA;

            headA = temp;

        }

        int len = lenB - lenA;

        ListNode indexB = headB;

        while(len > 0) {

            indexB = indexB.next;

            len--;

        }

        ListNode indexA = headA;

        while(indexA != indexB) {

            indexA = indexA.next;

            indexB = indexB.next;

        }

        return indexB;

    }

}

2方法2set

import java.util.*;

/*

public class ListNode {

    int val;

    ListNode next = null;

 

    ListNode(int val) {

        this.val = val;

    }

}*/

public class Solution {

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        ListNode p1 = pHead1, p2 = pHead2;

        Set<ListNode> set = new HashSet<>();

        while(p1 != null) {

            set.add(p1);

            p1 = p1.next;

        }

        while(p2 != null) {

            if(set.contains(p2)) {

                return p2;

            }

            p2 = p2.next;

        }

        return null;

    }

}

 

3方法3:暴力求解

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        if (pHead1 == null || pHead2 == null) {

            return null;

        }

        ListNode firstNode = pHead2;

        for (; pHead1 != null; pHead1 = pHead1.next) {

            if (pHead2 == null) {

                pHead2 = firstNode;

            }

            for (; pHead2 != null; pHead2 = pHead2.next) {

                if (pHead1 == pHead2) {

                    return pHead1;

                }

            }

        }

        return null;

    }

 

 

5、链表实现栈

思路:原生链表,前面加前面移除

package com.jhliu20.real;

import java.util.LinkedList;

public class MyStack {

    private LinkedList linkedList;

    public MyStack() {

        linkedList = new LinkedList();

    }

 

    public void push(Object object) {

        linkedList.addFirst(object);

    }

 

    public Object pop() {

        return linkedList.removeFirst();

    }

 

    public Object peek() {

        return linkedList.getFirst();

    }

 

    public boolean isEmpty() {

        return linkedList.isEmpty();

    }

}

 

6、大数加法

思路:链表+头插法

import java.util.*;

 

 

public class Solution {

    /**

     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

     * 计算两个数之和

     * @param s string字符串 表示第一个整数

     * @param t string字符串 表示第二个整数

     * @return string字符串

     */

    public String solve (String s, String t) {

        //尝试链表+头插法

        LinkedList<Integer> head = new LinkedList<>();

        char[] arrS = s.toCharArray();

        char[] arrT = t.toCharArray();

        int lenS = arrS.length;

        int lenT = arrT.length;

        int leave = 0;

        while(lenS-- > 0 && lenT-- > 0) {

            //char如何转为真正的int-'0'

            int num = leave + Integer.valueOf(arrS[lenS] - '0') + Integer.valueOf(arrT[lenT] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        while(lenS-- > 0) {

            int num = leave + Integer.valueOf(arrS[lenS] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        while(lenT-- > 0) {

            int num = leave + Integer.valueOf(arrT[lenT] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        if (leave > 0) {

            head.addFirst(leave);

        }

        StringBuilder sb = new StringBuilder();

        Iterator iter = head.iterator();

        while(iter.hasNext()) {

            sb.append(iter.next());

        }

        return new String(sb);

    }

}

 

7、合并k个升序链表

思路:使用升序优先队列,依次进入,循环出队首

import java.util.*;

 

 

public class Solution {

    /**

     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

     * 计算两个数之和

     * @param s string字符串 表示第一个整数

     * @param t string字符串 表示第二个整数

     * @return string字符串

     */

    public String solve (String s, String t) {

        //尝试链表+头插法

        LinkedList<Integer> head = new LinkedList<>();

        char[] arrS = s.toCharArray();

        char[] arrT = t.toCharArray();

        int lenS = arrS.length;

        int lenT = arrT.length;

        int leave = 0;

        while(lenS-- > 0 && lenT-- > 0) {

            //char如何转为真正的int-'0'

            int num = leave + Integer.valueOf(arrS[lenS] - '0') + Integer.valueOf(arrT[lenT] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        while(lenS-- > 0) {

            int num = leave + Integer.valueOf(arrS[lenS] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        while(lenT-- > 0) {

            int num = leave + Integer.valueOf(arrT[lenT] - '0');

            head.addFirst(num % 10);

            leave = num / 10;

        }

        if (leave > 0) {

            head.addFirst(leave);

        }

        StringBuilder sb = new StringBuilder();

        Iterator iter = head.iterator();

        while(iter.hasNext()) {

            sb.append(iter.next());

        }

        return new String(sb);

    }

}

 

8、删除排序链表的重复元素

思路:nextnext.next比较

/**

 * 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 deleteDuplicates(ListNode head) {

        if(head == null) {

            return head;

        }

        ListNode dummy = new ListNode(0, head);

        ListNode cur = dummy;

        while(cur.next != null && cur.next.next != null) {

            if(cur.next.val == cur.next.next.val) {

                int value = cur.next.val;

                while(cur.next != null && cur.next.val == value) {

                    cur.next = cur.next.next;

                }

            } else {

                cur = cur.next;

            }

        }

        return dummy.next;

    }

}

 

9、环形链表的入口节点

思路:慢指针在相遇点,快指针从头开始找相遇节点

/*

 public class ListNode {

    int val;

    ListNode next = null;

 

    ListNode(int val) {

        this.val = val;

    }

}

*/

public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {

        ListNode fast = pHead, slow = pHead;

        while(fast != null && fast.next != null) {

            slow = slow.next;

            fast = fast.next.next;

            if(fast == slow) {

                fast = pHead;

                //在快慢指针相遇点开始,往后找,直到相遇,即为入口节点

                while(fast != null && slow != null) {

                    if(fast == slow) {

                        return slow;

                    }

                    fast = fast.next;

                    slow = slow.next;

                }

            }

        }

        return null;

    }

}

 

10、链表排序-归并

思路:递归获取中点作为右指针,调用辅助函数排序两个子链表

/**

 * 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 sortList(ListNode head) {

        return head == null ? null : mergeSort(head);

    }

    public ListNode mergeSort(ListNode head) {

        if(head.next == null) {

            return head;

        }

        ListNode q = head, p = head, pre = null;

        while(q != null && q.next != null) {

            pre = p;

            p = p.next;

            q= q.next.next;

        }

        pre.next = null;

        ListNode l = mergeSort(head);

        ListNode r = mergeSort(p);

        return merge(l, r);

    }

    public ListNode merge(ListNode l, ListNode r) {

        ListNode dummy = new ListNode(0);

        ListNode cur = dummy;

        while(l != null && r != null) {

            if(l.val <= r.val) {

                cur.next = l;

                cur = cur.next;

                l = l.next;

            } else {

                cur.next = r;

                cur = cur.next;

                r = r.next;

            }

        }

        if(l != null) {

            cur.next = l;

        }

        if(r != null) {

            cur.next = r;

        }

        return dummy.next;

    }

}

 

11、反转链表某区间元素

思路:先找到左区间,再进行反转

/**

 * 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 reverseBetween(ListNode head, int left, int right) {

        ListNode dummy = new ListNode(-1, head);

        ListNode pre = dummy;

        for(int i = 1; i < left; i++) {

            pre = pre.next;

        }

        ListNode cur = pre.next;

        ListNode next;

        for(int i = left; i < right; i++) {

            next = cur.next;

            cur.next = next.next;

            next.next = pre.next; //cur为什么不行

            pre.next = next; //重点

        }

        return dummy.next;

    }

}

 

12、获取倒数第k个节点

思路:快慢指针,先走k

/**

 * Definition for singly-linked list.

 * public class ListNode {

 *     int val;

 *     ListNode next;

 *     ListNode(int x) { val = x; }

 * }

 */

class Solution {

    public ListNode getKthFromEnd(ListNode head, int k) {

        ListNode fast = head, slow = head;

        while(fast != null && k-- > 0) {

            fast = fast.next;

        }

        while(fast != null) {

            fast = fast.next;

            slow = slow.next;

        }

        return slow;

    }

}

 

13、删除倒数第n个节点

思路:记住pre节点&快慢指针获取倒数第k个节点

import java.util.*;

/*

 * public class ListNode {

 *   int val;

 *   ListNode next = null;

 * }

 */

public class Solution {

    /**

     *

     * @param head ListNode类

     * @param n int整型

     * @return ListNode类(删除节点而非返回被删除的节点)

     */

    public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode fast = head, slow = head;

        //注意第n个是n-->0!

        while(n-- > 0) {

            fast = fast.next;

        }

        //第n个就是最后一个节点,那么意思就是删除第一个几点

        if(fast == null) {

            return head.next;

        }

        //获取前一个节点,便于删除

        fast = fast.next;

        while(fast != null) {

            fast = fast.next;

            slow = slow.next;

        }

        slow.next = slow.next.next;

        return head;

    }

}

 

14、反转链表

思路:pre/cur/next指针

/*

public class ListNode {

    int val;

    ListNode next = null;

 

    ListNode(int val) {

        this.val = val;

    }

}*/

public class Solution {

    public ListNode ReverseList(ListNode head) {

        if(head == null) {

            return null;

        }

        ListNode cur = head;

        ListNode pre = null;

        ListNode next;

        while(cur != null) {

            next = cur.next;

            cur.next = pre;

            pre = cur;

            cur = next;

        }

        return pre;

    }

}

 

15k个一组翻转

思路:预计算有几个k

import java.util.*;

public class Solution {

    public ListNode reverseKGroup (ListNode head, int k) {

        // 先计算反转几次

        int len = getLength(head) / k;

        ListNode dummy = new ListNode(-1);

        dummy.next = head;

        ListNode cur = dummy.next;

        ListNode pre = dummy;

        for(int i = 0; i < len; i++) {

            //每k个元素翻转一次

            //k个元素,需要执行k-1次

            for(int j = 0; j < k - 1; j++) {

                //尾插法,每轮循环将cur的next尾插到pre后面

                ListNode next = cur.next;

                cur.next = next.next;

                next.next = pre.next;

                pre.next = next;

            }

            //每k个元素,就把pre挪位置

            pre = cur;

            cur = pre.next;

        }

        return dummy.next;

    }

    public int getLength(ListNode head) {

        int len = 0;

        while(head != null) {

            len++;

            head = head.next;

        }

        return len;

    }

}

 

16、重排链表

思路:1-末尾-2-倒数第二,使用ArrayList

/**

 * 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; }

 * }

 */

 

//方法1:放入ArrayList

class Solution {

    public void reorderList(ListNode head) {

        if(head == null) {

            return;

        }

        List<ListNode> list = new ArrayList<ListNode>();

        ListNode node = head;

        while(node != null) {

            list.add(node);

            node = node.next;

        }

        int i = 0, j = list.size() - 1;

        while(i < j) {

            list.get(i).next = list.get(j);

            i++;

            if(i == j) {

                break;

            }

            list.get(j).next = list.get(i);

            j--;

        }

        list.get(i).next = null;

    }

}

 

17、两数相加

思路:判空&遍历+carry

/**

 * 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 addTwoNumbers(ListNode l1, ListNode l2) {

        ListNode root = new ListNode(0);

        ListNode cur = root;

        int carry = 0;

        while(l1 != null || l2 != null || carry != 0) {

            int l1_v = l1 == null ? 0 : l1.val;

            int l2_v = l2 == null ? 0 : l2.val;

            int sum = l1_v + l2_v + carry;

            carry = sum / 10;

 

            ListNode node = new ListNode(sum % 10);

            cur.next = node;

            cur = node;

 

            if(l1 != null) l1 = l1.next;

            if(l2 != null) l2 = l2.next;

        }

        return root.next;

    }

}

 

18、移除指定值的元素

思路:利用precur指针

/**

 * 添加虚节点方式

 * 时间复杂度 O(n)

 * 空间复杂度 O(1)

 * @param head

 * @param val

 * @return

 */

public ListNode removeElements(ListNode head, int val) {

    if (head == null) {

        return head;

    }

    // 因为删除可能涉及到头节点,所以设置dummy节点,统一操作

    ListNode dummy = new ListNode(-1, head);

    ListNode pre = dummy;

    ListNode cur = head;

    while (cur != null) {

        if (cur.val == val) {

            pre.next = cur.next;

        } else {

            pre = cur;

        }

        cur = cur.next;

    }

    return dummy.next;

}

/**

 * 不添加虚拟节点方式

 * 时间复杂度 O(n)

 * 空间复杂度 O(1)

 * @param head

 * @param val

 * @return

 */

public ListNode removeElements(ListNode head, int val) {

    while (head != null && head.val == val) {

        head = head.next;

    }

    // 已经为null,提前退出

    if (head == null) {

        return head;

    }

    // 已确定当前head.val != val

    ListNode pre = head;

    ListNode cur = head.next;

    while (cur != null) {

        if (cur.val == val) {

            pre.next = cur.next;

        } else {

            pre = cur;

        }

        cur = cur.next;

    }

    return head;

}

 

19、两两交换节点(k个翻转)

方法1:递归

// 递归版本

class Solution {

    public ListNode swapPairs(ListNode head) {

        // base case 退出提交

        if(head == null || head.next == null) return head;

        // 获取当前节点的下一个节点

        ListNode next = head.next;

        // 进行递归

        ListNode newNode = swapPairs(next.next);

        // 这里进行交换

        next.next = head;

        head.next = newNode;

        return next;

    }

}

 

方法2:使用虚拟头结点

// 虚拟头结点

class Solution {

  public ListNode swapPairs(ListNode head) {

 

    ListNode dummyNode = new ListNode(0);

    dummyNode.next = head;

    ListNode prev = dummyNode;

 

    while (prev.next != null && prev.next.next != null) {

      ListNode temp = head.next.next; // 缓存 next

      prev.next = head.next;          // 将 prev 的 next 改为 head 的 next

      head.next.next = head;          // 将 head.next(prev.next) 的next,指向 head

      head.next = temp;               // 将head 的 next 接上缓存的temp

      prev = head;                    // 步进1位

      head = head.next;               // 步进1位

    }

    return dummyNode.next;

  }

}

posted @   哥们要飞  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示