原创

Java数据结构和算法(代码版)-2


Java数据结构和算法(代码版)-2

B树

package com.lzlg.study.algorithm.new2023.btree;

import java.util.Arrays;

/
 * B树,特性有:
 * 1.非叶子节点的孩子树称为度(degree)或阶(order)-用m指代
 * 2.非叶子节点最多有m个孩子
 * 3.除根节点和叶子节点,其他节点至少有ceil(m/2)-[m除以2向上取整]个孩子
 * 4.所有的叶子节点都在同一层
 * 5.每个非叶子节点由n个关键字和n+1个指针组成,其中n的范围是[ceil(m/2)-1, m-1]
 * 孩子数量: [ceil(m/2), m]
 * 关键字数: [ceil(m/2)-1, m-1]
 * 6.同一层的节点按关键字升序排列
 *
 * @author lzlg
 * 2023/7/31 9:53
 */
public class BTree {

    // 树的根节点
    BNode root;
    // 树的最小度数
    int minDegree;
    // 节点的key数组的最大数量

    final int MAX_KEYS;
    // 节点的key数组的最小数量
    final int MIN_KEYS;

    public BTree() {
        // 默认最小度数为2
        this(2);
    }

    public BTree(int minDegree) {
        // 最小度数
        this.minDegree = minDegree;
        // 创建根节点
        this.root = new BNode(this.minDegree);
        // 最大的key数量是最小度数的两倍-1
        MAX_KEYS = this.minDegree * 2 - 1;
        // 最小的key数量是最小度数-1
        MIN_KEYS = this.minDegree - 1;
    }

    /
     * 删除指定key的节点
     *
     * @param key 指定key
     */
    public void remove(int key) {
        doRemove(root, key, null, 0);
    }

    /
     * 递归删除指定key的节点
     *
     * @param node   当前节点
     * @param key    指定key
     * @param parent 当前节点的父节点
     * @param index  当前节点在父节点的children的下标
     */
    private void doRemove(BNode node, int key, BNode parent, int index) {
        int i = 0;
        // 从当前节点中查找key
        while (i < node.keyCount) {
            // 注意这里的循环退出条件是>=
            // 包括两种情况:
            // 1.找到了key,此时i就是key对应的当前节点keys的下标
            // 2.没找到key,如果当前节点是叶子节点,则是没找到
            // 如果当前节点是非叶子节点,则需要到孩子节点中查找,此时i是孩子节点的下标
            if (node.keys[i] >= key) {
                break;
            }
            i++;
        }

        // 如果当前节点是叶子节点
        if (node.leaf) {
            // 如果没找到,则直接返回
            if (!found(node, key, i)) {
                return;
            }
            // 如果找到了,则从当前节点中删除key
            // 此时i就是key对应的当前节点keys的下标
            node.removeKey(i);

            // 删除key后,可能要进行调整

        } else {// 如果当前节点不是叶子节点
            // 如果没找到,则递归到孩子节点进行查找和删除
            if (!found(node, key, i)) {
                // 此时i是孩子节点的下标
                doRemove(node.children[i], key, node, i);

                // 删除key后,可能要进行调整
            } else {
                // 如果找到了,则进行删除
                // 删除时需要找到后继叶子节点,进行key的替换,然后删除后继节点
                // 此时需要在i+1的孩子节点上查找后继叶子节点
                BNode s = node.children[i + 1];
                // 直到找到是叶子节点的后继
                while (!s.leaf) {
                    s = s.children[0];
                }
                // 进行key的替换
                int sKey = s.keys[0];
                node.keys[i] = sKey;

                // 将后继节点删除
                doRemove(node.children[i + 1], sKey, node, i + 1);

                // 删除key后,可能要进行调整
            }
        }

        // 如果小于最小的度数,则进行调整
        if (node.keyCount < MIN_KEYS) {
            balance(parent, node, index);
        }
    }

    /
     * 删除节点后进行平衡调整
     *
     * @param parent 父节点
     * @param node   当前节点
     * @param index  当前节点在父节点的下标
     */
    private void balance(BNode parent, BNode node, int index) {
        // 如果当前节点已经是根节点,则不进行调整
        // 因为根节点的keyCount可以小于最小度数
        if (node == root) {
            // 情况4:如果根节点的keyCount为0,则将根节点的孩子节点变为根节点
            // 因为要留着根节点,所以有判断root.children[0] != null保留无数据的根节点(刚创建时的状态)
            if (root.keyCount == 0 && root.children[0] != null) {
                root = root.children[0];
            }
            return;
        }
        // 找到当前节点的左兄弟
        BNode left = parent.childLeftSibling(index);
        // 找到当前节点的右兄弟
        BNode right = parent.childRightSibling(index);

        // 情况1:如果左边兄弟富裕,则从左边兄弟借一个
        if (left != null && left.keyCount > MIN_KEYS) {
            // 先将父节点中的index-1(因为要从左边挪动一个到父节点,父节点挪动一个到当前节点)
            // 的key插入到当前节点keys的头部
            node.insertKey(parent.keys[index - 1], 0);

            // 如果左边兄弟节点不是叶子节点
            // 则需要把左边兄弟节点的最右边的孩子节点删除
            // 并插入到当前节点children的头部
            if (!left.leaf) {
                node.insertChild(left.removeRightMostChild(), 0);
            }

            // 从左边兄弟节点keys中删除最后一个,并赋值给父节点中的index-1
            parent.keys[index - 1] = left.removeRightMostKey();
            return;
        }

        // 情况2:如果右边兄弟富裕,则从右边兄弟借一个
        if (right != null && right.keyCount > MIN_KEYS) {
            // 将父节点的下标为index的key转移到当前节点最后
            node.insertKey(parent.keys[index], node.keyCount);

            // 如果右边兄弟不是叶子节点
            // 需要右边兄弟最左边的孩子节点删除
            // 并插入到新节点的最后
            if (!right.leaf) {
                node.insertChild(right.removeLeftMostChild(), node.keyCount + 1);
            }

            // 将右边兄弟最左边的key删除,并赋值给父节点的下标为index的key
            parent.keys[index] = right.removeLeftMostKey();
            return;
        }

        // 情况3:如果左右兄弟都不富裕,则进行合并,使用靠左合并的方法
        // 如果左边兄弟不为null,则合并到左边兄弟
        if (left != null) {
            // 先从父节点中删除当前节点
            parent.removeChild(index);

            // 将父节点的index-1的key删除并插入到左边兄弟的最右侧
            left.insertKey(parent.removeKey(index - 1), left.keyCount);

            // 然后将当前节点的keys和children都转移到左边兄弟
            node.moveToTarget(left);

        } else {// 否则合并到自身
            // 从父节点中删除右边兄弟
            parent.removeChild(index + 1);

            // 父节点中删除index指定的key并插入到当前节点中
            node.insertKey(parent.removeKey(index), node.keyCount);

            if (right != null) {
                // 将删除的右边兄弟的keys和children都转移到当前节点
                right.moveToTarget(node);
            }
        }

    }

    /
     * 是否在当前节点中找到指定key
     *
     * @param node 当前节点
     * @param key  指定key
     * @param i    下标
     * @return 结果
     */
    private boolean found(BNode node, int key, int i) {
        return i < node.keyCount && node.keys[i] == key;
    }


    /
     * 添加或更新指定的key
     *
     * @param key 指定key
     */
    public void put(int key) {
        doPut(root, key, null, 0);
    }

    /
     * 添加或更新指定的key
     *
     * @param node 节点
     * @param key  指定key
     */
    private void doPut(BNode node, int key, BNode parent, int index) {
        int i = 0;
        while (i < node.keyCount) {
            // 如果找到了,则进行更新(因为没有value,所以这里没有操作)
            if (node.keys[i] == key) {
                // 更新操作
                return;
            }
            // 如果遇到比key大的,则直接跳出循环
            // 此时i的值就指向待插入的位置下标
            if (node.keys[i] > key) {
                break;
            }
            i++;
        }
        // 如果当前节点是叶子节点,则进行插入
        if (node.leaf) {
            // 进行插入
            node.insertKey(key, i);

            // 插入之后,需判断当前节点的keyCount是否达到MAX_KEYS
            // 如果达到则需要进行分裂

        } else {// 不是叶子节点,则进行递归
            // 此时i值就是孩子节点数组的下标
            doPut(node.children[i], key, node, i);

            // 非叶子节点也有可能keyCount达到MAX_KEYS
            // 如果达到则需要进行分裂

        }

        // 进行分裂操作
        if (node.keyCount == MAX_KEYS) {
            split(node, parent, index);
        }

    }

    /
     * 进行分离,步骤如下:
     * 1.创建一个新的节点
     * 2.将待分裂节点的右半部分(范围从最小度数到keys数组末尾)转移到新节点上
     * 3.更新新节点和分裂节点的节点数量(keyCount)
     * 4.将分裂节点下标为minDegree的元素插入到父节点中
     * 插入父节点的下标即为当前分裂节点在父节点中的下标(index)
     * 5.将新节点插入到父节点孩子节点数组中
     * 插入的下标为当前分裂节点在父节点中的下标(index)+1
     * <p>
     * 两种特殊情况:
     * 1.如果分裂节点是根节点(parent为null)
     * 则先创建一个新的根节点,且将分裂节点加入到新根节点,替换旧根节点和赋值给父节点
     * 并按上述步骤操作即可
     * 2.如果分裂节点是非叶子节点,则在步骤2时,需把相应的孩子节点也转移过去
     *
     * @param child  孩子节点(也是需分裂的节点)
     * @param parent 父节点
     * @param index  孩子节点在父节点的下标
     */
    private void split(BNode child, BNode parent, int index) {
        // 特殊情况:如果是根节点
        if (parent == null) {
            // 创建新的根节点
            BNode newRoot = new BNode(minDegree);
            // 新的根节点一定不是叶子节点
            newRoot.leaf = false;
            // 需要将孩子节点插入到新根节点
            newRoot.insertChild(child, 0);

            // 替换旧根节点
            this.root = newRoot;
            // 父节点是新的根节点
            parent = newRoot;
        }

        // 创建新孩子节点
        BNode newChild = new BNode(minDegree);
        // 孩子节点和新孩子节点在同一层,因此是否叶子节点的leaf属性保持一致
        newChild.leaf = child.leaf;

        // 将孩子节点的数据转移一部分到新节点中
        System.arraycopy(child.keys, minDegree, newChild.keys, 0, minDegree - 1);
        // 特殊情况2:
        // 如果孩子节点不是叶子节点,则需要拷贝孩子节点的孩子节点
        if (!child.leaf) {
            // 注意这里复制的数量是minDegree(最小度数)
            System.arraycopy(child.children, minDegree, newChild.children, 0, minDegree);
        }
        // 更新新孩子节点的有效key数量
        newChild.keyCount = minDegree - 1;

        // 将孩子节点中间的元素插入到父节点中
        parent.insertKey(child.keys[minDegree - 1], index);
        // 更新孩子节点的有效key数量
        child.keyCount = minDegree - 1;

        // 将新孩子节点插入到父节点中
        parent.insertChild(newChild, index + 1);
    }

    /
     * 判断是否包含指定key
     *
     * @param key 指定key
     * @return 结果
     */
    public boolean contains(int key) {
        // 能找到不为null则是包含
        return root.get(key) != null;
    }

    /
     * 节点类
     */
    static class BNode {
        // 关键字数组,用于查找定位,是升序的
        int[] keys;
        // 孩子节点,有可能有多个
        BNode[] children;
        // 有效的关键字数量
        // keyCount+1是孩子节点最大的数量
        int keyCount;

        // 是否是叶子节点,默认是true
        boolean leaf;
        // 最小度数
        int minDegree;

        public BNode(int minDegree) {
            if (minDegree < 2) {
                throw new IllegalArgumentException("最小度数:" + minDegree + "非法");
            }
            this.minDegree = minDegree;
            // 默认是叶子节点
            this.leaf = true;
            // 孩子节点的数量是最小度数的2倍(B树性质)
            this.children = new BNode[this.minDegree * 2];
            // 关键字的数量是最小度数的2倍-1(B树性质)
            this.keys = new int[this.minDegree * 2 - 1];
        }

        @Override
        public String toString() {
            // 只返回keys数组的有效部分
            return Arrays.toString(Arrays.copyOfRange(keys, 0, keyCount));
        }

        /
         * 通过key查找指定的节点
         *
         * @param key 关键字key
         * @return 节点
         */
        BNode get(int key) {
            int i = 0;
            // 找到指定key的节点
            while (i < keyCount) {
                // 如果当前节点的keys中有对应key的,则返回该当前节点
                if (keys[i] == key) {
                    return this;
                }
                // 找到比指定key大的
                if (keys[i] > key) {
                    break;
                }
                i++;
            }
            // 如果当前节点是叶子节点,且在上面的循环中没有找到,则直接返回null
            if (leaf) {
                return null;
            }
            // 如果当前节点是非叶子节点,则在孩子节点中进行查找
            // 退出循环时的i值就是孩子节点的下标
            return children[i].get(key);
        }

        /
         * 向指定index下标插入key
         *
         * @param key   关键字key
         * @param index 下标
         */
        void insertKey(int key, int index) {
            // 把从index到数组尾的元素移动
            System.arraycopy(keys, index, keys, index + 1, keyCount - index);
            // 移动完成后,将key放入到指定下标
            keys[index] = key;
            // 数组元素数量加1
            keyCount++;
        }

        /
         * 向指定index下标插入孩子节点
         *
         * @param child 孩子节点
         * @param index 下标
         */
        void insertChild(BNode child, int index) {
            // 把从index到数组尾的元素移动
            System.arraycopy(children, index, children, index + 1, keyCount - index);
            // 移动完成后,将child放入到指定下标
            children[index] = child;
            // 数组元素数量的维护交给方法insertKey进行维护了,这里就不增加了
        }

        /
         * 删除指定下标的key
         *
         * @param index 指定下标
         * @return 被删除的key
         */
        int removeKey(int index) {
            // 被删除的key值
            int removed = keys[index];
            // 移动元素把下标为index的位置覆盖
            System.arraycopy(keys, index + 1, keys, index, keyCount - index - 1);
            // 减少关键字数量
            keyCount--;
            return removed;
        }

