常见的链表:单链表,双链表,循环链表。
使用链表实现LRU缓存淘汰策略代码:代码如下,写了3个小时,而且也不知道正不正确,使用了一个节点类和一个管理节点的链表类。
package com.freshbin.dataStructAndAlgo.chapter06.Link.LRU;
/**
* @author freshbin
* @date 2020/4/11 21:59
*/
public class MyNode {
public String token;
public MyNode next;
public MyNode(String token) {
this.token = token;
}
public String getToken() {
return this.token;
}
public void setToken(String token) {
this.token = token;
}
public MyNode getNext() {
return this.next;
}
public void setNext(MyNode next) {
this.next = next;
}
@Override
public String toString() {
return "MyNode{" +
"token='" + token + '\'' +
", next=" + next +
'}';
}
}
package com.freshbin.dataStructAndAlgo.chapter06.Link.LRU;
/**
* 使用链表实现LRU缓存淘汰策略
* @author freshbin
* @date 2020/4/11 21:24
*/
public class MyLinkLRU {
private volatile static MyLinkLRU myLinkLRU = null;
private Integer maxSize = 3;
private Integer currentCount = 0;
public MyNode firstNode;
private MyLinkLRU() {
}
public static synchronized MyLinkLRU getMyLinkLRU() {
if(myLinkLRU == null) {
synchronized (MyLinkLRU.class) {
if(myLinkLRU == null) {
myLinkLRU = new MyLinkLRU();
}
}
}
return myLinkLRU;
}
/**
* 0、判断缓存是否为空,如果为空,则直接插入头结点
* 1、先查询该值是否在缓存中,如果存在,则将其从原来位置删除,并插入到头结点中
* 2、如果该值不存在缓存中,则判断缓存是否满了,如果没满,那么就直接插入头结点,否则就删除尾结点,再插入头结点
* @param token
* @return
*/
public MyNode useCacheByToken(String token) {
MyNode cacheNode = new MyNode(token);
// 0
if(this.firstNode == null) {
insertToFirstNode(cacheNode);
return cacheNode;
}
// 1
MyNode oldNode = this.firstNode;
while (oldNode != null) {
if(oldNode.token.equals(token)) {
removeNodeByToken(token);
insertToFirstNode(cacheNode);
return firstNode;
}
oldNode = oldNode.next;
}
// 2
if(currentCount < maxSize) {
currentCount++;
MyNode oldFirstNode = this.firstNode;
this.firstNode = cacheNode;
this.firstNode.next = oldFirstNode;
} else {
removeTailNode();
insertToFirstNode(cacheNode);
}
return firstNode;
}
private void insertToFirstNode(MyNode newNode) {
// if(newNode == null || newNode.token == this.firstNode.token) {
// // 如果需要新插入的节点是头结点,那么不用再插入
// return;
// }
currentCount++;
MyNode node = firstNode;
this.firstNode = newNode;
this.firstNode.next = node;
}
public Boolean removeNodeByToken(String token) {
if(this.firstNode == null) {
return true;
}
if(currentCount > 0) {
currentCount--;
}
if(this.firstNode.token == token) {
// 删除头结点
this.firstNode = this.firstNode.next;
return true;
}
MyNode node = this.firstNode;
while (node != null) {
if(node.next != null && node.next.token == token) {
node.next = node.next.next;
return true;
}
node = node.next;
}
return false;
}
public Boolean removeTailNode() {
if(this.firstNode == null) {
return false;
}
MyNode preNode = firstNode;
MyNode currentNode = firstNode;
while (currentNode.next != null) {
preNode = currentNode;
currentNode = currentNode.next;
}
preNode.next = null;
currentCount--;
return true;
}
/**
* 每次都从头结点插入
* @param value
* @return
*/
public MyNode addLink(String value) {
MyNode myNode = new MyNode(value);
MyNode oldFirstNode = this.firstNode;
this.firstNode = myNode;
this.firstNode.next = oldFirstNode;
return this.firstNode;
}
public MyNode getFirstNode() {
return this.firstNode;
}
public void setFirstNode(MyNode firstNode) {
this.firstNode = firstNode;
}
public Integer getCurrentCount() {
return this.currentCount;
}
public void setCurrentCount(Integer currentCount) {
this.currentCount = currentCount;
}
}
package com.freshbin.dataStructAndAlgo.chapter06.Link.LRU;
/**
* @author freshbin
* @date 2020/4/11 21:36
*/
public class MyLinkLRUMain {
public static void main(String[] arg) {
String token01 = "token01";
String token02 = "token02";
String token03 = "token03";
String token04 = "token04";
MyLinkLRU myLinkLRU = MyLinkLRU.getMyLinkLRU();
myLinkLRU.useCacheByToken(token01);
myLinkLRU.useCacheByToken(token01);
MyNode node = myLinkLRU.getFirstNode();
System.out.print("缓存中的值:");
while (node != null) {
System.out.print(node.token + "->");
node = node.next;
}
System.out.println();
myLinkLRU.useCacheByToken(token02);
myLinkLRU.useCacheByToken(token03);
myLinkLRU.useCacheByToken(token04);
myLinkLRU.useCacheByToken(token02);
MyNode node02 = myLinkLRU.getFirstNode();
System.out.print("缓存中的值:");
while (node02 != null) {
System.out.print(node02.token + "->");
node02 = node02.next;
}
}
}
运行结果如下:
使用数组实现LRU缓存淘汰策略代码:写了一个多小时,看起来像是正确的,不过也是简单的代码,代码如下
package com.freshbin.dataStructAndAlgo.chapter06.Array.LRU;
/**
* @author freshbin
* @date 2020/4/12 9:23
* 数组实现LRU缓存淘汰策略
*/
public class MyArrayLRU {
private volatile static MyArrayLRU myArrayLRU = null;
private Integer maxSize = 3;
private String[] cacheArr = new String[3];
private Integer currentCount = 0;
private MyArrayLRU() { }
public static synchronized MyArrayLRU getMyArrayLRU() {
if(myArrayLRU == null) {
synchronized (MyArrayLRU.class) {
if(myArrayLRU == null) {
myArrayLRU = new MyArrayLRU();
}
}
}
return myArrayLRU;
}
/**
* 0、判断缓存是否为空,如果为空,则直接插入到索引为0的位置
* 1、先查询该值是否在缓存中,如果存在,则遍历原缓存数组从索引0开始到该值存在的位置,把数组的每一项都后移一位,并把该值写入索引为0的位置
* 2、如果该值不存在缓存中,则判断缓存是否满了,
* 如果没满,则遍历原缓存数组,把数组的每一项都后移一位,并把该值写入索引为0的位置,
* 如果满了,那么就把数组前size-1项往后移一位(size为数组大小),再把该值写入索引为0的位置。
* @param token
* @return
*/
public String useCache(String token) {
if(cacheArr[0] == null) {
cacheArr[0] = token;
currentCount++;
return token;
}
int index = findIndexInCacheArray(token);
if(index == 0) {
// 如果该值已经是第0个索引,那么也不用继续放到最前面。
return token;
}
if(index != -1) {
forwardArrayOneStep(0, index);
cacheArr[0] = token;
return token;
}
// 因为该值不在缓存中,所以缓存数量+1
if(currentCount < maxSize) {
currentCount++;
}
// 该值不在缓存中的情况
if(currentCount < maxSize) {
// 缓存没满
forwardArrayOneStep(0, currentCount-1);
cacheArr[0] = token;
} else {
// 缓存满了
forwardArrayOneStep(0, maxSize-1);
cacheArr[0] = token;
}
return token;
}
private void forwardArrayOneStep(int start, int end) {
if(start >= end || end <= 0) {
return;
}
for(; end > start; end--) {
// 这里需要从后往前遍历,否则前面的会把后面的值覆盖了
// 把前面的值放到后一位
cacheArr[end] = cacheArr[end-1];
}
}
/**
* 返回-1表示没找到,否则返回索引值
* @param token
* @return
*/
private int findIndexInCacheArray(String token) {
for(int i = 0; i < cacheArr.length; i++) {
if(cacheArr[i] == token) {
return i;
}
}
return -1;
}
public String[] getCacheArr() {
return this.cacheArr;
}
}
package com.freshbin.dataStructAndAlgo.chapter06.Array.LRU;
/**
* @author freshbin
* @date 2020/4/12 10:10
*/
public class MyArrayLRUMain {
public static void main(String[] arg) {
String token01 = "token01";
String token02 = "token02";
String token03 = "token03";
String token04 = "token04";
MyArrayLRU myArrayLRU = MyArrayLRU.getMyArrayLRU();
myArrayLRU.useCache(token01);
myArrayLRU.useCache(token01);
String[] cacheArr = myArrayLRU.getCacheArr();
System.out.print("缓存值:");
for(int i = 0; i < cacheArr.length; i++) {
if(cacheArr[i] != null) {
System.out.print(cacheArr[i] + " ");
}
}
System.out.println();
System.out.print("缓存值:");
myArrayLRU.useCache(token02);
myArrayLRU.useCache(token03);
myArrayLRU.useCache(token04);
myArrayLRU.useCache(token02);
for(int i = 0; i < cacheArr.length; i++) {
if(cacheArr[i] != null) {
System.out.print(cacheArr[i] + " ");
}
}
}
}
运行结果:
使用单链表判断一个字符串是否是回文字符串:使用了两条链表来保存字符串。总共三个类,一个节点类(复用上面的),一个管理节点的链表类,一个测试类,代码如下:
package com.freshbin.dataStructAndAlgo.chapter06.Link.palindromicString;
import com.freshbin.dataStructAndAlgo.chapter06.Link.LRU.MyNode;
/**
* 使用链表实现LRU缓存淘汰策略
* @author freshbin
* @date 2020/4/11 21:24
*/
public class MyLink {
public MyNode firstNode;
public MyLink() {
}
/**
* 每次都从头结点插入
* @param value
* @return
*/
public MyNode addLink(String value) {
MyNode myNode = new MyNode(value);
MyNode oldFirstNode = this.firstNode;
this.firstNode = myNode;
this.firstNode.next = oldFirstNode;
return this.firstNode;
}
public MyNode getFirstNode() {
return this.firstNode;
}
public void setFirstNode(MyNode firstNode) {
this.firstNode = firstNode;
}
}
package com.freshbin.dataStructAndAlgo.chapter06.Link.palindromicString;
import com.freshbin.dataStructAndAlgo.chapter06.Link.LRU.MyNode;
/**
* 使用数组实现回文字符串校验
*
* 思路:
* 1、首先将字符串按顺序写进链表1,再把字符串按逆序即从length-1到0的顺序写进链表2
* 2、遍历链表1,从头结点遍历到中节点。
* 使用currentIndex从0开始记录当前遍历的索引,使用middleIndex记录中节点的索引
* 遍历方法内比较链表1和链表2的值是否一致,不一致则退出循环,表示不是回文数,否则是回文数。
* 遍历一次currentIndex就+1,middeleIndex的值为(0+length)/2,length为字符串长度
*
* @author freshbin
* @date 2020/4/12 11:28
*/
public class MyLinkPalindromicString {
public static Boolean isPalindromicString(String target) {
int currentIndex = 0;
int middeleIndex = (target.length()) / 2;
MyLink myNode01 = initMyLink01(target);
MyLink myNode02 = initMyLink02(target);
displayNode(myNode01);
displayNode(myNode02);
MyNode firstNode01 = myNode01.getFirstNode();
MyNode firstNode02 = myNode02.getFirstNode();
while(currentIndex < middeleIndex && firstNode01 != null) {
if(!firstNode01.token.equals(firstNode02.token)) {
return false;
}
firstNode01 = firstNode01.next;
firstNode02 = firstNode02.next;
currentIndex++;
}
return true;
}
private static void displayNode(MyLink myNode) {
System.out.print("链表内容:");
MyNode firstNode = myNode.getFirstNode();
while(firstNode != null) {
System.out.print(firstNode.token + "->");
firstNode = firstNode.next;
}
System.out.println();
}
/**
* 字符串逆序写入链表
* @param target
* @return
*/
private static MyLink initMyLink02(String target) {
MyLink myLink = new MyLink();
// 因为该链表每次都是从头结点插入,所以这里字符串从后到前进行插入
for(int i = 0; i < target.length(); i++) {
myLink.addLink(String.valueOf(target.charAt(i)));
}
return myLink;
}
/**
* 字符串顺序写入链表
* @param target
* @return
*/
private static MyLink initMyLink01(String target) {
MyLink myLink = new MyLink();
// 因为该链表每次都是从头结点插入,所以这里字符串从后到前进行插入
for(int i = target.length() - 1; i >= 0; i--) {
myLink.addLink(String.valueOf(target.charAt(i)));
}
return myLink;
}
public static void main(String[] arg) {
String s1 = "a";
String s2 = "ab";
String s3 = "abc";
String s4 = "abcba";
String s5 = "abcbabcba";
System.out.println("a:" + isPalindromicString(s1));
System.out.println("ab:" + isPalindromicString(s2));
System.out.println("abc:" + isPalindromicString(s3));
System.out.println("abcba:" + isPalindromicString(s4));
System.out.println("abcbabcba:" + isPalindromicString(s5));
}
}
测试结果如下:
使用数组判断一个字符串是否是回文字符串:代码如下,想清楚之后还是很容易写出来的。
package com.freshbin.dataStructAndAlgo.chapter06.Array.palindromicString;
/**
* 使用数组实现回文字符串校验
*
* 思路:
* 1、首先将字符串写进数组
* 2、从索引0到(0+size)/2(小于该值)处遍历数组,
* 同时用leftIndex记录从0递增到(0+size)/2(小于该值),用rightIndex记录从size-1递减到(0+size)/2(大于该值)
* 左右两边同时进行移动,判断左右两边的值是否相等,不相等就退出循环表示不是回文数,否则就是回文数
* @author freshbin
* @date 2020/4/12 10:45
*/
public class MyArrayPalindromicString {
public static Boolean isPalindromicString(String target) {
if(target == null && target.length() == 0) {
return false;
}
target = target.trim();
// 写进数组
String[] targetArray = initArray(target);
int leftIndex = 0;
int rightIndex = targetArray.length-1;
int middleIndex = (0+targetArray.length)/2;
while(leftIndex < middleIndex) {
if(!targetArray[leftIndex].equals(targetArray[rightIndex])) {
return false;
}
leftIndex++;
rightIndex--;
}
return true;
}
private static String[] initArray(String target) {
String[] arr = new String[target.length()];
System.out.print("需要判断回文的字符串:");
for(int i = 0; i < arr.length; i++) {
arr[i] = String.valueOf(target.charAt(i));
System.out.print(arr[i]);
}
System.out.println();
return arr;
}
public static void main(String[] arg) {
String s1 = "a";
String s2 = "ab";
String s3 = "abc";
String s4 = "abcba";
String s5 = "abcbabcba";
System.out.println("a:" + isPalindromicString(s1));
System.out.println("ab:" + isPalindromicString(s2));
System.out.println("abc:" + isPalindromicString(s3));
System.out.println("abcba:" + isPalindromicString(s4));
System.out.println("abcbabcba:" + isPalindromicString(s5));
}
}
结果如下:
感想:写出来之后才感到原来这么简单啊,虽然想了很久才搞定,不过总算是比我几年前刚学java有进步,这要是放到以前,我无论如何也不可能在没有任何参考的情况下完整写出来。还是太菜了,学了好几年了,也该有进步了,是该要有进步了啊!
github代码地址:https://github.com/freshbin/dataStructAndAlgo.git
课程例题的github地址:https://github.com/wangzheng0822/algo/tree/master/java/06_linkedlist