单链表结构
| |
| public class Node<V> { |
| V value; |
| Node next; |
| } |
双链表
| public class DoubleNode<V> { |
| V value; |
| DoubleNode next; |
| DoubleNode last; |
| } |
反转单向链表
| public static Node reverseList(Node curNode) { |
| Node pre = null; |
| Node next = null; |
| while (curNode != null) { |
| next = curNode.next; |
| curNode.next = pre; |
| pre = curNode; |
| curNode = next; |
| } |
| return pre; |
| } |
反转双向链表
| public static DoubleNode reverseNode(DoubleNode head) { |
| DoubleNode curNode = head; |
| DoubleNode pre = null; |
| DoubleNode next = null; |
| while (curNode.next != null) { |
| next = curNode.next; |
| curNode.next = pre; |
| curNode.pre = next; |
| pre = curNode; |
| curNode = next; |
| |
| } |
| return pre; |
| } |
打印俩个 有序链表 的公共部分
俩个指针,谁小谁移动,相等打印,然后都移动。 一个到头停止

| public static void printCommonPart(Node node1, Node node2) { |
| while (node1 != null && node2 != null) { |
| if (node1.value < node2.value) { |
| |
| node1 = node1.next; |
| } else if (node1.value > node2.value) { |
| node2 = node2.next; |
| } else { |
| |
| System.out.print(node1.value + " "); |
| |
| node1 = node1.next; |
| node2 = node2.next; |
| } |
| } |
| } |
判断一个链表是否为回文结构
对称轴是虚的, 或者不是虚的

- 使用栈,全部 先入栈,然后比较
- 只放右边的数据。省一半空间,快慢指针,快指针走俩步,慢指针走一步,快指针走到结尾,慢指针就到中间的部分 了
考虑奇数 偶数

| public static boolean isPalindromeList_01(Node head) { |
| |
| Stack<Node> stack = new Stack<>(); |
| |
| Node curNode = head; |
| while (curNode != null) { |
| stack.push(curNode); |
| curNode = curNode.next; |
| } |
| |
| while (head != null) { |
| Node stackNode = stack.pop(); |
| if (stackNode.value != head.value) { |
| return false; |
| } |
| head = head.next; |
| } |
| |
| return true; |
| } |
只添加右半部分,使用快慢指针
| public static boolean isPalindromeList_02(Node head) { |
| |
| Stack<Node> stack = new Stack<>(); |
| |
| Node f = head; |
| Node s = head; |
| |
| while (f != null) { |
| f = f.next; |
| if (f != null) { |
| f = f.next; |
| } else { |
| break; |
| } |
| s = s.next; |
| |
| } |
| System.out.println("===s-> " + s.value); |
| while (s != null) { |
| stack.push(s); |
| s = s.next; |
| } |
| |
| while (!stack.empty()) { |
| Node stackNode = stack.pop(); |
| if (head.value != stackNode.value) { |
| return false; |
| } |
| head = head.next; |
| } |
| return true; |
| } |
快慢指针判断优化:
注意:判断是否为空,使用快指针。
| while (f.next != null && f.next.next != null) { |
| s = s.next; |
| f = f.next.next; |
| } |
- 如果要求 空间复杂度为O(1)
通过修改链表,也是快慢指针,但是不用栈了,当慢指针到了中间位置,后续的node 逆序,然后 向中间走,依次比较。

| public static boolean isPalindromeList_03(Node head) { |
| if (head == null || head.next == null) { |
| return true; |
| } |
| |
| Node s = head; |
| Node f = head; |
| while (f.next != null && f.next.next != null) { |
| s = s.next; |
| f = f.next.next; |
| } |
| |
| Node right_first_node = s.next; |
| |
| |
| s.next = null; |
| |
| |
| Node right_pre = null; |
| Node right_next = null; |
| while (right_first_node != null) { |
| right_next = right_first_node.next; |
| right_first_node.next = right_pre; |
| right_pre = right_first_node; |
| right_first_node = right_next; |
| } |
| |
| |
| Node right_last_node = right_pre; |
| |
| Node left_first_node = head; |
| |
| boolean res = true; |
| |
| |
| while (left_first_node != null && right_last_node != null) { |
| if (left_first_node.value != right_last_node.value) { |
| |
| res = false; |
| break; |
| } |
| |
| left_first_node = left_first_node.next; |
| right_last_node = right_last_node.next; |
| } |
| |
| |
| |
| right_first_node = s.next; |
| |
| |
| |
| |
| Node right_recover_pre = null; |
| Node right_recover_next = null; |
| while (right_last_node != null) { |
| right_recover_next = right_last_node.next; |
| right_last_node.next = right_recover_pre; |
| right_recover_pre = right_last_node; |
| right_last_node = right_recover_next; |
| |
| } |
| return res; |
| } |
将单向链表按某值划分成左边小、中间相等、右边大的形式
要求:相对顺序不变。时间复杂度O(N) , 空间复杂度O(1)

