JAVA-数据结构与算法-双向链表和栈

496 阅读3分钟

写在前面

双向链表

  • 单向链表查找的方向只有一个;不能实现自我删除,需要辅助节点
  • 双向链表可以向前向后查找,可以实现自我删除
  • 一个节点中多出一个pre指向前一个节点

代码实现

  • 更新展示跟单链表一样
  • 节点,pre绑定上一个链表
class Node {
    public int id;
    public String name;
    public Node next;
    public Node pre;
    // Node(int id, String name) 和 toString...
}
  • 添加,temp.next指向nodenode.pre指向temp
public void add(Node node) {
    //头节点不能动,通过辅助节点进行遍历
    Node temp = head;
    while (true) {
        //最后一个节点的next指向空
        if (temp.next == null) {
            break;
        }
        //如果没有找到最后,就将temp后移
        temp = temp.next;
    }
    //当退出while循环,temp指向链表的最后
    //temp.next指向node,node.pre指向temp
    temp.next = node;
    node.pre = temp;
}
  • 顺序添加,要注意双向绑定时,添加的位置是不是最后一个,避免空指针异常;要先将后面的接到添加的node上,再加node添加到链表上
public void addByOrder(Node node) {
    //通过辅助变量找到插入的位置,位于添加位置的前一个节点
    Node temp = head;
    boolean flag = false; //添加的编号是否已经存在,默认为false
    while (true) {
        //说明temp已经在最后了
        if (temp.next == null) {
            break;
        }
        //找到位置,这个位置的后一个id要大于要添加的id
        if (temp.next.id > node.id) {
            break;
        } else if (temp.next.id == node.id) {
            //添加的id已经存在,不能添加
            flag = true;
            break;
        }
        //后移
        temp = temp.next;
    }
    if (flag) {
        System.out.println("准备插入的节点已经存在,修改名字");
        temp.next.name = node.name;
    } else {
        if (temp.next != null) {
            node.next = temp.next;
            temp.next.pre = node;
        }
        temp.next = node;
        node.pre = temp;
    }
}
  • 删除,双向删除
public void del(int id) {
    if (head.next == null) {
        System.out.println("List is empty.");
        return;
    }
    Node temp = head.next;
    boolean flag = false; //是否找到待删除的节点
    //直接找到待删除的节点
    while (true) {
        //已经到了链表的最后且没有找到
        if (temp == null) {
            break;
        }
        if (temp.id == id) {
            //找到待删除的节点
            flag = true;
            break;
        }
        temp = temp.next;
    }
    if (flag) {
        //待删除的节点的上一个节点的next指向待删除节点的下一个节点
        temp.pre.next = temp.next;
        //待删除的节点的下一个节点的pre指向待删除节点的上一个节点
        //有风险,要处理如果待删除的节点是最后一个节点
        if (temp.next != null) {
            temp.next.pre = temp.pre;
        }
    } else {
        System.out.println("没有找到id为" + id + "的节点");
    }
}

  • 先进后出 FILO-First In Last Out
  • 允许插入和删除的一端,是栈顶;固定的一端,是栈底
  • 应用场景 子程序的调用;递归调用;表达式转换与求值;二叉树遍历;图形的深度优先搜索法

数组模拟栈

class ArrStack {
    private int maxSize;
    private int[] stack;
    // 表示栈顶初始化为-1
    private int top = -1;

    public ArrStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

    //栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //入栈
    public void push(int value)  {
        //是否满
        if (isFull()) {
            System.out.println("Stack is full.");
            return;
        }
        top ++;
        stack[top] = value;
    }
    //出栈
    public int pop() {
        if (isEmpty()) {
            //抛出异常
            throw new RuntimeException("Stack is empty.");
        }
        int value = stack[top];
        top --;
        return value;
    }
    //遍历栈,从栈顶开始显示数据
    public void list() {
        if (isEmpty()) {
            System.out.println("Stack is empty.");
            return;
        }
        for (int i = top; i >= 0 ; i--) {
            System.out.println("stack[" + i + "]=" + stack[i]);
        }
    }
}

链表模拟栈

