单链表结构
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; // 获取下一个node
curNode.next = pre; //将当前node的下一个指向前一个
pre = curNode; //更新 前一个Node
curNode = next; // 更新 当前Node
}
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
Node right_first_node = s.next;
// Node right_node = right_first_node;
// 将中间的下一个 next置为null
s.next = null;
// 将右边的逆序
Node right_pre = null;
Node right_next = null;
while (right_first_node != null) {
right_next = right_first_node.next; // 获取下一个node
right_first_node.next = right_pre; // 当前node 的下一个指向前一个node
right_pre = right_first_node; // 更新 pre
right_first_node = right_next; // 更新 当前node 为下一个
}
// 保存右边 最后一个node,就是right_pre,
Node right_last_node = right_pre;
// 左边第一个 node
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;
}
// 更新左右 node 为下一个
left_first_node = left_first_node.next;
right_last_node = right_last_node.next;
}
// 恢复 链表,
// 中间node 指向右边第一个,
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; // 把当前node 搞成独立的,不然都连起来了。
if (head.value < pivot) {
// 当前node 小于 pivot
if (sh == null) {
// 如果 当前 sh 为null
sh = head;
st = head;
} else {
st.next = head; // 指向下一个
st = head;
}
} else if (head.value == pivot) {
// 当前 node 等于 pivot
if (eh == null) {
eh = head;
et = head;
} else {
et.next = head;
et = head;
}
} else {
// 当前 node 大于 pivot
if (bh == null) {
bh = head;
bt = head;
} else {
bt.next = head;
bt = head;
}
}
head = next; // 更新 head
}
// 拼接 st -> eh et -> bh
// 考虑三个区域都可能为null
if (st != null) {
// 如果小于区域 不为 null
st.next = eh;
// 如果等于区域 为null, et就可以 置为 st;
et = et == null ? st : et;
}
if (et != null) {
// 如果等于区域不为null
et.next = bh;
}
// 如果sh 不为null 就返回sh
// 如果sh 为空就返回 eh
// 在继续判断eh
// 如果eh不为空,就返回 eh
// 如果eh 为空, 就返回 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;
// 构造hashmap , key = node , value = 复制的node
while (cur != null) {
map.put(cur, new RandomNode(cur.value));
cur = cur.next;
}
cur = head;
while (cur != null) {
// 给复制的node 链接 next 和 rand
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;
// 构造为 1-> 1' -> 2 -> 2'
while (cur != null) {
cur_copy = new RandomNode(cur.value); // copy当前node
next = cur.next; // 将当前node 的next 保存
cur.next = cur_copy; // 将当前node 的next 指向 copynode
cur_copy.next = next; // copynode的 next指向 当前节点的next
cur = next; // 更新 cur
}
// 开始给 copynode 链接 rand node;
cur = head;
RandomNode curRand = null;
while (cur != null) {
curRand = cur.rand;
// 要判断 curRand 是否为null
cur.next.rand = curRand != null ? curRand.next : null;
// 更新 cur
cur = cur.next.next;
}
// split ,去掉原始元素
RandomNode res = head.next;
cur = head;
while (cur != null) {
next = cur.next.next; // cur.next 一定不是空的哈。不需要担心null
// 把复制的节点的下一个copynode 指向下一个node的 copynode,
cur.next.next = next != null ? next.next : null;// 要判断 next 为空哈。
// 更新cur
cur = next;
}
return res;
}