        /
         * 删除最左边的key(第一个)
         *
         * @return 被删除的key
         */
        int removeLeftMostKey() {
            return removeKey(0);
        }

        /
         * 删除最右边的key(最后一个)
         *
         * @return 被删除的key
         */
        int removeRightMostKey() {
            return removeKey(keyCount - 1);
        }

        /
         * 删除指定下标的孩子节点
         *
         * @param index 下标
         * @return 孩子节点
         */
        BNode removeChild(int index) {
            BNode child = children[index];
            // 注意这里最后一个参数不再减1,因为移动key的时候已经做过了
            System.arraycopy(children, index + 1, children, index, keyCount - index);
            // help GC
            children[keyCount] = null;
            return child;
        }

        /
         * 删除最左边的child(第一个)
         *
         * @return 被删除的child
         */
        BNode removeLeftMostChild() {
            return removeChild(0);
        }

        /
         * 删除最右边的child(最后一个)
         *
         * @return 被删除的child
         */
        BNode removeRightMostChild() {
            // 注意这里最后一个参数不再减1,因为移动key的时候已经做过了
            return removeChild(keyCount);
        }

        /
         * 找到指定下标孩子节点的左兄弟
         *
         * @param index 下标
         * @return 左兄弟
         */
        BNode childLeftSibling(int index) {
            // 如果大于0,则有左兄弟,否则没有
            return index > 0 ? children[index - 1] : null;
        }

        /
         * 找到指定下标孩子节点的右兄弟
         *
         * @param index 下标
         * @return 右兄弟
         */
        BNode childRightSibling(int index) {
            // 如果等于keyCount,则没有右兄弟,否则有
            return index == keyCount ? null : children[index + 1];
        }

        /
         * 将节点的keys和children转移到目标节点上
         *
         * @param target 目标节点
         */
        void moveToTarget(BNode target) {
            // 起始添加的位置
            int start = target.keyCount;
            // 如果不是叶子节点,则移动孩子节点
            if (!leaf) {

                // 进行copy,要比移动keys多移动一位
                System.arraycopy(children, 0, target.children, start, keyCount + 1);
//                for (int i = 0; i < keyCount; i++) {
//                    target.children[start + i] = children[i];
//                }
            }
            // 进行copy移动
            System.arraycopy(keys, 0, target.keys, start, keyCount);
            // 目标的keyCount增加当前节点的keyCount
            target.keyCount += keyCount;
            // 移动keys
//            for (int i = 0; i < keyCount; i++) {
//                // 移动完成后,还得增加目标的keyCount
//                target.keys[target.keyCount++] = keys[i];
//            }
        }
    }
}
package com.lzlg.study.algorithm.new2023.btree;

import java.util.LinkedList;

/
 * 测试
 *
 * @author lzlg
 * 2023/7/31 12:54
 */
public class TestBTree {
    public static void main(String[] args) {
        testBtree();
    }

    private static void testMoveTarget() {
        BTree.BNode node = new BTree.BNode(2);
        node.leaf = false;
        node.keyCount = 2;
        node.keys = new int[]{2, 3, 0};
        node.children = new BTree.BNode[]{
                new BTree.BNode(2),
                new BTree.BNode(2),
                new BTree.BNode(2)
        };
        System.out.println(node);
        System.out.println("==================");
        BTree.BNode target = new BTree.BNode(2);

        node.moveToTarget(target);
        System.out.println(target);
    }

    private static void testBtree() {
        BTree tree = new BTree();
        tree.put(1);
        tree.put(2);
        tree.put(3);
        tree.put(4);
        tree.put(5);
        tree.put(6);
        tree.put(7);
        tree.put(8);
        tree.put(9);
        tree.put(10);
        tree.put(11);
        print(tree.root);
        System.out.println("=============================");
        tree.remove(2);
        print(tree.root);
        System.out.println("=============================");
    }

    private static void print(BTree.BNode node) {
        if (node == null) {
            return;
        }
        LinkedList<TestNode> queue = new LinkedList<>();
        queue.offer(new TestNode(node, null));

        int count = 1;
        while (!queue.isEmpty()) {
            // 下一层的元素数量
            int nextCount = 0;

            for (int i = 0; i < count; i++) {
                // 弹出栈顶元素
                TestNode pop = queue.pop();
                System.out.print(pop + "\t");

                // 将孩子节点加入到队列中
                for (int j = 0; j <= pop.node.keyCount; j++) {
                    if (pop.node.children[j] != null) {
                        nextCount++;
                        queue.offer(new TestNode(pop.node.children[j], pop.node));
                    }
                }
            }
            // 换行
            System.out.println("--------------------------------");
            count = nextCount;
        }
    }

    private static class TestNode {
        // 当前节点
        BTree.BNode node;
        // 父节点
        BTree.BNode parent;

        public TestNode(BTree.BNode node, BTree.BNode parent) {
            this.node = node;
            this.parent = parent;
        }

        @Override
        public String toString() {
            return parent == null ? node.toString()
                    : "parent: " + parent + ", keys: " + node.toString() + "\n";
        }
    }
}

package com.lzlg.study.algorithm.new2023.stack;

import java.util.Iterator;

/
 * 使用数组实现栈
 * 数组的头部是栈底,尾部是栈顶
 *
 * @author lzlg
 * 2023/7/23 10:14
 */
public class ArrayStack<T> implements MyStack<T> {

    // 数组
    private final T[] array;
    // 栈顶指针下标
    private int top;

    @SuppressWarnings("unchecked")
    public ArrayStack(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("栈不能为空");
        }
        this.array = (T[]) new Object[capacity];
        this.top = 0;
    }

    /
     * 压栈
     *
     * @param element 待添加元素
     * @return 是否成功
     */
    @Override
    public boolean push(T element) {
        if (isFull()) {
            return false;
        }
//        array[top] = element;
//        top++;
        // 可简写成
        array[top++] = element;
        return true;
    }

    /
     * 出栈
     *
     * @return 出栈元素
     */
    @Override
    public T pop() {
        if (isEmpty()) {
            return null;
        }
//        T element = array[top - 1];
//        top--;
//        return element;
        // 可简写成
        return array[--top];
    }

    /
     * 查看栈顶
     *
     * @return 栈顶元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        return array[top - 1];
    }

    /
     * 栈是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return top == 0;
    }

    /
     * 栈是否满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return top == array.length;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            int p = top;

            @Override
            public boolean hasNext() {
                return p > 0;
            }

            @Override
            public T next() {
//                T element = array[p - 1];
//                p--;
//                return element;
                // 可简写成
                return array[--p];
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.stack;

import java.util.Iterator;

/
 * 使用链表实现栈
 *
 * @author lzlg
 * 2023/7/23 10:21
 */
public class LinkedListStack<T> implements MyStack<T> {

    // 栈容量
    private final int capacity;
    // 栈中实际元素的数量
    private int size;
    // 栈头节点(是哨兵节点)
    private final Node<T> head;

    public LinkedListStack(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("栈不能为空");
        }
        this.capacity = capacity;
        this.size = 0;
        this.head = new Node<>(null, null);
    }

    /
     * 压栈
     *
     * @param element 待添加元素
     * @return 是否成功
     */
    @Override
    public boolean push(T element) {
        if (isFull()) {
            return false;
        }
        // 让链表头部插入元素
        head.next = new Node<>(element, head.next);
        size++;
        return true;
    }

    /
     * 出栈
     *
     * @return 出栈元素
     */
    @Override
    public T pop() {
        if (isEmpty()) {
            return null;
        }
        Node<T> first = head.next;
        // 移出头部节点
        head.next = first.next;
        size--;
        return first.element;
    }

    /
     * 查看栈顶
     *
     * @return 栈顶元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        return head.next.element;
    }

    /
     * 栈是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
//        return size == 0;
        // 可写成
        return head.next == null;
    }

    /
     * 栈是否满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == capacity;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            Node<T> p = head.next;

            @Override
            public boolean hasNext() {
                return p != null;
            }

            @Override
            public T next() {
                T element = p.element;
                p = p.next;
                return element;
            }
        };
    }

    /
     * 节点类
     */
    private static class Node<T> {

        T element;

        Node<T> next;

        public Node(T element, Node<T> next) {
            this.element = element;
            this.next = next;
        }
    }
}
package com.lzlg.study.algorithm.new2023.stack;

/
 * 自定义栈接口
 * 栈结构是只在一端进行操作的数据结构,此一端称为栈顶,另一端称为栈底
 *
 * @author lzlg
 * 2023/7/23 10:10
 */
public interface MyStack<T> extends Iterable<T> {
    /
     * 压栈
     *
     * @param element 待添加元素
     * @return 是否成功
     */
    boolean push(T element);

    /
     * 出栈
     *
     * @return 出栈元素
     */
    T pop();

    /
     * 查看栈顶
     *
     * @return 栈顶元素
     */
    T peek();

    /
     * 栈是否为空
     *
     * @return 结果
     */
    boolean isEmpty();

    /
     * 栈是否满
     *
     * @return 结果
     */
    boolean isFull();
}
package com.lzlg.study.algorithm.new2023.stack;

/
 * 测试栈
 *
 * @author lzlg
 * 2023/7/23 10:20
 */
public class MyStackTest {

    public static void main(String[] args) {
        MyStack<Integer> stack = new LinkedListStack<>(3);
        stack.push(1);
        System.out.println(stack.peek());
        stack.push(2);
        System.out.println(stack.peek());
        stack.push(3);
        System.out.println(stack.peek());
        stack.push(4);
        System.out.println(stack.peek());

        System.out.println("打印所有栈元素:");
        for (Integer element : stack) {
            System.out.println(element);
        }
    }
}
package com.lzlg.study.algorithm.new2023.stack.quiz;

import com.lzlg.study.algorithm.new2023.stack.LinkedListStack;
import com.lzlg.study.algorithm.new2023.stack.MyStack;

/
 * 计算逆波兰表达式-后缀表达式
 * 所有的输入都是合法的,无须担心异常情况
 * 中缀表达式,就是平常使用的,如 a * b + c
 * 后缀表达式,则是运算符写在数值后面的: a b * c +
 *
 * @author lzlg
 * 2023/7/23 11:05
 */
public class CalculateReversePolishNotation {
    /
     * 计算逆波兰表达式
     *
     * @param tokens 表达式字符串数值
     * @return 计算结果
     */
    public static int calculate(String[] tokens) {
        // 创建栈
        MyStack<Integer> stack = new LinkedListStack<>(Integer.MAX_VALUE);
        int right;
        int left;
        for (String token : tokens) {
            switch (token) {
                // 遇到运算符,从栈中弹出两个数字,进行运算
                // 预算完成还需将结果压入栈中
                case "+":
                    // 注意,第一个弹出的是运算符右边的
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left + right);
                    break;
                case "-":
                    // 注意,第一个弹出的是运算符右边的
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left - right);
                    break;
                case "*":
                    // 注意,第一个弹出的是运算符右边的
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left * right);
                    break;
                case "/":
                    // 注意,第一个弹出的是运算符右边的
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left / right);
                    break;
                default:
                    // 遇到数字,则压入栈中
                    stack.push(Integer.parseInt(token));
                    break;
            }
        }
        // 最后栈顶保存的就是最终的运算结果
        return stack.pop();

    }

    public static void main(String[] args) {
        String[] tokens1 = {"2", "1", "+", "3", "*"};
        System.out.println("计算结果为: " + calculate(tokens1));
        String[] tokens2 = {"10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"};
        System.out.println("计算结果为: " + calculate(tokens2));
    }
}
package com.lzlg.study.algorithm.new2023.stack.quiz;

import com.lzlg.study.algorithm.new2023.stack.ArrayStack;
import com.lzlg.study.algorithm.new2023.stack.MyStack;