class StackLinkedList {
    private int maxSize;
    private Node head = new Node(0);
    public StackLinkedList(int maxSize) {
        this.maxSize = maxSize;
        Node cur = null;
        Node newNode = null;
        for (int i = 1; i <= this.maxSize; i++) {
            newNode = new Node(i);
            if (i == 1) {
                head.next = newNode;
                cur = newNode;
            } else {
                cur.next = newNode;
                cur = newNode;
            }
        }
    }
    //空
    public boolean isEmpty() {
        return head.next.flag == false;
    }
    //满
    public boolean isFull() {
        Node cur = head.next;
        while (true) {
            if (cur.next == null && cur.flag == true) {
                return true;
            }
            if (cur.flag == false) {
                return false;
            }
            cur = cur.next;
        }
    }
    //添加数据
    public void push(int value) {
        if (isFull()) {
            System.out.println("stack is full");
            return;
        }
        Node cur = head.next;
        while (true) {
            if (cur.flag == false) {
                cur.flag = true;
                break;
            }
            cur = cur.next;
        }
        cur.value = value;
    }
    //pop
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("stack is empty22");
        }
        Node cur = head.next;
        while (true) {
            if (cur.next != null) {
                if (cur.flag == true && cur.next.flag == false) {
                    break;
                }
            } else {
                break;
            }
            cur = cur.next;
        }
        cur.flag = false;
        return cur.value;
    }
    //list
    public void list() {
        if (isEmpty()) {
            System.out.println("stack is empty33");
            return;
        }
        reverseLinkedList();
        Node cur = head.next;
        while (true) {
            if (cur.flag == true) {
                System.out.println(cur);
            }
            if (cur.next == null) {
                break;
            }

            cur = cur.next;
        }
    }
    //反转
    public void reverseLinkedList() {
        if (head.next == null || head.next.next == null) {
            return;
        }
        Node cur = head.next;
        Node next = null;
        Node reverseHead = new Node(0);
        while (cur != null) {
            next = cur.next;
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            cur = next;
        }
        head.next = reverseHead.next;

    }
}
  • 节点
class Node {
    public int value;
    public int top;
    public Node next;
    //是否有值的标记,默认false没有值
    public boolean flag = false;
    public Node(int top) {
        this.top = top;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", top=" + top +
                '}';
    }
}

栈实现综合计算器

  • 中缀表达式实现
 String expression = "30+2*6-7";
    //创建两个栈
    ArrStack numStack = new ArrStack(10);
    ArrStack operStack = new ArrStack(10);
    //扫描变量
    int index = 0;
    int num1 = 0;
    int num2 =  0;
    int oper = 0;
    int res = 0;
    //每次扫描得到的char
    char ch = ' ';
    //拼接多位数
    String num = "";
    while (true) {
        //扫描expression
        ch = expression.substring(index, index + 1).charAt(0);
        if (operStack.isOperation(ch)) {
            if (!operStack.isEmpty()) {
                //如果符号栈有操作符,进行比较,如果当前的优先级小于或者等于栈顶的操作符
                if (operStack.proiority(ch) <= operStack.proiority(operStack.peek())) {
                    //就要先从数栈中pop出两个数,从栈pop出操作符进行运算,再存储
                    num1 = numStack.pop();
                    num2 = numStack.pop();
                    oper = operStack.pop();
                    res = numStack.calculate(num1, num2, oper);
                    numStack.push(res);
                    operStack.push(ch);
                } else  {
                    //当前优先级大于栈顶
                    operStack.push(ch);
                }
            } else {
                //符号栈为空直接入栈
                operStack.push(ch);
            }
        } else {
            //数字直接入数栈
            //numStack.push(ch - 48); //ascii码转换
            //处理多位数。需要在遍历字符串时,多检测一位,如果是数就继续扫描,并拼接
            num += ch;
            //如果ch是最后一位,直接入栈
            if(index == expression.length() - 1) {
                numStack.push(Integer.parseInt(num));
            } else {
                //判断下一个字符是不是数据,如果是数字,就继续扫描,如果运算符则入栈
                if (operStack.isOperation(expression.substring(index + 1, index + 2).charAt(0))) {
                    numStack.push(Integer.parseInt(num));
                    //清空num
                    num = "";
                }
            }

        }
        //index+1,并判断是否结尾
        index ++;
        if (index >= expression.length()) {
            break;
        }
    }

    //扫描完,进行剩下的运算
    while (true) {
        //如果符号栈为空,则计算完成
        if (operStack.isEmpty()) {
            break;
        }
        num1 = numStack.pop();
        num2 = numStack.pop();
        oper = (char)operStack.pop();
        res = numStack.calculate(num1, num2, oper);
        numStack.push(res);
    }
    System.out.println(expression + " = " + numStack.pop());
}
  • 数组实现栈的功能添加
//返回运算符优先级,数字越大,优先级越高
//只有 + - * /
public int proiority(int operation) {
    if (operation == '*' || operation == '/') {
        return 1;
    } else if (operation == '+' || operation == '-') {
        return 0;
    } else {
        return -1;
    }
}
//判断是不是运算法
public boolean isOperation(int val) {
    return val == '+' || val == '-' || val == '*' || val == '/';
}

//计算方法
public int calculate(int num1, int num2, int operation) {
    int res = 0;
    switch (operation) {
        case '+':
            res = num1 + num2;
            break;
        case '*':
            res = num1 * num2;
            break;
        case '-':
            res = num2 - num1;
            break;
        case '/':
            res = num2 / num1;
            break;
        default:
            break;
    }
    return res;
}
//返回当前栈顶的值,但是不出栈
public int peek() {
    return stack[top];
}