数据结构与算法-链表与数组

常见的链表:单链表,双链表,循环链表。

使用链表实现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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值