【算法题-分类型总结】链表相关的算法题
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)方法2:set存
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、删除排序链表的重复元素
思路:next和next.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; } } |
15、k个一组翻转
思路:预计算有几个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、移除指定值的元素
思路:利用pre和cur指针
/** * 添加虚节点方式 * 时间复杂度 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; } } |
本文来自博客园,作者:哥们要飞,转载请注明原文链接:https://www.cnblogs.com/liujinhui/p/15889787.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix