单向链表
public class Linked<E> {
private Node<E> first;
private Node<E> last;
public void addFirst(E item){
//新节点
final Node<E> newNode = new Node<E>(item);
//保存原头节点
final Node<E> f = first;
//新节点为头节点
first = newNode;
if (f == null){
last = newNode;
}else {
//新节点指向原头节点
newNode.next = f;
}
}
public void addLast(E item){
final Node<E> newNode = new Node<E>(item);
final Node<E> l = last;
last = newNode;
if (l == null){
first = newNode;
}else {
l.next = newNode;
}
}
public String toString(){
StringJoiner stringJoiner = new StringJoiner("->");
for (Node<E> n = first;n != null;n = n.next){
stringJoiner.add(n.item.toString());
}
return stringJoiner.toString();
}
static class Node<E>{
E item;
Node<E> next;
public Node(E data){
this.item = data;
}
}
}
双向链表
public class Linked2<E> {
private int size;
private Node<E> first;
private Node<E> last;
//头插法
public void addFirst(E item){
final Node<E> f = first;
final Node<E> newNode = new Node<E>(null,item,f);
first = newNode;
if (f == null){
last = newNode;
}else {
f.prev = newNode;
}
size ++;
}
//尾插法
public void addLast(E item){
final Node<E> l = last;
Node<E> newNode = new Node<E>(l,item,null);
last = newNode;
if (last == null){
first = newNode;
}else {
l.next = newNode;
}
size ++;
}
public void removeFirst(){
final Node<E> f = first;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null){
last = null;
}else{
next.prev = null;
}
size --;
}
public void removeLast(){
final Node<E> l = last;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null;
last = prev;
if (prev == null){
first = null;
}else {
prev.next = null;
}
size --;
}
public String toStringFromFirst(){
StringJoiner stringJoiner = new StringJoiner("->");
for (Node<E> n = first; n != null; n = n.next){
stringJoiner.add(n.item.toString());
}
return stringJoiner.toString();
}
public String toStringFromLast(){
StringJoiner stringJoiner = new StringJoiner("<-");
for (Node<E> n = last;n != null;n = n.prev){
stringJoiner.add(n.item.toString());
}
return stringJoiner.toString();
}
static class Node<E>{
private Node<E> prev;
private E item;
private Node<E> next;
public Node(Node<E> prev,E element,Node<E> next){
this.prev = prev;
this.item = element;
this.next = next;
}
}
}
用例链表
public class Linked3<E> {
public Node<E> first;
public Node<E> last;
public int size;
public int size(){
for (Node n = first;n != null;n = n.next){
size ++;
}
return size;
}
public void addFirst(E item){
final Node<E> newNode = new Node<E>(item);
final Node<E> f = first;
first = newNode;
if (f == null){
last = newNode;
}else {
newNode.next = f;
}
}
public Node addLast(E item){
final Node<E> newNode = new Node<E>(item);
final Node<E> l = last;
last = newNode;
if (l == null){
first = newNode;
}else {
l.next = newNode;
}
return newNode;
}
public String get(){
StringJoiner stringJoiner = new StringJoiner("->");
for(Node<E> n = first;n != null;n = n.next){
stringJoiner.add(n.item.toString());
}
return stringJoiner.toString();
}
public void addNode(Node node){
for (Node n = node;n != null;n = node.next){
addLast((E) n.item);
}
}
static class Node<E>{
E item;
Node<E> next;
public Node(E item){
this.item = item;
}
}
}
有序链表合并
两链表从头节点开始遍历比较,小的放入结果链表中,后继节点继续比较,大的不动,直至一个链表为空,另一个则一直放,两链表都为空时合并完成。
//有序链表合并,双指针法
public static Linked3 meger(Linked3 l1,Linked3 l2){
Linked3.Node<Integer> p1 = l1.first;
Linked3.Node<Integer> p2 = l2.first;
//结果链表
Linked3<Integer> result = new Linked3<>();
while (p1 != null || p2 != null){
if (p1 == null){
result.addLast(p2.item);
p2 = p2.next;
continue;
}
if (p2 == null){
result.addLast(p1.item);
p1 = p1.next;
continue;
}
if (p1.item < p2.item){
result.addLast(p1.item);
p1 = p1.next;
}else {
result.addLast(p2.item);
p2 = p2.next;
}
}
return result;
}
链表反转
将链表遍历放入栈,再从栈取出存入链表,借助栈先进后出完成反转。
//链表反转,借助栈
public static Linked3 reverse(Linked3 l){
Stack<Linked3.Node> stack = new Stack<>();
for (Linked3.Node<Integer> n = l.first;n != null;n = n.next){
stack.add(n);
}
l = new Linked3();
while (!stack.isEmpty()){
l.addLast(stack.pop().item);
}
return l;
}
链表计算两数和
两链表从头节点开始遍历(两链表都是头插法产生,方便运算,即头部低位,尾部高位),依次相加,算出当前位与进位值,当前位存入结果链表,进位值参与下一位预算。注意最高位的进位。
//遍历头插法的每个node相加,注意进位
public static Linked3 addTwoNumbers(Linked3 l1,Linked3 l2){
Linked3.Node<Integer> n1 = l1.first;
Linked3.Node<Integer> n2 = l2.first;
Linked3<Integer> result = new Linked3<>();
//进位
int carry = 0;
while (n1 != null || n2 != null){
int x = n1 != null ? n1.item : 0;
int y = n2 != null ? n2.item : 0;
int sum = x + y + carry;
carry = sum / 10;
result.addFirst(sum % 10);
if (n1 != null){
n1 = n1.next;
}
if (n2 != null){
n2 = n2.next;
}
//注意最终进位
if (carry != 0){
result.addFirst(carry);
}
}
return result;
}
判断链表是否有环
1. 使用set集合的特性,判断链表中是否有重复 Node ,有则有环,反之无环。
2. 使用快慢指针法。两个指针,快指针一次走两步,慢指针一次走一步,同时从头节点出发,若能指向同一个 Node 证明有环。
//set判断
public static boolean hasCycle1(Linked3.Node node){
HashSet<Linked3.Node> set = new HashSet<>();
while (node != null){
if (set.contains(node)){
return true;
}
set.add(node);
node = node.next;
}
return false;
}
//快慢指针,判断有无环
public static boolean hasCycle2(Linked3.Node node){
if (node == null){
return false;
}
Linked3.Node fast = node;
Linked3.Node slow = node;
while (fast != null && fast.next != null && slow != null){
fast = fast.next.next;
slow = slow.next;
if (fast == slow){
return true;
}
}
return false;
}
判断两链表是否相交
1. 将两个链表依次遍历,如有相同节点则相交。
2. 两链表长度若不相等,相交节点必然在长链表差值之后,则使长链表指针后移差值个节点,使得两个链表剩余长度相同,再同时后移遍历,遇到相同节点跳出循环,证明相交。
//双遍历
public static boolean isIntersect1(Linked3 link1, Linked3 link2) {
for (Linked3.Node n = link1.first;n != null;n = n.next){
for (Linked3.Node m = link2.first;m != null;m = m.next){
if (n == m){
return true;
}
}
}
return false;
}
//双指针
public static boolean isIntersect2(Linked3 link1, Linked3 link2) {
if (link1 ==null || link2==null){
return false;
}
//长链表头节点
Linked3.Node p = link1.size() > link2.size()? link1.first:link2.first;
//短链表头节点
Linked3.Node q = link1.size() > link2.size()? link2.first:link1.first;
//长度差
int diff = Math.abs(link1.size() - link2.size());
//长链表后移diff个节点,使长短链表长度一致
while(diff-- > 0){
p = p.next;
}
//判断是否相等(相交)
while (p != q){
p = p.next;
q = q.next;
}
//跳出循环有相交
if (q != null){
return true;
}else {
return false;
}
}
链表实现LRU缓存
哈希表 + 双向链表(有固定头尾节点的)实现
哈希表的 value 与链表的节点形成映射关系
哈希表进行存取操作,对应的链表节点移动至首部(最近使用)
超出缓存容量删除链表尾部(最近最少使用)
public class LRUCache {
//哈希表
private Map<Integer,Node> cache = new HashMap<>();
//链表长度
private int size;
//缓存容量
private int capacity;
//伪头节点
private Node first;
//伪尾节点
private Node last;
public LRUCache(int capacity) {
size = 0;
this.capacity = capacity;
first = new Node();
last = new Node();
first.next = last;
last.prev = first;
}
private void addFirst(Node newNode){
newNode.prev = first;
newNode.next = first.next;
first.next.prev = newNode;
first.next = newNode;
}
private void removeNode(Node node){
node.next.prev = node.prev;
node.prev.next = node.next;
}
private void moveToFirst(Node node){
removeNode(node);
addFirst(node);
}
private Node removeLast(){
Node node = last.prev;
removeNode(node);
return node;
}
public void put(int key, int value) {
Node node = cache.get(key);
if (node == null){
Node newNode = new Node(key,value);
//移至首部
addFirst(newNode);
//map中添加新元素与映射关系
cache.put(key, newNode);
//判断容量
if (++size > capacity){
//超出容量,链表中删除最近最少使用
Node lastNode = removeLast();
//map中删除对应元素
cache.remove(lastNode.key);
size--;
}
}else {
node.value = value;
moveToFirst(node);
}
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
//不存在返回-1
return -1;
}
//被使用移至首部
moveToFirst(node);
return node.value;
}
public String toString(){
StringJoiner ret = new StringJoiner(",");
for (Map.Entry<Integer,Node> entry : cache.entrySet()){
ret.add(String.format("[%d:%d]",entry.getKey(),entry.getValue().value));
}
return ret.toString();
}
static class Node{
private Node prev;
private Node next;
public int key;
public int value;
public Node(){
}
public Node(int key,int value){
this.key = key;
this.value = value;
}
}
}