/
 * 检测是否是有效的括号
 * 一个字符串只有三种括号: {}, [], ()
 * 假如字符串是({}),则是有效的,如果是([[则不是有效的
 *
 * @author lzlg
 * 2023/7/23 10:44
 */
public class CheckValidBracket {
    /
     * 判断字符串是否有效
     * 使用栈
     * 遍历字符串种的字符,当遇到一个左括号时,压入栈一个相应的右括号
     * 当遇到一个右括号时,和栈顶的括号进行比较,如果不相等,则字符串不是有效的
     * 如果循环结束,栈不为空,则字符串不是有效的
     *
     * @param s 字符串
     * @return 结果
     */
    public static boolean isValid(String s) {
        if (s == null || s.length() == 0 || s.length() == 1) {
            return false;
        }
        int strLen = s.length();
        MyStack<Character> stack = new ArrayStack<>(strLen);
        for (int i = 0; i < strLen; i++) {
            char c = s.charAt(i);
            // 遇到一个左括号时,压入栈一个相应的右括号
            if (c == '{') {
                stack.push('}');
            } else if (c == '(') {
                stack.push(')');
            } else if (c == '[') {
                stack.push(']');
            } else {// 右括号情况
                // 如果一开始遇到的就是右括号,此时栈为空且字符串一定无效
                if (stack.isEmpty()) {
                    return false;
                }
                // 查看栈顶的括号是否相等
                if (c == stack.peek()) {
                    // 如果相等,弹出栈顶括号
                    stack.pop();
                } else {
                    // 如果不等,则直接返回false
                    return false;
                }

            }
        }
        // 循环结束,如果栈为空,则是有效的字符串
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        System.out.println(isValid("{()}"));
        System.out.println(isValid("{]"));
        System.out.println(isValid("{()["));
        System.out.println(isValid("[(])"));
        System.out.println(isValid(")("));
    }
}
package com.lzlg.study.algorithm.new2023.stack.quiz;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;
import com.lzlg.study.algorithm.new2023.stack.ArrayStack;
import com.lzlg.study.algorithm.new2023.stack.MyStack;

import java.util.Iterator;

/
 * 双栈模拟队列
 * 一个栈是队头,负责出队
 * 一个栈是队尾,负责入队
 *
 * @author lzlg
 * 2023/7/23 12:19
 */
public class DoubleStackQueue<T> implements MyQueue<T> {
    // 队头栈
    private final MyStack<T> headStack;
    // 队尾栈
    private final MyStack<T> tailStack;

    public DoubleStackQueue() {
        headStack = new ArrayStack<>(Integer.MAX_VALUE);
        tailStack = new ArrayStack<>(Integer.MAX_VALUE);
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // push到队尾的栈中
        tailStack.push(element);
        return false;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        // 如果队头栈为空,则从队尾弹出元素加入到队头
        if (headStack.isEmpty()) {
            while (!tailStack.isEmpty()) {
                headStack.push(tailStack.pop());
            }
        }
        return headStack.pop();
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队头栈为空,则从队尾弹出元素加入到队头
        if (headStack.isEmpty()) {
            while (!tailStack.isEmpty()) {
                headStack.push(tailStack.pop());
            }
        }
        return headStack.peek();
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return headStack.isEmpty() && tailStack.isEmpty();
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return headStack.isFull() || tailStack.isFull();
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return null;
    }
}
package com.lzlg.study.algorithm.new2023.stack.quiz;

import com.lzlg.study.algorithm.new2023.stack.LinkedListStack;
import com.lzlg.study.algorithm.new2023.stack.MyStack;

/
 * 中缀表达式转后缀表达式
 * 中缀表达式: 1+2*3
 * 后缀表达式: 123*+
 *
 * @author lzlg
 * 2023/7/23 11:42
 */
public class InfixToSuffix {
    /
     * 中缀表达式转后缀表达式
     * 遍历字符串中的字符
     * 1.如果遇到不是运算符,则直接添加到新字符串中
     * 2.如果遇到的是运算符:
     * 1)如果栈为空,则直接入栈
     * 2)如果栈不为空,则弹出比当前运算符相同或高的运算符并添加到新字符串中,然后当前运算符入栈
     * 如果栈顶的运算符的优先级比当前运算符低,则直接入栈
     * 3.如果遇到的是括号:
     * 1)如果是左括号,直接入栈,左括号的优先级比其他运算符优先级低
     * 2)如果是右括号,则弹出直到左括号的所有运算符并添加到新字符串中,且还要弹出左括号
     *
     * @param infix 中缀
     * @return 后缀
     */
    public static String infixToSuffix(String infix) {
        MyStack<Character> stack = new LinkedListStack<>(Integer.MAX_VALUE);
        // 新字符串
        StringBuilder sb = new StringBuilder();
        // 数组长度
        int len = infix.length();
        // 遍历字符串所有字符
        for (int i = 0; i < len; i++) {
            char c = infix.charAt(i);
            switch (c) {
                case '+':
                case '-':
                case '*':
                case '/':
                    // 遇到运算符,如果栈为空,则直接入栈
                    if (stack.isEmpty()) {
                        stack.push(c);
                    } else {
                        // 如果当前运算符的优先级高于栈顶的,则直接入栈
                        if (priority(c) > priority(stack.peek())) {
                            stack.push(c);
                        } else {
                            // 把所有大于或等于当前运算符优先级的运算符弹出,并添加到新字符串中
                            while (!stack.isEmpty() && priority(c) <= priority(stack.peek())) {
                                sb.append(stack.pop());
                            }
                            // 循环退出后,把当前运算符入栈
                            stack.push(c);
                        }
                    }
                    break;
                case '(':
                    // 遇到左括号,直接入栈
                    stack.push(c);
                    break;
                case ')':
                    // 遇到右括号,则弹出直到左括号的运算符,并添加到新字符串中
                    while (stack.peek() != '(') {
                        sb.append(stack.pop());
                    }
                    // 循环退出,弹出栈中的左括号
                    stack.pop();
                    break;
                default:
                    // 遇到的是数字,直接添加到系字符串中
                    sb.append(c);
                    break;
            }

        }
        // 如果栈不为空,则弹出栈中所有运算符,并添加到新字符串中
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }

        return sb.toString();
    }

    /
     * 获取优先级
     *
     * @param c 字符
     * @return 优先级
     */
    static int priority(char c) {
        switch (c) {
            case '*':
            case '/':
                return 2;
            case '+':
            case '-':
                return 1;
            case '(':
                return 0;
            default:
                throw new IllegalArgumentException("非法运算符: " + c);
        }
    }

    public static void main(String[] args) {
        String infix = "(2+1)*3";
        System.out.println("中缀表达式为: " + infix);
        System.out.println("后缀表达式为: " + infixToSuffix(infix));
        System.out.println("==================");
        infix = "4+(9/5)";
        System.out.println("中缀表达式为: " + infix);
        System.out.println("后缀表达式为: " + infixToSuffix(infix));
        System.out.println("==================");
        infix = "(1+(9/5+1)*6+5)*1+0+4+2*3+8-7";
        System.out.println("中缀表达式为: " + infix);
        System.out.println("后缀表达式为: " + infixToSuffix(infix));
    }
}
package com.lzlg.study.algorithm.new2023.stack.quiz;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;
import com.lzlg.study.algorithm.new2023.queue.SecondArrayQueue;
import com.lzlg.study.algorithm.new2023.stack.MyStack;

import java.util.Iterator;

/
 * 使用单个队列模拟栈
 * 在压栈时,如果队列为空,则直接添加
 * 如果队列不为空,则先把新元素添加到队列中,然后把队列中旧元素依次出队再入队
 *
 * @author lzlg
 * 2023/7/23 12:34
 */
public class SingleQueueStack<T> implements MyStack<T> {
    // 队列
    private final MyQueue<T> queue;
    // 记录元素数量
    private int size;

    public SingleQueueStack() {
        queue = new SecondArrayQueue<>(Integer.MAX_VALUE);
        size = 0;
    }

    /
     * 压栈
     *
     * @param element 待添加元素
     * @return 是否成功
     */
    @Override
    public boolean push(T element) {
        queue.offer(element);
        // 循环次数是原先队列中的元素数量
        for (int i = 0; i < size; i++) {
            queue.offer(queue.poll());
        }
        size++;
        return true;
    }

    /
     * 出栈
     *
     * @return 出栈元素
     */
    @Override
    public T pop() {
        size--;
        return queue.poll();
    }

    /
     * 查看栈顶
     *
     * @return 栈顶元素
     */
    @Override
    public T peek() {
        return queue.peek();
    }

    /
     * 栈是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return queue.isEmpty();
    }

    /
     * 栈是否满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return queue.isFull();
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return null;
    }
}

队列

package com.lzlg.study.algorithm.new2023.queue.block;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/
 * 使用双锁实现阻塞队列
 * 这样添加和删除操作不会争抢同一把锁
 *
 * @author lzlg
 * 2023/7/25 10:43
 */
public class DoubleLockBlockQueue<T> implements MyBlockQueue<T> {
    // 元素数组
    private final T[] array;
    // 头指针下标
    private int head;
    // 尾指针下标
    private int tail;
    // 元素数量,这里使用AtomicInteger因为使用了两个锁
    // 而poll和offer方法都会对size进行修改,因此会产生size修改不正确问题
    private AtomicInteger size;

    // 头部操作使用的锁
    private final ReentrantLock headLock;
    // 头部操作的等待条件
    private final Condition headCondition;

    // 尾部操作使用的锁
    private final ReentrantLock tailLock;
    // 尾部操作的等待条件
    private final Condition tailCondition;

    @SuppressWarnings("unchecked")
    public DoubleLockBlockQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列容量不能为空");
        }
        array = (T[]) new Object[capacity];
        head = 0;
        tail = 0;
        size = new AtomicInteger(0);
        // 头部锁的初始化
        headLock = new ReentrantLock();
        headCondition = headLock.newCondition();
        // 尾部锁的初始化
        tailLock = new ReentrantLock();
        tailCondition = tailLock.newCondition();
    }

    /
     * 添加元素到尾部
     *
     * @param element 元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public void offer(T element) throws InterruptedException {
        tailLock.lockInterruptibly();

        // 记录size修改前的值
        int prevSize;
        try {
            // 如果队列已满,则进入等待
            while (isFull()) {
                tailCondition.await();
            }

            // 添加元素到数组尾部
            array[tail] = element;
            // 如果尾部指针的下标+1等于数组长度,则置为0
            if (++tail == array.length) {
                tail = 0;
            }
            // 进行size的增加
            prevSize = size.getAndIncrement();

            // 错误1:不能直接写成下面的
            // 因为headCondition配对的锁是headLock
            // 需要在headLock的加锁和解锁上进行唤醒
//            headCondition.signal();

            // 错误2:会操作死锁
            // 因为是在tailLock的加锁和解锁之间,需要写在tailLock的加锁和解锁之外
//            headLock.lock();
//            try {
//                headCondition.signal();
//            } finally {
//                headLock.unlock();
//            }

            // 如果之前的size+1小于数组长度,则添加当前元素后
            // 队列中还有空间进行添加操作,则继续唤醒其他添加操作的等待线程进行处理
            if (prevSize + 1 < array.length) {
                tailCondition.signal();
            }


        } finally {
            tailLock.unlock();
        }

        // 唤醒头部操作等待的线程
        // 如果是下面的代码,则性能不高,因为当队列不满不空时是无须进行如下操作的
//        headLock.lock();
//        try {
//            headCondition.signal();
//        } finally {
//            headLock.unlock();
//        }

        // 如果size之前是0,则进行头部操作条件的唤醒
        // 使用这个判断条件则性能较高,只在队列空时添加第一个元素时唤醒
        if (prevSize == 0) {
            headLock.lock();
            try {
                headCondition.signal();
            } finally {
                headLock.unlock();
            }
        }
    }

    /
     * 添加元素到尾部,有时间限制
     *
     * @param element 元素
     * @param timeout 等待时间
     * @param unit    时间单位
     * @throws InterruptedException 打断异常
     */
    @Override
    public boolean offer(T element, long timeout, TimeUnit unit) throws InterruptedException {
        return false;
    }

    /
     * 删除头部元素
     *
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public T poll() throws InterruptedException {
        headLock.lockInterruptibly();
        // 记录之前的size
        int prevSize;
        // 返回的元素
        T element;
        try {
            // 如果队列已空,则进入等待
            while (isEmpty()) {
                headCondition.await();
            }

            // 取出头部元素
            element = array[head];
            // help GC
            array[head] = null;
            // 如果尾部指针head+1等于数组长度,则置为0
            if (++head == array.length) {
                head = 0;
            }

            // 进行size的减少
            prevSize = size.getAndIncrement();

            // 错误1:不能直接写成下面的
            // 因为tailCondition配对的锁是tailLock
            // 需要在tailLock的加锁和解锁上进行唤醒
//            tailCondition.signal();

            // 错误2:会操作死锁
            // 因为是在headLock的加锁和解锁之间,需要写在headLock的加锁和解锁之外
//            tailLock.lock();
//            try {
//                tailCondition.signal();
//            } finally {
//                tailLock.unlock();
//            }

            // 如果之前的size>1则取出当前元素后
            // 队列中还有元素,则继续唤醒其他删除操作的等待线程进行处理
            if (prevSize > 1) {
                headCondition.signal();
            }

        } finally {
            headLock.unlock();
        }

        // 唤醒尾部操作等待的线程
        // 如果是下面的代码,则性能不高,因为当队列不满不空时是无须进行如下操作的
//        tailLock.lock();
//        try {
//            tailCondition.signal();
//        } finally {
//            tailLock.unlock();
//        }

        // 如果size是数组长度,则进行尾部操作条件的唤醒
        // 使用这个判断条件则性能较高,只在队列满时删除一个元素时唤醒
        if (prevSize == array.length) {
            tailLock.lock();
            try {
                tailCondition.signal();
            } finally {
                tailLock.unlock();
            }
        }

        return element;
    }

    /
     * 删除头部元素,有时间限制
     *
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public T poll(long timeout, TimeUnit unit) throws InterruptedException {
        return null;
    }

    /
     * 队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size.get() == 0;
    }

    /
     * 队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size.get() == array.length;
    }

    @Override
    public String toString() {
        return Arrays.toString(array);
    }
}
package com.lzlg.study.algorithm.new2023.queue.block;

import java.util.concurrent.TimeUnit;

/
 * 阻塞队列接口
 *
 * @author lzlg
 * 2023/7/25 9:17
 */
public interface MyBlockQueue<T> {

    /
     * 添加元素到尾部
     *
     * @param element 元素
     * @throws InterruptedException 打断异常
     */
    void offer(T element) throws InterruptedException;

    /
     * 添加元素到尾部,有时间限制
     *
     * @param element 元素
     * @param timeout 等待时间
     * @param unit    时间单位
     * @throws InterruptedException 打断异常
     */
    boolean offer(T element, long timeout, TimeUnit unit) throws InterruptedException;

    /
     * 删除头部元素
     *
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    T poll() throws InterruptedException;

    /
     * 删除头部元素,有时间限制
     *
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    T poll(long timeout, TimeUnit unit) throws InterruptedException;

    /
     * 队列是否为空
     *
     * @return 结果
     */
    boolean isEmpty();

    /
     * 队列是否已满
     *
     * @return 结果
     */
    boolean isFull();
}
package com.lzlg.study.algorithm.new2023.queue.block;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/
 * 单锁实现阻塞队列
 * 使用单锁实现的缺点,在队列没满也没空时,添加和删除共用一把锁,造成不能同时进行
 *
 * @author lzlg
 * 2023/7/25 9:21
 */
public class SingleLockBlockQueue<T> implements MyBlockQueue<T> {
    // 数组
    private final T[] array;
    // 头指针下标
    private int head;
    // 尾指针下标
    private int tail;
    // 元素数量
    private int size;

    // 锁对象
    private final ReentrantLock lock;
    // 头部删除时条件
    private final Condition headCondition;
    // 尾部添加时条件
    private final Condition tailCondition;