借助6个变量
SH: 小于某值的 头 ST: 小于某值的 尾
EH: 等于某值的 头 ET: 等于某值的 尾
BH: 大于某值的 头 BT: 大于某值的 尾

某个区域可能没有,注意空指针。
| public static Node smallEqualBigger(Node head, int pivot) { |
| |
| Node sh = null; |
| Node st = null; |
| |
| Node eh = null; |
| Node et = null; |
| |
| Node bh = null; |
| Node bt = null; |
| |
| Node next = null; |
| |
| while (head != null) { |
| next = head.next; |
| head.next = null; |
| if (head.value < pivot) { |
| |
| if (sh == null) { |
| |
| sh = head; |
| st = head; |
| } else { |
| st.next = head; |
| st = head; |
| |
| } |
| |
| } else if (head.value == pivot) { |
| |
| if (eh == null) { |
| eh = head; |
| et = head; |
| } else { |
| et.next = head; |
| et = head; |
| } |
| |
| } else { |
| |
| if (bh == null) { |
| bh = head; |
| bt = head; |
| } else { |
| bt.next = head; |
| bt = head; |
| } |
| } |
| |
| head = next; |
| |
| } |
| |
| |
| if (st != null) { |
| |
| st.next = eh; |
| |
| et = et == null ? st : et; |
| |
| } |
| |
| if (et != null) { |
| |
| et.next = bh; |
| } |
| |
| |
| |
| |
| |
| return sh != null ? sh : eh != null ? eh : bh; |
| |
| } |
复制还有随机指针节点的链表
Node 还有一个属性就是 多了一个node,可能指向任何 链表内的节点,包括自己。
- 用额外空间。使用hashmap key=老node value = 克隆的node.然后依次遍历找到关系
RandomNode
| public static class RandomNode { |
| public int value; |
| public RandomNode next; |
| public RandomNode rand; |
| |
| public RandomNode(int value) { |
| this.value = value; |
| } |
| } |
| public static RandomNode copyListWithRandom_01(RandomNode head) { |
| HashMap<RandomNode, RandomNode> map = new HashMap<>(); |
| |
| RandomNode cur = head; |
| |
| |
| while (cur != null) { |
| map.put(cur, new RandomNode(cur.value)); |
| cur = cur.next; |
| } |
| |
| cur = head; |
| while (cur != null) { |
| |
| map.get(cur).next = map.get(cur.next); |
| map.get(cur).rand = map.get(cur.rand); |
| cur = cur.next; |
| } |
| return map.get(head); |
| |
| } |
- 不使用额外空间 构造如下图,一对一对遍历。 random就是 next.next

| public static RandomNode copyListWithRandom_02(RandomNode head) { |
| RandomNode cur = head; |
| RandomNode next = null; |
| RandomNode cur_copy = null; |
| |
| while (cur != null) { |
| cur_copy = new RandomNode(cur.value); |
| next = cur.next; |
| cur.next = cur_copy; |
| cur_copy.next = next; |
| cur = next; |
| } |
| |
| |
| cur = head; |
| RandomNode curRand = null; |
| while (cur != null) { |
| curRand = cur.rand; |
| |
| cur.next.rand = curRand != null ? curRand.next : null; |
| |
| cur = cur.next.next; |
| |
| } |
| |
| |
| RandomNode res = head.next; |
| cur = head; |
| |
| while (cur != null) { |
| next = cur.next.next; |
| |
| cur.next.next = next != null ? next.next : null; |
| |
| cur = next; |
| } |
| |
| return res; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统