    @SuppressWarnings("unchecked")
    public SingleLockBlockQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = (T[]) new Object[capacity];
        head = 0;
        tail = 0;
        size = 0;
        lock = new ReentrantLock();
        headCondition = lock.newCondition();
        tailCondition = lock.newCondition();
    }

    /
     * 添加元素到尾部
     *
     * @param element 元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public void offer(T element) throws InterruptedException {
        // 使用可打断的锁
        lock.lockInterruptibly();
        try {
            // 如果队列已满,则进行等待
            // 使用while循环,防止假唤醒造成的错误
            while (isFull()) {
                tailCondition.await();
            }

            // 在数组尾部添加元素
            array[tail] = element;
            // 如果尾指针下标加1大于等于数组长度,则置为0
            if (++tail == array.length) {
                tail = 0;
            }
            size++;

            // 唤醒删除poll操作的条件
            headCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    /
     * 添加元素到尾部,有时间限制
     *
     * @param element 元素
     * @param timeout 等待时间
     * @param unit    时间单位
     * @throws InterruptedException 打断异常
     */
    @Override
    public boolean offer(T element, long timeout, TimeUnit unit) throws InterruptedException {
        // 使用可打断的锁
        lock.lockInterruptibly();
        try {
            // 如果队列已满,则进行等待
            long time = unit.toNanos(timeout);
            // 使用while循环,防止假唤醒造成的错误
            while (isFull()) {
                // 如果等待时间已经用完,还没获得锁,则返回false不再进行等待
                if (time <= 0) {
                    return false;
                }
                // 这里使用有等待时间的方法
                // 该方法的返回值是剩余等待的时间
                // 需要更新等待时间,否则等待时间一直是offer方法传入的时间数
                time = tailCondition.awaitNanos(time);
            }

            // 在数组尾部添加元素
            array[tail] = element;
            // 如果尾指针下标加1大于等于数组长度,则置为0
            if (++tail == array.length) {
                tail = 0;
            }
            size++;

            // 唤醒删除poll操作的条件
            headCondition.signal();

            return true;
        } finally {
            lock.unlock();
        }
    }

    /
     * 删除头部元素
     *
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public T poll() throws InterruptedException {
        // 使用可打断的锁
        lock.lockInterruptibly();
        try {
            // 如果队列已空,则进行等待
            while (isEmpty()) {
                headCondition.await();
            }

            // 取出头部元素
            T element = array[head];
            // 如果head指针下标+1等于数组长度,则置为0
            if (++head == array.length) {
                head = 0;
            }
            size--;

            // 通知offer方法可以进行添加元素
            tailCondition.signal();

            return element;
        } finally {
            lock.unlock();
        }
    }

    /
     * 删除头部元素,有时间限制
     *
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return 头部元素
     * @throws InterruptedException 打断异常
     */
    @Override
    public T poll(long timeout, TimeUnit unit) throws InterruptedException {
        // 使用可打断的锁
        lock.lockInterruptibly();
        try {
            // 等待时间
            long time = unit.toNanos(timeout);
            // 如果队列已空,则进行等待
            while (isEmpty()) {
                // 如果等待时间已过,还没获得锁,则返回null
                if (time <= 0) {
                    return null;
                }
                // 等待时间需更新
                time = headCondition.awaitNanos(time);
            }

            // 取出头部元素
            T element = array[head];
            // help GC
            array[head] = null;
            // 如果head指针下标+1等于数组长度,则置为0
            if (++head == array.length) {
                head = 0;
            }
            size--;

            // 通知offer方法可以进行添加元素
            tailCondition.signal();

            return element;
        } finally {
            lock.unlock();
        }
    }

    /
     * 队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    @Override
    public String toString() {
        return Arrays.toString(array);
    }
}
package com.lzlg.study.algorithm.new2023.queue.block;

/
 * 测试阻塞队列
 *
 * @author lzlg
 * 2023/7/25 8:55
 */
public class TestBlockQueue {
    public static void main(String[] args) throws Exception {
        MyBlockQueue<String> queue = new DoubleLockBlockQueue<>(3);
        new Thread(() -> {
            try {
                queue.offer("work1");
                System.out.println(queue);
                queue.offer("work2");
                System.out.println(queue);
                queue.offer("work3");
                System.out.println(queue);
//                queue.offer("work4", 5000, TimeUnit.MILLISECONDS);
//                System.out.println(queue);
                queue.offer("work4");
                System.out.println(queue);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }, "t1").start();

//        TimeUnit.MILLISECONDS.sleep(2000);
        queue.poll();
    }
}
package com.lzlg.study.algorithm.new2023.queue;

import java.util.Iterator;

/
 * 使用环形数组实现队列,环形数组是个逻辑结构
 * 方式3的扩展: 使用头尾指针,但是头尾指针不再指向数组元素的下标,而是一直增长
 * 使用位运算代替取模运算
 * a = b * x + c (其中, a, b, c, x都是整数)
 * 当除数b是2的幂时, 可通过按位与 a & (b - 1) 得出余数c
 * 比如 13 = 4 * 3 + 1 而 4 = 2^2
 * 13    的二进制形式是 0000 1101
 * 4-1即3的二进制形式是 0000 0011
 * 按位与后是           0000 0001 即等于1也就是余数
 * <p>
 * 如果要使用上面方法,就必须保证数组的长度是2的幂
 *
 * @author lzlg
 * 2023/7/22 11:02
 */
public class FastThirdArrayQueue<T> implements MyQueue<T> {
    // 存储元素的数组
    private final T[] array;
    // 头指针
    private int head;
    // 尾指针
    private int tail;

    @SuppressWarnings("unchecked")
    public FastThirdArrayQueue(int capacity) {
        // 1.方式1: 传入的容量必须是2的幂,2的幂与其减1的数字按位与等于0
        // 比如8的二进制是  0000 1000
        // 8-1即7的二进制是 0000 0111
        // 按位与则是       0000 0000 即是0
        if ((capacity & (capacity - 1)) != 0) {
            throw new IllegalArgumentException("容量必须是2的幂");
        }
        // 2.方式2: 找到比传入的capacity大的2的幂
        // 使用对数运算,使用对数的换底公式
        // 需要减1,防止因为是capacity原本就是2的幂造成结果不正确
//        int n = ((int) (Math.log10(capacity - 1) / Math.log10(2))) + 1;
        // 创建数组,注意数组长度是容量
        array = (T[]) new Object[capacity];
        head = 0;
        tail = 0;
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // 如果队列已满,则不能添加
        if (isFull()) {
            return false;
        }
        // 使用(尾指针&数组长度-1)下标来存储元素
        array[tail & array.length - 1] = element;
        // 保证尾指针增长
        tail++;
        return true;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出(头指针&数组长度-1)下标的元素
        T element = array[head & array.length - 1];
        // 保证头指针增长
        head++;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出(头指针&数组长度-1)下标的元素
        return array[head & array.length - 1];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        // 当头尾相等时,则队列为空
        return head == tail;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        // 头尾的差值等于数组长度时,则队列已满
        return (tail - head) == array.length;
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            // 使用指针p辅助访问
            int p = head;

            @Override
            public boolean hasNext() {
                // 当p等于尾指针下标时,则已经访问完
                return p != tail;
            }

            @Override
            public T next() {
                T element = array[p & array.length - 1];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue;

import java.util.Iterator;

/
 * 使用环形数组实现队列,环形数组是个逻辑结构
 * 使用的是方式1: 使用头尾指针,并舍弃一个数组位置作为判空和判满的条件依据
 *
 * @author lzlg
 * 2023/7/22 11:02
 */
public class FirstArrayQueue<T> implements MyQueue<T> {
    // 存储元素的数组
    private final T[] array;
    // 头指针下标
    private int head;
    // 尾指针下标
    private int tail;

    @SuppressWarnings("unchecked")
    public FirstArrayQueue(int capacity) {
        // 创建数组,注意数组长度是容量+1,要留一个位置作为判空和判满的条件依据
        array = (T[]) new Object[capacity + 1];
        head = 0;
        tail = 0;
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // 如果队列已满,则不能添加
        if (isFull()) {
            return false;
        }
        // 使用尾指针下标存储元素
        array[tail] = element;
        // 移动尾指针,为防止数组越界,故%上数组长度
        tail = (tail + 1) % array.length;
        return true;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出头指针下标的元素
        T element = array[head];
        // 移动头指针,为防止数组越界,故%上数组长度
        head = (head + 1) % array.length;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出头指针下标的元素
        return array[head];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return (tail + 1) % array.length == head;
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            // 使用指针p辅助访问
            int p = head;

            @Override
            public boolean hasNext() {
                // 当p等于尾指针下标时,则已经访问完
                return p != tail;
            }

            @Override
            public T next() {
                T element = array[p];
                p = (p + 1) % array.length;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue;

import java.util.Iterator;

/
 * 使用单向循环哨兵链表实现队列
 *
 * @author lzlg
 * 2023/7/22 10:05
 */
public class MyLinkedListQueue<T> implements MyQueue<T> {
    // 头指针,始终指向哨兵节点
    private final Node<T> head;
    // 尾指针,指向最新插入的节点
    private Node<T> tail;

    // 队列元素个数
    private int size;
    // 队列容量
    private final int capacity;

    public MyLinkedListQueue() {
        this(Integer.MAX_VALUE);
    }

    public MyLinkedListQueue(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException("队列不能为空");
        }
        this.capacity = capacity;
        this.size = 0;
        // 创建头节点,作为哨兵节点
        this.head = new Node<>(null, null);
        // 将尾节点指向头节点
        this.tail = head;
        // 将尾节点的next指向头节点,构成环
        tail.next = head;
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // 如果队列已满,则不能添加
        if (isFull()) {
            return false;
        }
        // 创建新节点,新节点的next指向tail,新节点将作为尾节点
        Node<T> added = new Node<>(element, head);
        // 将新节点添加到尾部
        tail.next = added;
        // 移动尾指针,将新节点作为新的尾节点
        tail = added;
        size++;
        return true;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        if (isEmpty()) {
            return null;
        }
        // 找到第一个节点(非哨兵)
        Node<T> first = head.next;
        // 因为要删除,因此移动head的next指向first的next
        head.next = first.next;
        // 考虑到一个元素的情况,此时删除的是队尾节点,因此还需移动尾指针
        if (first == tail) {
            tail = head;
        }
        size--;
        // 返回元素
        return first.element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队列为空,则返回null,否则返回队头的next中的元素
        return isEmpty() ? null : head.next.element;
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == capacity;
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            Node<T> p = head.next;

            @Override
            public boolean hasNext() {
                return p != head;
            }

            @Override
            public T next() {
                T element = p.element;
                p = p.next;
                return element;
            }
        };
    }


    /
     * 链表节点类
     */
    private static class Node<T> {
        // 节点元素值
        T element;
        // 下个节点指针
        Node<T> next;

        public Node(T element, Node<T> next) {
            this.element = element;
            this.next = next;
        }
    }
}
package com.lzlg.study.algorithm.new2023.queue;

/
 * 自定义队列接口
 * 插入的一端为队尾,删除的一端为队头
 *
 * @author lzlg
 * 2023/7/22 10:00
 */
public interface MyQueue<T> extends Iterable<T> {

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    boolean offer(T element);

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    T poll();

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    T peek();

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    boolean isEmpty();

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    boolean isFull();
}
package com.lzlg.study.algorithm.new2023.queue.priority;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;

import java.util.Iterator;

/
 * 使用大顶堆实现优先级队列
 * 大顶堆的特性: 父节点的值总比左右孩子的大
 *
 * @author lzlg
 * 2023/7/24 11:23
 */
@SuppressWarnings("unchecked")
public class BigHeapPriorityQueue<T extends Priority> implements MyQueue<T> {
    // 数组
    private final Priority[] array;
    // 元素数量
    private int size;

    public BigHeapPriorityQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = new Priority[capacity];
        size = 0;
    }

    /
     * 队尾插入
     * 一个child下标指向数组中下标为size的位置,然后开始调整堆
     * 1.比较父节点的优先级,如果大于父节点的优先级,则父节点的下标下移到child指定的下标
     * 2.一直循环上述步骤,直到child为0或者遇到不大于父节点优先级时停止
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        if (isFull()) {
            return false;
        }
        // child指针位置,注意添加后size还要++,这里合并写在一起了
        int child = size++;
        // 找到父节点下标
        int parent = (child - 1) >>> 1;
        // 如果child>0且父节点的优先级小于待插入元素节点的优先级
        while (child > 0 && array[parent].priority() < element.priority()) {
            // 父节点的值赋值给子节点
            array[child] = array[parent];
            // 变更子节点和父节点
            child = parent;
            parent = (child - 1) >>> 1;
        }
        // 循环结束,此时child指针指向的是待插入元素的下标
        array[child] = element;
        return true;
    }

    /
     * 队头去除元素,并删除
     * 步骤如下:
     * 1.先把数组的第一个元素和最后一个元素交换
     * 2.移动size,即size--,优先级最大的则被移出(即返回值)
     * 3.开始调整堆
     * 1)从第一个元素(父节点)开始比较它的左右子节点的优先级
     * 2)如果父节点的优先级最大,则不用调整
     * 3)否则从左右子节点中选出优先级最大的和父节点进行交换
     * 4)直到到达数组尾部
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        if (isEmpty()) {
            return null;
        }
        // 交换数组第一个和最后一个,此时最后一个元素是优先级最大的元素
        swap(0, size - 1);
        // 减少元素数量
        size--;
        // 返回的优先级最大的元素
        T element = (T) array[size];
        // help GC
        array[size] = null;

        // 从根节点开始调整堆
        heapAdjust(0);

        return element;
    }

    /
     * 调整大顶堆
     *
     * @param parent 父节点下标
     */
    private void heapAdjust(int parent) {
        // 左孩子
        int leftChild = 2 * parent + 1;
        // 右孩子
        int rightChild = leftChild + 1;

        // 最大优先级
        int max = parent;
        if (leftChild < size && array[leftChild].priority() > array[max].priority()) {
            max = leftChild;
        }
        if (rightChild < size && array[rightChild].priority() > array[max].priority()) {
            max = rightChild;
        }
        // 如果最大的不是父节点,则进行交换
        if (max != parent) {
            swap(max, parent);
            // 再次递归调整剩下的堆
            heapAdjust(max);
        }
    }

    /
     * 使用临时变量交换
     *
     * @param i 下标1
     * @param j 下标2
     */
    private void swap(int i, int j) {
        Priority temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }


    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        // 数组中下标0存储的是优先级最大的
        return (T) array[0];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            int p = 0;

            @Override
            public boolean hasNext() {
                return p < size;
            }

            @Override
            public T next() {
                T element = (T) array[p];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue.priority;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;

import java.util.Iterator;

/
 * 使用无序数组实现优先级队列
 *
 * @author lzlg
 * 2023/7/24 11:23
 */
@SuppressWarnings("unchecked")
public class DisorderPriorityQueue<T extends Priority> implements MyQueue<T> {
    // 数组
    private final Priority[] array;
    // 元素数量
    private int size;

    public DisorderPriorityQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = new Priority[capacity];
        size = 0;
    }

    /
     * 队尾插入,由于是无序的,直接在数组尾部插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        if (isFull()) {
            return false;
        }
        array[size++] = element;
        return true;
    }

    /
     * 队头去除元素,并删除
     * 要先找到优先级最大的元素下标
     * 然后删除此元素
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        if (isEmpty()) {
            return null;
        }
        // 找到优先级最大的元素下标
        int maxPriorityIndex = maxPriorityIndex();
        T element = (T) array[maxPriorityIndex];
        // 删除此元素
        remove(maxPriorityIndex);
        return element;
    }

    /
     * 删除指定下标的元素
     *
     * @param index 下标
     */
    private void remove(int index) {
        // 如果在数组的最后一个,则不用移动元素
        if (index < size - 1) {
            // 移动数组元素
            System.arraycopy(array, index + 1, array, index, size - 1 - index);
        }
        size--;
        // help GC
        // 这里最后的元素置空就可以了
        // 在删除过程中会移动元素将优先级最高的元素覆盖了
        array[size] = null;
    }

    /
     * 获取最大优先级的数组下标
     *
     * @return 数组下标
     */
    private int maxPriorityIndex() {
        int max = 0;
        for (int i = 1; i < size; i++) {
            // 如果有优先级比max大的,则赋值给max
            if (array[i].priority() > array[max].priority()) {
                max = i;
            }
        }
        return max;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        // 找到优先级最大的元素下标
        int maxPriorityIndex = maxPriorityIndex();
        return (T) array[maxPriorityIndex];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            int p = 0;

            @Override
            public boolean hasNext() {
                return p < size;
            }

            @Override
            public T next() {
                T element = (T) array[p];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue.priority;

/
 * 使用小顶堆实现合并多个升序的链表
 *
 * @author lzlg
 * 2023/7/24 14:33
 */
public class MergeMultipleSortedLinkedList {

    /
     * 使用小顶堆实现合并多个升序的链表
     *
     * @param nodes 链表数组
     * @return 新链表
     */
    public static Node merge(Node[] nodes) {
        // 使用小顶堆的优先级队列
        SmallHeapPriorityQueue<Node> queue = new SmallHeapPriorityQueue<>(nodes.length);
        // 将节点的第一个节点加入到队列中
        for (Node node : nodes) {
            if (node != null) {
                queue.offer(node);
            }
        }

        // 使用哨兵节点
        Node sentinel = new Node(-1, null);
        // 总指向新链表最后一个元素的指针
        Node p = sentinel;
        while (!queue.isEmpty()) {
            // 把最小的取出,加入新链表的最后
            Node min = queue.poll();
            p.next = min;
            // 指针p始终指向新链表最后一个元素
            p = min;

            // 添加旧链表的下一个元素到队列中
            if (min.next != null) {
                queue.offer(min.next);
            }
        }

        return sentinel.next;
    }

    public static void main(String[] args) {
        Node node1 = new Node(1, new Node(3, new Node(4, null)));

        Node node2 = new Node(2, new Node(5, new Node(6, null)));

        Node node3 = new Node(1, new Node(4, new Node(7, null)));

        Node[] nodes = {node1, node2, node3};

        Node node = merge(nodes);
        printNode(node);
    }

    private static void printNode(Node node) {
        Node p = node;
        while (p != null) {
            System.out.println(p);
            p = p.next;
        }
    }

    /
     * 节点类
     */
    private static class Node implements Priority {

        private int value;

        private Node next;

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }

        /
         * 返回优先级数值
         *
         * @return 优先级
         */
        @Override
        public int priority() {
            return value;
        }

        @Override
        public String toString() {
            return "Node[" + value + "]";
        }
    }
}
package com.lzlg.study.algorithm.new2023.queue.priority;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;

import java.util.Iterator;

/
 * 使用有序数组实现优先级队列
 *
 * @author lzlg
 * 2023/7/24 11:23
 */
@SuppressWarnings("unchecked")
public class OrderPriorityQueue<T extends Priority> implements MyQueue<T> {
    // 数组
    private final Priority[] array;
    // 元素数量
    private int size;

    public OrderPriorityQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = new Priority[capacity];
        size = 0;
    }

    /
     * 队尾插入,因为是有序的,因此要找到插入的位置下标
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        if (isFull()) {
            return false;
        }
        // 从后往前遍历
        int i = size - 1;
        // 找到小于等于待插入元素优先级的下标
        while (i >= 0 && array[i].priority() > element.priority()) {
            // 移动数组元素
            array[i + 1] = array[i];
            i--;
        }
        // 找到后,进行插入
        array[i + 1] = element;
        size++;
        return true;
    }


    /
     * 队头去除元素,并删除
     * 因为是有序的,因此直接删除数组的最后一个即可
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        if (isEmpty()) {
            return null;
        }
        T element = (T) array[size - 1];
        size--;
        // help GC
        array[size] = null;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        return (T) array[size - 1];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            int p = 0;

            @Override
            public boolean hasNext() {
                return p < size;
            }

            @Override
            public T next() {
                T element = (T) array[p];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue.priority;

/
 * 优先级接口
 *
 * @author lzlg
 * 2023/7/24 11:23
 */
public interface Priority {
    /
     * 返回优先级数值
     *
     * @return 优先级
     */
    int priority();
}
package com.lzlg.study.algorithm.new2023.queue.priority;

import com.lzlg.study.algorithm.new2023.queue.MyQueue;

import java.util.Iterator;

/
 * 使用小顶堆实现优先级队列
 * 小顶堆的特性: 父节点的值总比左右孩子的小
 *
 * @author lzlg
 * 2023/7/24 11:23
 */
@SuppressWarnings("unchecked")
public class SmallHeapPriorityQueue<T extends Priority> implements MyQueue<T> {
    // 数组
    private final Priority[] array;
    // 元素数量
    private int size;

    public SmallHeapPriorityQueue(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = new Priority[capacity];
        size = 0;
    }

    /
     * 队尾插入
     * 一个child下标指向数组中下标为size的位置,然后开始调整堆
     * 1.比较父节点的优先级,如果小于父节点的优先级,则父节点的下标下移到child指定的下标
     * 2.一直循环上述步骤,直到child为0或者遇到不小于父节点优先级时停止
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        if (isFull()) {
            return false;
        }
        // child指针位置,注意添加后size还要++,这里合并写在一起了
        int child = size++;
        // 找到父节点下标
        int parent = (child - 1) >>> 1;
        // 如果child>0且父节点的优先级大于待插入元素节点的优先级
        while (child > 0 && array[parent].priority() > element.priority()) {
            // 父节点的值赋值给子节点
            array[child] = array[parent];
            // 变更子节点和父节点
            child = parent;
            parent = (child - 1) >>> 1;
        }
        // 循环结束,此时child指针指向的是待插入元素的下标
        array[child] = element;
        return true;
    }

    /
     * 队头去除元素,并删除
     * 步骤如下:
     * 1.先把数组的第一个元素和最后一个元素交换
     * 2.移动size,即size--,优先级最小的则被移出(即返回值)
     * 3.开始调整堆
     * 1)从第一个元素(父节点)开始比较它的左右子节点的优先级
     * 2)如果父节点的优先级最小,则不用调整
     * 3)否则从左右子节点中选出优先级最小的和父节点进行交换
     * 4)直到到达数组尾部
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        if (isEmpty()) {
            return null;
        }
        // 交换数组第一个和最后一个,此时最后一个元素是优先级最大的元素
        swap(0, size - 1);
        // 减少元素数量
        size--;
        // 返回的优先级最大的元素
        T element = (T) array[size];
        // help GC
        array[size] = null;

        // 从根节点开始调整堆
        heapAdjust(0);

        return element;
    }

    /
     * 调整大顶堆
     *
     * @param parent 父节点下标
     */
    private void heapAdjust(int parent) {
        // 左孩子
        int leftChild = 2 * parent + 1;
        // 右孩子
        int rightChild = leftChild + 1;

        // 最小优先级
        int min = parent;
        if (leftChild < size && array[leftChild].priority() < array[min].priority()) {
            min = leftChild;
        }
        if (rightChild < size && array[rightChild].priority() < array[min].priority()) {
            min = rightChild;
        }
        // 如果最小的不是父节点,则进行交换
        if (min != parent) {
            swap(min, parent);
            // 再次递归调整剩下的堆
            heapAdjust(min);
        }
    }

    /
     * 使用临时变量交换
     *
     * @param i 下标1
     * @param j 下标2
     */
    private void swap(int i, int j) {
        Priority temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }


    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        // 数组中下标0存储的是优先级最大的
        return (T) array[0];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            int p = 0;

            @Override
            public boolean hasNext() {
                return p < size;
            }

            @Override
            public T next() {
                T element = (T) array[p];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue.priority;

/
 * 测试优先级队列
 *
 * @author lzlg
 * 2023/7/24 11:53
 */
public class TestPriorityQueue {

    public static void main(String[] args) {
        BigHeapPriorityQueue<Entry> queue = new BigHeapPriorityQueue<>(4);

        queue.offer(new Entry("work1", 5));
        queue.offer(new Entry("work2", 2));
        queue.offer(new Entry("work3", 8));
        queue.offer(new Entry("work4", 1));

        System.out.println("=================");
        for (Entry entry : queue) {
            System.out.println(entry);
        }
        System.out.println("=================");
        System.out.println(queue.poll());
        System.out.println("=================");
        System.out.println(queue.peek());

    }

    /
     * 实体类
     */
    private static class Entry implements Priority {

        private final String value;

        private final int priority;

        public Entry(String value, int priority) {
            this.value = value;
            this.priority = priority;
        }

        /
         * 返回优先级数值
         *
         * @return 优先级
         */
        @Override
        public int priority() {
            return priority;
        }

        @Override
        public String toString() {
            return value + ", 优先级: " + priority;
        }
    }
}
package com.lzlg.study.algorithm.new2023.queue;

import java.util.Iterator;

/
 * 使用环形数组实现队列,环形数组是个逻辑结构
 * 使用的是方式2: 使用头尾指针,使用一个表示数组实际元素数量的size
 *
 * @author lzlg
 * 2023/7/22 11:02
 */
public class SecondArrayQueue<T> implements MyQueue<T> {
    // 存储元素的数组
    private final T[] array;
    // 头指针下标
    private int head;
    // 尾指针下标
    private int tail;
    // 表示数组元素的实际数量
    private int size;

    @SuppressWarnings("unchecked")
    public SecondArrayQueue(int capacity) {
        // 创建数组,注意数组长度是容量,因为使用了size
        array = (T[]) new Object[capacity];
        head = 0;
        tail = 0;
        size = 0;
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // 如果队列已满,则不能添加
        if (isFull()) {
            return false;
        }
        // 使用尾指针下标存储元素
        array[tail] = element;
        // 移动尾指针,为防止数组越界,故%上数组长度
        tail = (tail + 1) % array.length;
        size++;
        return true;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出头指针下标的元素
        T element = array[head];
        // 移动头指针,为防止数组越界,故%上数组长度
        head = (head + 1) % array.length;
        size--;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出头指针下标的元素
        return array[head];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == array.length;
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            // 使用指针p辅助访问
            int p = head;
            // 使用计数器
            int count = 0;

            @Override
            public boolean hasNext() {
                // 计数器小于size时,则还能访问
                return count < size;
            }

            @Override
            public T next() {
                T element = array[p];
                p = (p + 1) % array.length;
                // 访问一个,则计数器增加
                count++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue;

/
 * @author lzlg
 * 2023/7/22 10:22
 */
public class TestMyQueue {
    public static void main(String[] args) {
        MyQueue<Integer> queue = new ThirdArrayQueue<>(2);
        queue.offer(1);
        System.out.println(queue.peek());
        queue.offer(2);
        queue.poll();
        queue.offer(3);

        System.out.println("=====删除队头元素====");
        System.out.println(queue.peek());
        System.out.println("=======队列元素======");
        for (Integer ele : queue) {
            System.out.println(ele);
        }
    }

    /
     * 测试链表实现的队列
     */
    private static void testLinkedListQueue() {
        MyQueue<Integer> queue = new MyLinkedListQueue<>(2);
        queue.offer(1);
        System.out.println(queue.peek());
        queue.offer(2);
        queue.poll();
        queue.offer(3);

        System.out.println("=====删除队头元素====");
        System.out.println(queue.peek());
        System.out.println("=======队列元素======");
        for (Integer ele : queue) {
            System.out.println(ele);
        }
    }
}```
```java
package com.lzlg.study.algorithm.new2023.queue;

import java.util.Iterator;

/
 * 使用环形数组实现队列,环形数组是个逻辑结构
 * 使用的是方式3: 使用头尾指针,但是头尾指针不再指向数组元素的下标,而是一直增长
 * 这种方式有可能导致head和tail超出int的最大值范围,使用long类型的头尾指针比较好
 *
 * @author lzlg
 * 2023/7/22 11:02
 */
public class ThirdArrayQueue<T> implements MyQueue<T> {
    // 存储元素的数组
    private final T[] array;
    // 头指针
    private int head;
    // 尾指针
    private int tail;

    @SuppressWarnings("unchecked")
    public ThirdArrayQueue(int capacity) {
        // 创建数组,注意数组长度是容量
        array = (T[]) new Object[capacity];
        head = 0;
        tail = 0;
    }

    /
     * 队尾插入
     *
     * @param element 待插入的元素
     * @return 是否插入成功
     */
    @Override
    public boolean offer(T element) {
        // 如果队列已满,则不能添加
        if (isFull()) {
            return false;
        }
        // 使用(尾指针%数组长度)下标来存储元素
        array[tail % array.length] = element;
        // 保证尾指针增长
        tail++;
        return true;
    }

    /
     * 队头去除元素,并删除
     *
     * @return 队头元素
     */
    @Override
    public T poll() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出(头指针%数组长度)下标的元素
        T element = array[head % array.length];
        // 保证头指针增长
        head++;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peek() {
        // 如果队列已空,则返回null
        if (isEmpty()) {
            return null;
        }
        // 取出(头指针%数组长度)下标的元素
        return array[head % array.length];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        // 当头尾相等时,则队列为空
        return head == tail;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        // 头尾的差值等于数组长度时,则队列已满
        return (tail - head) == array.length;
    }

    /
     * 迭代器实现
     *
     * @return 迭代器
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            // 使用指针p辅助访问
            int p = head;

            @Override
            public boolean hasNext() {
                // 当p等于尾指针下标时,则已经访问完
                return p != tail;
            }

            @Override
            public T next() {
                T element = array[p % array.length];
                p++;
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.queue;

import java.util.ArrayList;
import java.util.List;

/
 * 队列使用: 二叉树的层序遍历
 * 如果有如下二叉树
 * 1
 * /\
 * 2  3
 * /\  /\
 * 4 5 6 7
 * 则层序遍历的结果为1, 2, 3, 4, 5, 6, 7
 *
 * @author lzlg
 * 2023/7/22 12:17
 */
public class TreeLevelForEachQuiz {
    /
     * 层序遍历二叉树
     * 使用一个队列
     * 1.开始时,将根节点加入队列
     * 2.开始循环,取出队列的头部节点,访问此节点的元素
     * 3.判断该节点如果有左右孩子,则分别加入队列中
     * 4.直到队列为空,退出循环
     *
     * @param root 二叉树根节点
     */
    public static void levelLoopTree(TreeNode root) {
        // 创建队列
        MyQueue<TreeNode> queue = new MyLinkedListQueue<>();
        // 将根节点加入队列
        queue.offer(root);

        while (!queue.isEmpty()) {
            // 取出节点
            TreeNode node = queue.poll();
            // 访问该节点
            System.out.println(node);

            // 如果有左右孩子节点,则分别加入队列中
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

    /
     * 将二叉树的每层变成一个列表,并把这所有列表组合成一个列表返回
     *
     * @param root 二叉树根节点
     * @return 层序列表
     */
    public static List<List<Integer>> toLevelList(TreeNode root) {
        // 返回结果
        List<List<Integer>> result = new ArrayList<>();
        // 当二叉树为空,则直接返回空list
        if (root == null) {
            return result;
        }

        // 创建队列
        MyQueue<TreeNode> queue = new MyLinkedListQueue<>();
        // 将根节点加入队列
        queue.offer(root);

        // 当前二叉树层的节点数量,初始为1,因为先遍历根节点,根节点这一层只有一个
        int currentLevelNodeCount = 1;


        while (!queue.isEmpty()) {
            // 下个层序的节点数量
            int nextLevelNodeCount = 0;

            // 当前层序的列表
            List<Integer> levelList = new ArrayList<>();
            // 根据当前层的节点数量分别取出
            for (int i = 0; i < currentLevelNodeCount; i++) {
                // 取出节点
                TreeNode node = queue.poll();
                // 将此节点的值加入到当前层序的列表
                levelList.add(node.value);

                // 如果有左右孩子节点,则分别加入队列中
                if (node.left != null) {
                    queue.offer(node.left);
                    // 下个层序的节点数量增加
                    nextLevelNodeCount++;
                }
                if (node.right != null) {
                    queue.offer(node.right);
                    // 下个层序的节点数量增加
                    nextLevelNodeCount++;
                }
            }

            // 当前层序遍历完成,则添加到返回结果中
            result.add(levelList);

            // 切换当前层序的数量为下个层序的数量,进入下次循环
            // 下次循环就是下个层次的遍历了
            currentLevelNodeCount = nextLevelNodeCount;
        }

        // 返回最终结果
        return result;
    }

    public static void main(String[] args) {
        // 创建二叉树
        TreeNode root = new TreeNode(
                1,
                new TreeNode(2, new TreeNode(4), new TreeNode(5)),
                new TreeNode(3, new TreeNode(6), new TreeNode(7))
        );

        levelLoopTree(root);
        System.out.println("=======层序遍历的列表=======");
        System.out.println(toLevelList(root));
    }

    /
     * 二叉树节点类
     */
    private static class TreeNode {
        // 节点值
        int value;
        // 节点左孩子
        private TreeNode left;
        // 节点右孩子
        private TreeNode right;

        public TreeNode(int value) {
            this.value = value;
        }

        public TreeNode(int value, TreeNode left, TreeNode right) {
            this.value = value;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return String.valueOf(value);
        }
    }
}

双端队列

package com.lzlg.study.algorithm.new2023.deque;

import java.util.Iterator;

/
 * 使用数组实现双端队列
 * 使用两个指针,头指针,尾指针
 * 此方式会牺牲一个数组位置
 * <p>
 * 创建后情况:
 * 指针  h
 * t
 * 下标: 0 1 2 3
 * <p>
 * 头部添加一个元素a:
 * 指针        h
 * t
 * 下标: 0 1 2 3
 * 元素:       a
 * <p>
 * 尾部添加一个元素b:
 * 指针        h
 * t
 * 下标: 0 1 2 3
 * 元素: b     a
 *
 * @author lzlg
 * 2023/7/24 9:59
 */
public class FirstArrayDeque<T> implements MyDeque<T> {
    // 头指针
    private int head;
    // 尾指针
    private int tail;
    // 存储元素的数组
    private final T[] array;

    @SuppressWarnings("unchecked")
    public FirstArrayDeque(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        array = (T[]) new Object[capacity + 1];
        head = 0;
        tail = 0;
    }

    /
     * 增加索引的方法
     *
     * @param index  当前索引
     * @param length 数组长度
     * @return 增加后的索引
     */
    private int indexIncrease(int index, int length) {
        // 如果索引+1大于等于数组长度,则返回下标为0的位置(数组头)
        if (index + 1 >= length) {
            return 0;
        }
        // 否则直接加1
        return index + 1;
    }

    /
     * 减少索引的方法
     *
     * @param index  当前索引
     * @param length 数组长度
     * @return 减少后的索引
     */
    private int indexDecrease(int index, int length) {
        // 如果索引-1小于0,则返回下标为数组长度-1的位置(数组尾)
        if (index - 1 < 0) {
            return length - 1;
        }
        return index - 1;
    }

    /
     * 队头添加元素
     * 先head减少,然后根据head指针下标添加到数组中
     *
     * @param element 元素
     * @return 结果
     */
    @Override
    public boolean offerFirst(T element) {
        if (isFull()) {
            return false;
        }
        // head指针减少
        head = indexDecrease(head, array.length);
        array[head] = element;
        return true;
    }

    /
     * 队尾添加元素
     * 把元素添加到tail指向的下标位置,然后tail增加
     *
     * @param element 元素
     * @return 结果
     */
    @Override
    public boolean offerLast(T element) {
        if (isFull()) {
            return false;
        }
        // 添加元素
        array[tail] = element;
        // 增加tail下标
        tail = indexIncrease(tail, array.length);
        return true;
    }

    /
     * 删除队头元素
     * 先取出头部元素,然后增加head
     *
     * @return 队头元素
     */
    @Override
    public T pollFirst() {
        if (isEmpty()) {
            return null;
        }
        // 先取出头部元素
        T element = array[head];
        // help GC
        array[head] = null;
        head = indexIncrease(head, array.length);
        return element;
    }

    /
     * 删除队尾元素
     * 先减少tail,然后取出元素
     *
     * @return 队头元素
     */
    @Override
    public T pollLast() {
        if (isEmpty()) {
            return null;
        }
        tail = indexDecrease(tail, array.length);
        T element = array[tail];
        // help GC
        array[tail] = null;
        return element;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peekFirst() {
        if (isEmpty()) {
            return null;
        }
        return array[head];
    }

    /
     * 查看队尾元素
     *
     * @return 队头元素
     */
    @Override
    public T peekLast() {
        if (isEmpty()) {
            return null;
        }
        return array[indexDecrease(tail, array.length)];
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    /
     * 判断队列是否已满
     * 分为三种情况:
     * 1.tail>head时,此时判断tail-head是否等于数组长度-1
     * 2.tail<head时,此时判断head-tail是否等于1
     * 3.tail=head时,队列为空
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        if (tail > head) {
            return tail - head == array.length - 1;
        }
        if (tail < head) {
            return head - tail == 1;
        }
        return false;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            int p = head;

            @Override
            public boolean hasNext() {
                return p != tail;
            }

            @Override
            public T next() {
                T element = array[p];
                p = indexIncrease(p, array.length);
                return element;
            }
        };
    }
}
package com.lzlg.study.algorithm.new2023.deque;

import java.util.Iterator;

/
 * 使用双向环形链表实现双端队列
 * 使用哨兵节点作为头和尾节点
 *
 * @author lzlg
 * 2023/7/24 10:23
 */
public class LinkedListDeque<T> implements MyDeque<T> {
    // 哨兵节点
    private final Node<T> sentinel;
    // 队列容量
    private int capacity;
    // 元素数量
    private int size;

    public LinkedListDeque(int capacity) {
        if (capacity < 1) {
            throw new IllegalArgumentException("队列不能为空");
        }
        this.capacity = capacity;
        size = 0;
        // 初始化哨兵节点
        sentinel = new Node<>(null, null, null);
        // 头节点(还是哨兵节点)的是哨兵节点
        sentinel.next = sentinel;
        // 尾节点(还是哨兵节点)的是哨兵节点
        sentinel.prev = sentinel;
    }

    /
     * 队头添加元素
     *
     * @param element 元素
     * @return 结果
     */
    @Override
    public boolean offerFirst(T element) {
        if (isFull()) {
            return false;
        }
        // 新节点的前一个节点是哨兵节点
        Node<T> prev = sentinel;
        // 新节点的后一个节点是哨兵节点的next
        Node<T> next = sentinel.next;
        // 创建新节点
        Node<T> added = new Node<>(prev, element, next);
        // 前节点的next是新节点
        prev.next = added;
        // 后节点的prev是新节点
        next.prev = added;

        size++;
        return true;
    }

    /
     * 队尾添加元素
     *
     * @param element 元素
     * @return 结果
     */
    @Override
    public boolean offerLast(T element) {
        if (isFull()) {
            return false;
        }
        // 新节点的前一个节点是尾节点的prev
        Node<T> prev = sentinel.prev;
        // 新节点的后一个节点是尾节点
        Node<T> next = sentinel;
        // 创建新节点
        Node<T> added = new Node<>(prev, element, next);
        // 前节点的next是新节点
        prev.next = added;
        // 后节点的prev是新节点
        next.prev = added;

        size++;
        return true;
    }

    /
     * 删除队头元素
     *
     * @return 队头元素
     */
    @Override
    public T pollFirst() {
        if (isEmpty()) {
            return null;
        }
        // 待删除节点的前一个节点
        Node<T> prev = sentinel;
        // 待删除节点是哨兵节点的next
        Node<T> removed = sentinel.next;
        // 待删除记得的后一个节点
        Node<T> next = removed.next;

        // 删除待删除节点
        prev.next = next;
        next.prev = prev;
        // helpGC
        removed.prev = null;
        removed.next = null;

        size--;
        return removed.value;
    }

    /
     * 删除队尾元素
     *
     * @return 队头元素
     */
    @Override
    public T pollLast() {
        if (isEmpty()) {
            return null;
        }
        // 待删除节点的下一个节点
        Node<T> next = sentinel;
        // 待删除节点是尾节点的前一个节点
        Node<T> removed = sentinel.prev;
        // 待删除节点的上一个节点
        Node<T> prev = removed.prev;

        // 删除待删除节点
        prev.next = next;
        next.prev = prev;
        // helpGC
        removed.prev = null;
        removed.next = null;

        size--;
        return removed.value;
    }

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    @Override
    public T peekFirst() {
        if (isEmpty()) {
            return null;
        }
        return sentinel.next.value;
    }

    /
     * 查看队尾元素
     *
     * @return 队头元素
     */
    @Override
    public T peekLast() {
        if (isEmpty()) {
            return null;
        }
        return sentinel.prev.value;
    }

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    @Override
    public boolean isFull() {
        return size == capacity;
    }

    /
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            Node<T> p = sentinel.next;

            @Override
            public boolean hasNext() {
                return p != sentinel;
            }

            @Override
            public T next() {
                T element = p.value;
                p = p.next;
                return element;
            }
        };
    }

    /
     * 节点类
     */
    private static class Node<T> {
        // 前节点指针
        private Node<T> prev;
        // 元素值
        private T value;
        // 后节点指针
        private Node<T> next;

        public Node(Node<T> prev, T value, Node<T> next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
}
package com.lzlg.study.algorithm.new2023.deque;

/
 * 双端队列
 * 能在两头进行添加删除操作的队列
 *
 * @author lzlg
 * 2023/7/24 9:54
 */
public interface MyDeque<T> extends Iterable<T> {
    /
     * 队头添加元素
     *
     * @param element 元素
     * @return 结果
     */
    boolean offerFirst(T element);

    /
     * 队尾添加元素
     *
     * @param element 元素
     * @return 结果
     */
    boolean offerLast(T element);

    /
     * 删除队头元素
     *
     * @return 队头元素
     */
    T pollFirst();

    /
     * 删除队尾元素
     *
     * @return 队头元素
     */
    T pollLast();

    /
     * 查看队头元素
     *
     * @return 队头元素
     */
    T peekFirst();

    /
     * 查看队尾元素
     *
     * @return 队头元素
     */
    T peekLast();

    /
     * 判断队列是否为空
     *
     * @return 结果
     */
    boolean isEmpty();

    /
     * 判断队列是否已满
     *
     * @return 结果
     */
    boolean isFull();
}
package com.lzlg.study.algorithm.new2023.deque;

/
 * @author lzlg
 * 2023/7/24 10:19
 */
public class MyDequeTest {
    public static void main(String[] args) {
        MyDeque<Integer> deque = new LinkedListDeque<>(4);
        deque.offerFirst(1);
        deque.offerFirst(2);
        deque.offerLast(3);
        deque.offerLast(4);

        for (Integer element : deque) {
            System.out.println(element);
        }
        System.out.println("===============");
        System.out.println(deque.peekFirst());
        System.out.println("===============");
        System.out.println(deque.peekLast());

        System.out.println("===============");
        System.out.println(deque.pollFirst());
        System.out.println("===============");
        System.out.println(deque.pollLast());

        System.out.println("===============");
        for (Integer element : deque) {
            System.out.println(element);
        }

        System.out.println("===============");
        System.out.println(deque.pollFirst());
        System.out.println("===============");
        System.out.println(deque.pollLast());

    }
}
package com.lzlg.study.algorithm.new2023.deque.quiz;

import com.lzlg.study.algorithm.new2023.queue.MyLinkedListQueue;
import com.lzlg.study.algorithm.new2023.queue.MyQueue;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/
 * 双端队列使用: 二叉树的z字层序遍历
 * 如果有如下二叉树
 * 1
 * /\
 * 2  3
 * /\  /\
 * 4 5 6 7
 * 则z字层序遍历的结果为1, 3, 2, 4, 5, 6, 7
 * 即奇数层从前往后,偶数层从后往前
 *
 * @author lzlg
 * 2023/7/22 12:17
 */
public class TreeLevelZForEachQuiz {
    /
     * 层序遍历二叉树
     * 使用一个队列
     * 1.开始时,将根节点加入队列
     * 2.开始循环,取出队列的头部节点,访问此节点的元素
     * 3.判断该节点如果有左右孩子,则分别加入队列中
     * 4.直到队列为空,退出循环
     *
     * @param root 二叉树根节点
     */
    public static void levelLoopTree(TreeNode root) {
        // 创建队列
        MyQueue<TreeNode> queue = new MyLinkedListQueue<>();
        // 将根节点加入队列
        queue.offer(root);

        while (!queue.isEmpty()) {
            // 取出节点
            TreeNode node = queue.poll();
            // 访问该节点
            System.out.println(node);

            // 如果有左右孩子节点,则分别加入队列中
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

    /
     * 将二叉树的每层变成一个列表,并把这所有列表组合成一个列表返回
     *
     * @param root 二叉树根节点
     * @return 层序列表
     */
    public static List<List<Integer>> toLevelList(TreeNode root) {
        // 返回结果
        List<List<Integer>> result = new ArrayList<>();
        // 当二叉树为空,则直接返回空list
        if (root == null) {
            return result;
        }

        // 创建队列
        MyQueue<TreeNode> queue = new MyLinkedListQueue<>();
        // 将根节点加入队列
        queue.offer(root);

        // 当前二叉树层的节点数量,初始为1,因为先遍历根节点,根节点这一层只有一个
        int currentLevelNodeCount = 1;

        // 判断当前层数是否是奇数
        boolean odd = true;

        while (!queue.isEmpty()) {
            // 下个层序的节点数量
            int nextLevelNodeCount = 0;

            // 当前层序的列表,这里使用实现双端队列接口的LinkedList
            LinkedList<Integer> levelList = new LinkedList<>();
            // 根据当前层的节点数量分别取出
            for (int i = 0; i < currentLevelNodeCount; i++) {
                // 取出节点
                TreeNode node = queue.poll();
                // 如果是奇数,将此节点的值加入到当前层序的列表的最后
                if (odd) {
                    levelList.offerLast(node.value);
                } else {// 如果是偶数,则添加到头部
                    levelList.offerFirst(node.value);
                }

                // 如果有左右孩子节点,则分别加入队列中
                if (node.left != null) {
                    queue.offer(node.left);
                    // 下个层序的节点数量增加
                    nextLevelNodeCount++;
                }
                if (node.right != null) {
                    queue.offer(node.right);
                    // 下个层序的节点数量增加
                    nextLevelNodeCount++;
                }
            }
            // 每次循环完成,奇数变偶数
            odd = !odd;

            // 当前层序遍历完成,则添加到返回结果中
            result.add(levelList);

            // 切换当前层序的数量为下个层序的数量,进入下次循环
            // 下次循环就是下个层次的遍历了
            currentLevelNodeCount = nextLevelNodeCount;
        }

        // 返回最终结果
        return result;
    }

    public static void main(String[] args) {
        // 创建二叉树
        TreeNode root = new TreeNode(
                1,
                new TreeNode(2,
                        new TreeNode(4, new TreeNode(8), new TreeNode(9)),
                        new TreeNode(5, new TreeNode(10), new TreeNode(11))
                ),
                new TreeNode(3, new TreeNode(6), new TreeNode(7))
        );

        levelLoopTree(root);
        System.out.println("=======Z字层序遍历的列表=======");
        System.out.println(toLevelList(root));
    }

    /
     * 二叉树节点类
     */
    private static class TreeNode {
        // 节点值
        int value;
        // 节点左孩子
        private TreeNode left;
        // 节点右孩子
        private TreeNode right;

        public TreeNode(int value) {
            this.value = value;
        }

        public TreeNode(int value, TreeNode left, TreeNode right) {
            this.value = value;
            this.left = left;
            this.right = right;
        }

        @Override
        public String toString() {
            return String.valueOf(value);
        }
    }
}

链表

package com.lzlg.study.algorithm.new2023.linkedlist;

import java.util.Iterator;

/
 * 双向循环链表,带哨兵节点
 *
 * @author lzlg
 * 2023/7/18 12:55
 */
public class DoubleCycleLinkedList implements Iterable<Integer> {

    private final Node sentinel;

    public DoubleCycleLinkedList() {
        // 哨兵节点
        this.sentinel = new Node(null, Integer.MAX_VALUE, null);
        // 形成环
        sentinel.prev = sentinel;
        sentinel.next = sentinel;
    }

    /
     * 头插法
     *
     * @param element 带插入的元素
     */
    public void addFirst(int element) {
        // 待插入节点的前一个节点是哨兵节点
        Node prev = sentinel;
        // 待插入节点的后一个节点是哨兵节点的下一个节点
        Node next = sentinel.next;

        // 插入新节点
        Node added = new Node(prev, element, next);
        // 前一个节点的下一个节点指向新插入的节点
        prev.next = added;
        // 后一个节点的上一个节点指向新插入的节点
        next.prev = added;
    }

    /
     * 删除第一个元素
     */
    public void removeFirst() {
        // 待删除的节点的哨兵节点的后节点
        Node removed = sentinel.next;
        if (removed == sentinel) {
            throw new IllegalArgumentException("链表为空");
        }

        // 待删除节点的前一个节点是哨兵节点
        Node prev = sentinel;
        // 待删除节点的后一个节点
        Node next = removed.next;

        // 将前一个节点的后节点指向下一个节点
        prev.next = next;
        // 将下一个节点的前节点指向前一个节点
        next.prev = prev;

        // 将删除的节点的前后指针置空,方便GC
        removed.prev = null;
        removed.next = null;
    }

    /
     * 尾插法
     *
     * @param element 带插入的元素
     */
    public void addLast(int element) {
        // 待插入节点的前一个节点是哨兵节点的上一个节点
        Node prev = sentinel.prev;
        // 待插入节点的后一个节点是哨兵节点
        Node next = sentinel;

        // 插入新节点
        Node added = new Node(prev, element, next);
        // 前一个节点的下一个节点指向新插入的节点
        prev.next = added;
        // 后一个节点的上一个节点指向新插入的节点
        next.prev = added;
    }

    /
     * 删除最后一个元素
     */
    public void removeLast() {
        // 待删除的节点的哨兵节点的前节点
        Node removed = sentinel.prev;
        if (removed == sentinel) {
            throw new IllegalArgumentException("链表为空");
        }

        // 待删除节点的前一个节点
        Node prev = removed.prev;
        // 待删除节点的后一个节点是哨兵节点
        Node next = sentinel;

        // 将前一个节点的后节点指向下一个节点
        prev.next = next;
        // 将下一个节点的前节点指向前一个节点
        next.prev = prev;

        // 将删除的节点的前后指针置空,方便GC
        removed.prev = null;
        removed.next = null;
    }

    /
     * 删除指定下标的元素
     *
     * @param index 指定下标
     */
    public void remove(int index) {
        // 首先找到待删除的节点
        Node removed = findNode(index);
        if (removed == null) {
            throw new IllegalArgumentException("索引非法");
        }

        // 待删除节点的前后节点
        Node prev = removed.prev;
        Node next = removed.next;

        // 切换指针
        prev.next = next;
        next.prev = prev;

        // 将删除的节点的前后指针置空,方便GC
        removed.prev = null;
        removed.next = null;
    }

    /
     * 根据索引找到指定的节点
     *
     * @param index 索引
     * @return 节点
     */
    private Node findNode(int index) {
        int i = 0;
        for (Node p = sentinel.next; p != sentinel; p = p.next, i++) {
            if (i == index) {
                return p;
            }
        }
        return null;
    }

    /
     * 使用迭代器遍历
     */
    @Override
    public Iterator<Integer> iterator() {
        return new NodeIterator();
    }

    /
     * 迭代器
     */
    private class NodeIterator implements Iterator<Integer> {

        Node p = sentinel.next;

        @Override
        public boolean hasNext() {
            return p != sentinel;
        }

        @Override
        public Integer next() {
            int element = p.value;
            p = p.next;
            return element;
        }
    }

    static class Node {
        // 前指针
        Node prev;
        // 节点的元素值
        int value;
        // 后指针
        Node next;

        public Node(Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
}
package com.lzlg.study.algorithm.new2023.linkedlist;

/
 * 双向循环链表,带哨兵节点
 *
 * @author lzlg
 * 2023/7/18 12:55
 */
public class DoubleCycleLinkedListTest {
    public static void main(String[] args) {
        DoubleCycleLinkedList list = new DoubleCycleLinkedList();
        list.addFirst(1);
        list.addFirst(2);
        list.addFirst(3);
        list.addFirst(4);
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }
        list =  new DoubleCycleLinkedList();
        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }

        list.removeFirst();
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }
        list.removeFirst();
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }

        list.removeLast();
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }
        list.removeLast();
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }

        list =  new DoubleCycleLinkedList();
        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);

        list.remove(4);
        System.out.println("============");
        for (Integer element : list) {
            System.out.println(element);
        }
    }

}
package com.lzlg.study.algorithm.new2023.linkedlist;

import java.util.Iterator;
import java.util.function.IntConsumer;

/
 * 双向链表,带头尾哨兵
 *
 * @author lzlg
 * 2023/7/18 12:05
 */
public class DoubleLinkedList implements Iterable<Integer> {
    // 头指针-头哨兵
    private final Node head;
    // 尾指针-尾哨兵
    private final Node tail;

    public DoubleLinkedList() {
        // 创建头尾指针
        this.head = new Node(null, Integer.MAX_VALUE, null);
        this.tail = new Node(null, Integer.MIN_VALUE, null);
        // 将头指针的下个节点指向尾节点
        head.next = tail;
        // 将尾指针的上个节点指向头节点
        tail.prev = head;
    }

    /
     * 获取索引下标的节点的元素值
     *
     * @param index 索引下标
     * @return 元素值
     */
    public int get(int index) {
        Node node = findNode(index);
        if (node == null) {
            throw new IllegalArgumentException("索引非法");
        }
        return node.value;
    }

    /
     * 在指定下标位置插入元素
     *
     * @param index   指定下标
     * @param element 待插入的元素
     */
    public void insert(int index, int element) {
        // 首先找到要插入的索引下标节点的前一个节点
        Node prev = findNode(index - 1);
        if (prev == null) {
            throw new IllegalArgumentException("索引非法");
        }
        // 前一个节点的下个节点是待插入节点的下个节点
        Node next = prev.next;
        // 插入新节点,且设置新节点的前一个节点和后一个节点
        Node insertNode = new Node(prev, element, next);
        // 将前节点的下个节点指向插入的新节点
        prev.next = insertNode;
        // 将后节点的上个节点指向插入的新节点
        tail.prev = insertNode;
    }

    /
     * 找到指定下标的节点
     *
     * @param index 下标
     * @return 指定下标的节点
     */
    private Node findNode(int index) {
        int i = -1;
        for (Node p = head; p != tail; p = p.next, i++) {
            if (i == index) {
                return p;
            }
        }
        return null;
    }

    /
     * 删除指定下标的元素
     *
     * @param index 指定下标
     */
    public void remove(int index) {
        // 找到待删除的节点
        Node removed = findNode(index);
        if (removed == null) {
            throw new IllegalArgumentException("索引非法");
        }
        // 待删除节点的上一个节点
        Node prev = removed.prev;
        // 待删除节点的下一个节点
        Node next = removed.next;
        // 上一个节点的后节点指向待删除节点的下一个节点
        prev.next = next;
        // 下一个节点的前节点指向待删除节点的上一个节点
        next.prev = prev;

        // 将删除的节点的前后指针置空,方便GC
        removed.prev = null;
        removed.next = null;
    }

    /
     * 头插法
     *
     * @param element 待插入的元素
     */
    public void addFirst(int element) {
        insert(0, element);
    }

    /
     * 删除第一个元素
     */
    public void removeFirst() {
        remove(0);
    }

    /
     * 尾插法
     *
     * @param element 待插入的元素
     */
    public void addLast(int element) {
        // 尾节点的上一个节点是最后一个节点
        Node last = tail.prev;
        // 插入新节点,新节点的上一个节点是上一个最后元素,新节点的下一个节点是尾节点
        Node added = new Node(last, element, tail);
        // 上一个最后元素的下一个节点是新插入的节点
        last.next = added;
        // 尾节点的前一个节点是新插入的节点
        tail.prev = added;
    }

    /
     * 删除最后一个元素
     */
    public void removeLast() {
        // 尾节点的上一个节点是最后一个节点
        Node last = tail.prev;
        // 最后一个节点不能是头哨兵节点
        if (last == head) {
            throw new IllegalArgumentException("链表为空");
        }
        // 最后一个节点的上个节点
        Node prev = last.prev;
        // 最后一个节点的上个节点的下个节点是尾节点
        prev.next = tail;
        // 尾节点的上一个节点是最后一个节点的上个节点
        tail.prev = prev;

        // 将最后一个节点的前后指针置空,方便GC
        last.prev = null;
        last.next = null;
    }

    /
     * 使用while循环遍历链表
     */
    public void loopWhile(IntConsumer consumer) {
        Node p = head.next;
        while (p != tail) {
            consumer.accept(p.value);
            p = p.next;
        }
    }

    /
     * 使用for循环遍历链表
     */
    public void loopFor(IntConsumer consumer) {
        for (Node p = head.next; p != tail; p = p.next) {
            consumer.accept(p.value);
        }
    }

    /
     * 使用迭代器遍历链表
     */
    @Override
    public Iterator<Integer> iterator() {
        return new NodeIterator();
    }

    /
     * 迭代器
     */
    private class NodeIterator implements Iterator<Integer> {

        Node p = head.next;

        @Override
        public boolean hasNext() {
            return p != tail;
        }

        @Override
        public Integer next() {
            int element = p.value;
            p = p.next;
            return element;
        }
    }

    /
     * 节点
     */
    static class Node {
        // 前一个节点
        Node prev;
        // 节点值
        int value;
        // 后一个节点
        Node next;

        public Node(Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
}
package com.lzlg.study.algorithm.new2023.linkedlist;

/
 * 双向链表
 *
 * @author lzlg
 * 2023/7/18 12:05
 */
public class DoubleLinkedListTest {
    public static void main(String[] args) {
        DoubleLinkedList list = new DoubleLinkedList();

        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        list.addLast(5);

        System.out.println("=================");
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.loopFor(System.out::println);
        System.out.println("=================");
        for (Integer ele : list) {
            System.out.println(ele);
        }
        System.out.println("=================");
        System.out.println(list.get(4));
        System.out.println("=================");
        list.insert(5, 6);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.removeFirst();
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.remove(0);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.removeLast();
        list.loopWhile(System.out::println);
    }
}
package com.lzlg.study.algorithm.new2023.linkedlist;

import java.util.Iterator;
import java.util.Optional;
import java.util.function.IntConsumer;

/
 * 单向链表
 *
 * @author lzlg
 * 2023/7/18 9:47
 */
public class SingleLinkedList implements Iterable<Integer> {
    // 头节点-头指针
    private Node head = null;

    /
     * 迭代器实现
     */
    @Override
    public Iterator<Integer> iterator() {
        return new NodeIterator();
    }

    /
     * 节点迭代器,如果为static class,则指针p访问不到外部类的head
     */
    private class NodeIterator implements Iterator<Integer> {
        // 使用指针p访问链表
        Node p = head;

        @Override
        public boolean hasNext() {
            return p != null;
        }

        @Override
        public Integer next() {
            // 取出节点的值,并返回
            int value = p.value;
            // 将指针p指向下一个
            p = p.next;
            return value;
        }
    }

    /
     * 头插法
     *
     * @param element 要插入的元素值
     */
    public void addFirst(int element) {
        // 1.链表为空
//        head = new Node(element, null);
        // 2.链表非空(因为是头插法)
//        head = new Node(element, head);
        // 可将上诉两行代码合并
        head = new Node(element, head);
    }

    /
     * 使用while循环遍历链表
     */
    public void loopWhile(IntConsumer consumer) {
        // 使用指针
        Node p = head;
        // 条件是p指针不为空时,则依次取出节点的值
        while (p != null) {
            consumer.accept(p.value);
            p = p.next;
        }
    }

    /
     * 使用for循环遍历链表
     */
    public void loopFor(IntConsumer consumer) {
        // 和上面的while循环实现进行对比
        for (Node p = head; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }

    /
     * 链表节点类
     * 加static关键字, 因为Node不使用SingleLinkedList(外部类)的成员变量
     */
    static class Node {
        // 节点存储的值
        int value;
        // 指向下一个节点的指针
        Node next;

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
    }

    /
     * 尾插法
     *
     * @param element 要插入的元素值
     */
    public void addLast(int element) {
        // 首先要找到插入位置的前一个节点
        Optional<Node> last = findLast();
        // 如果找到前一个节点,则创建新节点,并将前一个节点的下一个节点指向新节点
        if (last.isPresent()) {
            last.get().next = new Node(element, null);
        } else {
            // 如果链表为空,则调用addFirst方法
            addFirst(element);
        }
    }

    /
     * 找到尾部插入的前一个节点
     */
    private Optional<Node> findLast() {
        // 如果链表为空,则返回空
        if (head == null) {
            return Optional.empty();
        }
        Node p;
        // 注意,这里的循环判断条件是p.next != null,因为要找到插入位置的前一个节点
        for (p = head; p.next != null; p = p.next) ;
        return Optional.of(p);
    }

    /
     * 根据索引位置返回节点的值
     *
     * @param index 索引
     * @return 节点的值
     */
    public Optional<Integer> get(int index) {
        if (index < 0) {
            throw new IllegalArgumentException("索引值不能小于0");
        }
        // 首先根据索引找到节点
        Optional<Node> node = findNode(index);
        // 如果找到,则返回节点的值
        return node.map(element -> element.value);
    }

    /
     * 根据索引找到节点
     *
     * @param index 索引
     * @return 索引对应的节点
     */
    private Optional<Node> findNode(int index) {
        int i = 0;
        for (Node p = head; p != null; p = p.next, i++) {
            if (i == index) {
                return Optional.of(p);
            }
        }
        return Optional.empty();
    }

    /
     * 向指定索引位置插入元素值
     *
     * @param index   索引
     * @param element 待插入的元素值
     */
    public void insert(int index, int element) {
        // 如果是index = 0,则是在链表头部进行插入
        if (index == 0) {
            addFirst(element);
            return;
        }
        // 找到索引指定的节点的上一个节点
        Optional<Node> prev = findNode(index - 1);
        // 如果存在,则进行插入
        if (prev.isPresent()) {
            // 创建新节点,将新节点的下一个节点指向前一个节点的下一个节点
            prev.get().next = new Node(element, prev.get().next);
        } else {
            throw new IllegalArgumentException("索引非法");
        }
    }

    /
     * 删除第一个节点
     */
    public void removeFirst() {
        // 如果链表为空,则直接返回
        if (head == null) {
            throw new IllegalArgumentException("链表为空");
        }
        // 保存原来的头节点指针
        Node p = head;
        // 将头指针指向头部的下一个节点
        head = head.next;
        // 将删除的节点的next指针置空,方便GC回收
        p.next = null;
    }

    /
     * 删除指定索引的节点
     *
     * @param index 指定的索引
     */
    public void remove(int index) {
        // 如果索引为0,则删除的是头节点,则直接调用removeFirst
        if (index == 0) {
            removeFirst();
            return;
        }
        // 首先找到索引指向的节点的上一个节点
        Optional<Node> prev = findNode(index - 1);
        // 如果找到,则进行删除
        if (prev.isPresent()) {
            // 前一个节点的下一个节点是要删除的节点
            Node removed = prev.get().next;
            // 如果要删除的节点不存在,则抛出异常
            if (removed == null) {
                throw new IllegalArgumentException("索引非法");
            }
            // 将上一个节点的next指向要删除的节点的下个节点
            prev.get().next = removed.next;
            // 将删除的节点的指向下个节点next置空,方便GC
            removed.next = null;
        } else {
            throw new IllegalArgumentException("索引非法");
        }

    }

    /
     * 递归循环
     */
    public void recursionLoop(IntConsumer before, IntConsumer after) {
        // 调用递归方法
        recursion(head, before, after);
    }

    /
     * 递归
     *
     * @param node 节点
     */
    private void recursion(Node node, IntConsumer before, IntConsumer after) {
        // 递归结束条件
        if (node == null) {
            return;
        }
        // 前置递归
        before.accept(node.value);
        recursion(node.next, before, after);
        // 后置递归
        after.accept(node.value);
    }
}
package com.lzlg.study.algorithm.new2023.linkedlist;

/
 * 单向链表测试
 *
 * @author lzlg
 * 2023/7/18 9:47
 */
public class SingleLinkedListTest {
    public static void main(String[] args) {
        SingleLinkedList list = new SingleLinkedList();

        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        list.addLast(5);

        list.recursionLoop(
                element -> System.out.println("before: " + element),
                element -> System.out.println("after: " + element)
        );

    }

    /
     * 测试有哨兵的单链表
     */
    private static void testSingleSentinelLinkedList() {
        SingleSentinelLinkedList list = new SingleSentinelLinkedList();

        list.addFirst(1);
        list.addFirst(2);
        list.addFirst(3);
        list.addFirst(4);
        list.addFirst(5);

        System.out.println("=================");
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.loopFor(System.out::println);
        System.out.println("=================");
        for (Integer ele : list) {
            System.out.println(ele);
        }
        System.out.println("=================");
        System.out.println(list.get(5));
        System.out.println("=================");
        list.insert(5, 6);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.removeFirst();
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.remove(0);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.remove(3);
        list.loopWhile(System.out::println);
    }

    /
     * 测试单链表
     */
    private static void testSingleLinkedList() {
        SingleLinkedList list = new SingleLinkedList();

        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        list.addLast(5);

        System.out.println("=================");
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.loopFor(System.out::println);
        System.out.println("=================");
        for (Integer ele : list) {
            System.out.println(ele);
        }
        System.out.println("=================");
        System.out.println(list.get(5));
        System.out.println("=================");
        list.insert(5, 6);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.removeFirst();
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.remove(0);
        list.loopWhile(System.out::println);
        System.out.println("=================");
        list.remove(3);
        list.loopWhile(System.out::println);
    }
}
package com.lzlg.study.algorithm.new2023.linkedlist;

import java.util.Iterator;
import java.util.Optional;
import java.util.function.IntConsumer;

/
 * 单向链表带哨兵节点
 *
 * @author lzlg
 * 2023/7/18 9:47
 */
public class SingleSentinelLinkedList implements Iterable<Integer> {
    // 头节点-头指针-哨兵节点
    private final Node head = new Node(Integer.MIN_VALUE, null);

    /
     * 迭代器实现
     */
    @Override
    public Iterator<Integer> iterator() {
        return new NodeIterator();
    }

    /
     * 节点迭代器,如果为static class,则指针p访问不到外部类的head
     */
    private class NodeIterator implements Iterator<Integer> {
        // 使用指针p访问链表
        Node p = head.next;

        @Override
        public boolean hasNext() {
            return p != null;
        }

        @Override
        public Integer next() {
            // 取出节点的值,并返回
            int value = p.value;
            // 将指针p指向下一个
            p = p.next;
            return value;
        }
    }

    /
     * 头插法
     *
     * @param element 要插入的元素值
     */
    public void addFirst(int element) {
        insert(0, element);
    }

    /
     * 使用while循环遍历链表
     */
    public void loopWhile(IntConsumer consumer) {
        // 使用指针
        Node p = head.next;
        // 条件是p指针不为空时,则依次取出节点的值
        while (p != null) {
            consumer.accept(p.value);
            p = p.next;
        }
    }

    /
     * 使用for循环遍历链表
     */
    public void loopFor(IntConsumer consumer) {
        // 和上面的while循环实现进行对比
        for (Node p = head.next; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }

    /
     * 链表节点类
     * 加static关键字, 因为Node不使用SingleLinkedList(外部类)的成员变量
     */
    static class Node {
        // 节点存储的值
        int value;
        // 指向下一个节点的指针
        Node next;

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
    }

    /
     * 尾插法
     *
     * @param element 要插入的元素值
     */
    public void addLast(int element) {
        // 首先要找到插入位置的前一个节点
        Optional<Node> last = findLast();
        // 如果找到前一个节点,则创建新节点,并将前一个节点的下一个节点指向新节点
        if (last.isPresent()) {
            last.get().next = new Node(element, null);
        } else {
            // 如果链表为空,则调用addFirst方法
            addFirst(element);
        }
    }

    /
     * 找到尾部插入的前一个节点
     */
    private Optional<Node> findLast() {
        Node p;
        // 注意,这里的循环判断条件是p.next != null,因为要找到插入位置的前一个节点
        for (p = head; p.next != null; p = p.next) ;
        return Optional.of(p);
    }

    /
     * 根据索引位置返回节点的值
     *
     * @param index 索引
     * @return 节点的值
     */
    public Optional<Integer> get(int index) {
        if (index < 0) {
            throw new IllegalArgumentException("索引值不能小于0");
        }
        // 首先根据索引找到节点
        Optional<Node> node = findNode(index);
        // 如果找到,则返回节点的值
        return node.map(element -> element.value);
    }

    /
     * 根据索引找到节点
     *
     * @param index 索引
     * @return 索引对应的节点
     */
    private Optional<Node> findNode(int index) {
        int i = -1;
        for (Node p = head; p != null; p = p.next, i++) {
            if (i == index) {
                return Optional.of(p);
            }
        }
        return Optional.empty();
    }

    /
     * 向指定索引位置插入元素值
     *
     * @param index   索引
     * @param element 待插入的元素值
     */
    public void insert(int index, int element) {
        // 找到索引指定的节点的上一个节点
        Optional<Node> prev = findNode(index - 1);
        // 如果存在,则进行插入
        if (prev.isPresent()) {
            // 创建新节点,将新节点的下一个节点指向前一个节点的下一个节点
            prev.get().next = new Node(element, prev.get().next);
        } else {
            throw new IllegalArgumentException("索引非法");
        }
    }

    /
     * 删除第一个节点
     */
    public void removeFirst() {
        remove(0);
    }

    /
     * 删除指定索引的节点
     *
     * @param index 指定的索引
     */
    public void remove(int index) {
        // 首先找到索引指向的节点的上一个节点
        Optional<Node> prev = findNode(index - 1);
        // 如果找到,则进行删除
        if (prev.isPresent()) {
            // 前一个节点的下一个节点是要删除的节点
            Node removed = prev.get().next;
            // 如果要删除的节点不存在,则抛出异常
            if (removed == null) {
                throw new IllegalArgumentException("索引非法");
            }
            // 将上一个节点的next指向要删除的节点的下个节点
            prev.get().next = removed.next;
            // 将删除的节点的指向下个节点next置空,方便GC
            removed.next = null;
        } else {
            throw new IllegalArgumentException("索引非法");
        }

    }
}
数据结构
算法
程序员内功
  • 作者:lzlg520
  • 发表时间:2023-09-13 21:45
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 公众号转载:请在文末添加作者公众号二维码