3.线性表

目录

一、线性表:顺序表与链表。 

(1)顺序表。

(1)顺序表的代码实现。

(2)时间复杂度。

(3)ArrayList的底层原理。

(2)链表。

(1)单向链表。 

(2)双向链表。 

(3)java中的LinkedList实现。​编辑

平时调用的方法:

 (4)链表的时间复杂度分析。 

(5)单链表反转。

(6)快慢指针。

(7)单向链表是否有环。

(8)有环链表入口问题。 

二、线性表:栈。

(1)定义:

(2)代码实现。

(3)案例-括号匹配问题。

(4)案例-逆波兰表达式。

(1)定义:

(2)逻辑图。

 (3)代码实现。

三、线性表-队列。


 

一、线性表:顺序表与链表。 

f7a914ba329745e5a4e551349ad3c36b.png

(1)顺序表。

(1)顺序表的代码实现。

dfc7de8c75e8465a9aa7114209277073.png

package 线性表_顺序表;

import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;

public class SequeceList<T> implements Iterable<T>{
    //存储数据的数组
    private T[] eles;
    //记录当前顺序表中的元素个数
    private int N;
    public int len;//自己设置,记得删除

    //构造方法
    public SequeceList(int capacity){
        //初始化数据
        this.eles = (T[]) new Object[capacity];
        //初始化长度
        this.N = 0;
    }
    //将一个线性表置为空表
    public void clear(){
        this.N = 0;
    }
    //判断当前线性表是否为空表
    public boolean isEmpty(){
        return N == 0;
    }
    //获取线性表的长度
    public int length(){
        return N;
    }
    //获取指定位置的元素
    public T get(int i){
        if (i<0 || i>=N){
            throw new RuntimeException("当前元素不存在!");
        }
        return eles[i];
    }
    //向线性表中添加元素t
    public void insert(T t){
        if (N == eles.length){
            //扩容到原数组的两倍
            resize(2 * eles.length);//注意:这里的length是数组容量(添加数据后也不会变),是固定的,而N是数据的个数
        }
        eles[N++] = t;
    }
    //在i元素处插入元素t
    public void insert(int i,T t){
        if (N + 1 > eles.length){
            //扩容到原数组的两倍
            resize(2 * eles.length);//注意:这里的length是数组容量(添加数据后也不会变),是固定的,而N是数据的个数
        }
        if (N + 1 > eles.length){
            throw new RuntimeException("当前表已满");
        }
        //先把i索引处的元素及其后面的元素依次向后移动一位
        for (int index = N; index > i; index--) {
            eles[index] = eles[index - 1];
        }
        //再把t元素放到i索引处即可
        eles[i] = t;
        //元素个数+1
        N++;
    }
    //删除指定位置i处的元素,并返回该元素
    public T remote(int i){
        //记录索引i处的值
        T current = eles[i];
        //索引i后面元素依次向前移动一位即可
        for(int index = i;index < N - 1;index++){
            eles[index] = eles[index + 1];
        }
        //元素个数-1
        N--;
        if (N < (eles.length / 4)){
            resize(eles.length / 2);
        }
        return current;
    }
    //查找t元素第一次出现的位置
    public int indexOf(T t){
        for (int i = 0; i < N; i++) {
            if (eles[i].equals(t)){
                return i;
            }
        }
        return -1;//代表没找到
    }

    @Override
    public String toString() {
        return "SequeceList{" +
                "eles=" + Arrays.toString(eles) +
                ", N=" + N +
                ", len=" + len +
                '}';
    }
    //根据参数newSize,重置eles的大小
    public void resize(int newSize){
        //定义一个临时数组,指向原数组
        T[] temp = eles;
        //创建新数组
        eles = (T[]) new Object[newSize];
        //把原数组的数据拷贝到新数组即可
        for (int i = 0; i < N; i++) {
            eles[i] = temp[i];
        }
    }

    @Override
    public void forEach(Consumer<? super T> action) {
        Iterable.super.forEach(action);//Iterable.super 指本类的父类
    }

    //可以看看原理,迭代器
    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }
    private class SIterator implements Iterator<T>{
        private int cusor;
        //初始化
        public SIterator(){
            this.cusor = 0;
        }
        @Override
        public boolean hasNext() {
            //还有元素就返回true,否则false
            return cusor < N;
        }
        @Override
        public T next() {
            return eles[cusor++];
        }
    }
}

 测试代码:

package 线性表_顺序表;

import java.util.Arrays;

public class SequenceListTest {
    public static void main(String[] args) {
        //创建顺序表对象
        SequeceList<String> s1 = new SequeceList<>(2);
        int[] cc = new int[200];
        System.out.println("cc.length:"+cc.length);//cc.length:200
        //测试插入
        s1.insert("姚明");
        s1.insert("科比");
        s1.insert("麦迪");
        s1.insert(1,"詹姆斯");
        //增强for遍历和forEach
        s1.forEach(s -> System.out.println(s));
//        for (String s : s1) {
//            System.out.println(s);
//        }

        //测试获取
        System.out.println("获取索引1处的结果为:"+s1.get(1));
        //测试删除
        System.out.println("删除的元素是:"+s1.remote(0));
        //测试清空
        s1.clear();
        for (String s : s1) {
            System.out.println(s);
        }
        System.out.println("清空后的线性表中的袁术个数为:"+ s1.length());
        System.out.println("s1:"+ s1);
        for (int i : dd) {
            System.out.println(i);//打印5次,都是0
        }
    }
}

(2)时间复杂度。

a45b5dc6ada44461b8e5eb5ef4ecc911.png

(3)ArrayList的底层原理。

我们添加数据时调用的方法,它会调用add私有重载方法。 

c5d16f99a52949198127768436571f41.png

如果数组还没满就添加数据,满了就先扩容,再添加数据。

c12cf30042fd419abe7d26e5f3dc9f86.png

扩容1个数据。

b9e4c944c40c4c8d90908a4bcfea663b.png

(2)链表。

b9c39a3d2ad64419acf90751574dd1f0.png

(1)单向链表。 

b00579ebd5b4465196b950c7339d90ef.png

package 线性表_单向链表;

import java.util.Iterator;

public class LinkList<T> implements Iterable<T>{
    //记录头结点
    private Node head;
    //记录链表长度
    private int N;

    //结点类(内部类)
    private class Node{
        //存储数据
        T item;
        //下一个结点
        Node next;
        //构造方法
        public Node(T item,Node next){
            this.item = item;
            this.next = next;
        }
    }
    public LinkList(){
        //初始化头结点
        this.head = new Node(null,null);
        //初始化元素个数
        this.N = 0;
    }
    //清空链表
    public void clear(){
        head.next =  null;
        this.N = 0;
    }
    //获取链表的长度
    public int length(){
        return N;
    }
    //判断链表是否为空表
    public boolean isEmpty(){
        return N == 0;
    }
    //获取指定位置i处的元素
    public T get(int i){
        //通过循环,从头结点开始往后找,一次找i次,就可以找到对应的元素
        Node n = head.next;
        for (int index = 0; index < i; index++) {
            n = n.next;
        }
        return n.item;
    }
    //向链表中添加元素t
    public void insert(T t){
        //找到当前最后一个结点
        Node n = head;
        while (n.next != null) {
            n = n.next;
        }
        //创建新节点,保存元素t
         Node newNode = new Node(t,null);
        //让当前最后一个结点指向新结点
        n.next = newNode;
        //元素个数+1
        N++;
    }
    public void insert(int i,T t){
        //找到i位置前一个结点
        Node pre = head;
        for (int index = 0; index < i; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点,并且新结点需要指向原来i位置的结点
        Node newNode = new Node(t,curr);
        //原来i位置前的一个结点指向新结点即可
        pre.next = newNode;
        //元素的个数+1
        N++;

    }
    //删除指定位置i处的元素,并返回被删除的元素
    public T remove(int i){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index = 0; index < i; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nextNode = curr.next;
        //前一个结点指向下一个结点
        pre.next = nextNode;
        //元素的个数-1
        N--;
        return curr.item;
    }
    //查找元素t在链表中第一次出现的位置
    public int indexOf(T t){
        //从头结点开始,依次找出每一个结点,取出item,和t比较,如果相同,就找到了
        Node n = head;
        //为什么不用while循环?因为需要返回索引是第几个,所以用这个有索引值的,用while也可以的
        for (int i = 0; n.next != null; i++) {
            n = n.next;
            if (n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }

    @Override
    public Iterator<T> iterator() {
        return new LIterator();
    }
    private class LIterator implements Iterator{
        private Node n;
        public LIterator(){
            n = head;
        }
        @Override
        public boolean hasNext() {
            return n.next != null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}

 测试代码:

package 线性表_单向链表;

public class LinkListTest {
    public static void main(String[] args) {
        //创建单向链表对象
        LinkList<String> l1 = new LinkList<>();
        //测试插入
        l1.insert("姚明");
        l1.insert("科比");
        l1.insert("麦迪");
        l1.insert(1,"詹姆斯");
        //测试获取
        System.out.println("获取索引1处的结果为:"+l1.get(1));
        //测试删除
        System.out.println("删除的元素是:"+l1.remove(0));
        //测试清空
        l1.clear();
        System.out.println("清空后的线性表中的袁术个数为:"+ l1.length());
        System.out.println("s1:"+ l1);
    }
}

(2)双向链表。 

7e23d77aae8f4d37ace1c81b3b144419.png

package 线性表_双向链表;


import java.util.Iterator;

public class TowWayLinkList<T> implements Iterable<T>{
    //首结点
    private Node head;
    //尾结点
    private Node last;
    //链表的长度
    private int N;

    //结点类
    private class Node{
        public Node(T item,Node pre,Node next){
            this.item = item;
            this.pre = pre;
            this.next = next;
        }
        //存储数据
        public T item;
        //指向上一个结点
        public Node pre;
        //指向下一个结点
        public Node next;
    }
    public TowWayLinkList(){
        //初始化头结点
        this.head = new Node(null,null,null);
        this.last = null;
        //初始化元素个数
        this.N = 0;
    }
    //清空链表
    public void clear(){
        this.head.next = null;
        this.last = null;
        this.N = 0;
    }
    //获取链表长度
    public int length(){
        return N;
    }
    //判断链表是否为空
    public boolean isEmpty(){
        return N == 0;
    }
    //获取第一个元素
    public T getFirst(){
        if (isEmpty()){
            return null;
        }
        return head.next.item;
    }
    //获取最后一个元素
    public T getLast(){
        if (isEmpty()){
            return null;
        }
        return last.item;
    }
    //插入元素t
    public void insert(T t){
        //如果链表为空
        if (isEmpty()){
            //创建新结点
            Node newNode = new Node(t,head,null);
            //让新结点成为尾结点
            last = newNode;
            //让头结点指向尾结点
            head.next = last;
        }else { //如果链表不为空
            //创建新的结点
            Node newNode = new Node(t, last, null);
            //让当前的尾结点指向新结点
            last.next = newNode;
            //让新结点成为尾结点
            last = newNode;
        }
        //元素个数+1
        N++;
    }
    //向指定位置i处插入元素t
    public void insert(int i,T t){//这里的没有写判断条件,主要是理解就行,有能力写出来
        //找到i位置的前一个结点
        Node  pre= head;
        for (int index = 0; index < i; index++) {
            pre = pre.next;
        }
        //找到i位置处的结点
        Node curr = pre.next;
        //创建新结点
        Node newNode = new Node(t,pre,curr);
        //让i位置的前一个结点的下一个结点变为新结点
        pre.next = newNode;
        //让i位置的前一个结点变为新结点
        curr.pre = newNode;
        //元素个数+1
        N++;
    }
    //获取指定位置i处的元素
    public T get(int i){
        Node n = head.next;
        for (int index = 0; index < i; index++) {
            n = n.next;
        }
        return n.item;
    }
    //找到元素t在链表中第一次出现的位置
    public int indexOf(T t){
        Node n = head;
        for (int i = 0; n.next != null; i++) {
            n = n.next;
            if (n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }
    //删除位置i处的元素,并返回该元素
    public T remove(int i){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index = 0; index < i; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nextNode = curr.next;
        //让i位置的前一个结点的下一个结点变为i位置的的下一个结点
        pre.next = nextNode;
        //让i位置的下一个结点的上一个结点变为i位置的前一个结点
        nextNode.pre = pre;
        //元素的个数-1
        N--;
        return curr.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new TIterator();
    }

    private class TIterator  implements Iterator{
        private Node n;
        public TIterator(){
            this.n = head;
        }
        @Override
        public boolean hasNext() {
            return n.next != null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }

}

 测试代码:

package 线性表_双向链表;

public class TowWayLinkListTest {
    public static void main(String[] args) {
        //创建双向链表对象
        TowWayLinkList<String> l1 = new TowWayLinkList<>();
        //测试插入
        l1.insert("姚明");
        l1.insert("科比");
        l1.insert("麦迪");
        l1.insert(1,"詹姆斯");
        //测试获取
        System.out.println("获取索引1处的结果为:"+l1.get(1));
        //测试删除
        System.out.println("删除的元素是:"+l1.remove(0));
        System.out.println("清空后的线性表中的袁术个数为:"+ l1.length());
        System.out.println("s1:"+ l1);
        System.out.println("-----------------");
        System.out.println("第一个元素是:"+l1.getFirst());
        System.out.println("最后一个元素是:"+l1.getLast());
        //测试清空
        l1.clear();
    }
}

(3)java中的LinkedList实现。fff9f598433a47008d444d5265fb099d.png

平时调用的方法:

1c4314876aa646bfb2a7a66018edaf37.png

对应的方法体:

ec71b816934e4a13b82b419dc0f5c61e.png

 内部类:

dff897f591ef4226b674ffc80c2c652a.png

 (4)链表的时间复杂度分析。 

8374e580b7ef41a8935b9d5e3be53af8.png

(5)单链表反转。

b579ac039d27400eb9f0554fcd02573e.png

//单链表反转实现
    public void reverse(){
        //判断当前链表是否为空链表,如果是空链表,则结束运行,如果不是,则调用重载的reverse方法完成反转
        if (isEmpty()){
            return;
        }
        reverse(head.next);
    }
    //反转指定的结点curr,并把反转后的结点返回
    public Node reverse(Node curr){
        if (curr.next == null){
            head.next = curr;
            return curr;
        }
        //递归的反转当前结点curr的下一个结点,返回值就是链表反转后,当前结点的上一个结点
        Node pre = reverse(curr.next);
        //让返回的结点的下一个结点变为当前结点curr;
        pre.next = curr;
        //把当前结点的下一个结点变为null
        curr.next = null;//原因:最后一个结点的next不指向任何地方,所以这里的只是针对最后一个结点,对其他没有任何作用
        return curr;
    }

(6)快慢指针。

ac1f0ff4f14a4f9c857aa2c9ec44aa18.png

package 线性表_快慢指针;

public class FastSlowTest {
    public static void main(String[] args) throws Exception {

        //注意:这种方法如果是偶数个数据,那么中间值就是除以2再加1,即(6个数据,第四个为中间值)
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> six = new Node<String>("ff", null);
        Node<String> seven = new Node<String>("gg", null);
        //完成结点之间的指向
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;
        //查找中间值
        String mid = getMid(first);//第一个数据结点
        System.out.println("中间值为:"+mid);
    }
    /**
     * @param first 链表的首结点
     * @return 链表的中间结点的值
     */
    public static String getMid(Node<String> first) {
        //定义两个指针
        Node<String> slow = first;
        Node<String> fast = first;
        //使用两个指针遍历链表,当快指针指向的结点没有下一个结点了,就可以结束了,结束之后,慢指针指向的结点就是中间值
        while(fast!=null && fast.next!=null){
            //fast!=null:必须确保fast不为null即可能还有元素,通过后再判断fast.next!=null,不然空指针异常
            //fast.next!=null:必须确保后面至少还有一个结点(刚好只有一个结点,那么fast移动两次后值就是null)
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow.item;
    }
    //结点类
    private static class Node<T> {
        //存储数据
        T item;
        //下一个结点
        Node next;
        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

(7)单向链表是否有环。

05d5242b23f64fcb9009abc7b9ceb20d.png

package 线性表_快慢指针;

public class CircleListCheckTest {
    public static void main(String[] args) throws Exception {
        //创建结点
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> six = new Node<String>("ff", null);
        Node<String> seven = new Node<String>("gg", null);

        //完成结点之间的指向
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;
//        //产生环
        seven.next = third;
//        six.next = third;

        //判断链表是否有环
        boolean circle = isCircle(first);
        System.out.println("first链表中是否有环:"+circle);
    }
    /**
     * 判断链表中是否有环
     * @param first 链表首结点
     * @return ture为有环,false为无环
     */
    public static boolean isCircle(Node<String> first) {
        //定义快慢指针
        Node<String> fast = first;
        Node<String> slow = first;

        //遍历链表,如果快慢指针指向了同一个结点,那么证明有环
        while (fast != null && fast.next != null){
            //变换fast和slow
            fast = fast.next.next;
            slow = slow.next;
            //注意:slow.equals才行,否则有空指针异常
            //slow.equals(fast)如果不重写equals也可以不出错
            if (slow == fast){
                //这里应当比较的是地址(==),而不是比较内容(equals 没有重写就相当于 == )
                return true;//代表有环
            }
        }
        return false;//代表无环
    }
    //结点类
    private static class Node<T> {
        //存储数据
        T item;
        //下一个结点
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

(8)有环链表入口问题。 

原理:快指针第一次追上慢指针必定指向同一个结点,此时快指针路程是满指针的两倍,慢指针路程是s,快指针是2s,即从当前满指针的下一个位置算起,走过第s个结点(设为a)就是当前快慢指针相遇的结点,即a和临时结点路程相同,当两者进入环之后的结点个数不算,也还是相同,即慢指针走过c个结点就是环入口,那么临时结点走过c个结点也是环的入口,所以两者这个时候同时走,相遇的结点就是环的入口。

cbcdfa4744024224bba4aeeca775ceab.png

package 线性表_快慢指针;

public class CircleListInTest {
    public static void main(String[] args) throws Exception {
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> six = new Node<String>("ff", null);
        Node<String> seven = new Node<String>("gg", null);

        //完成结点之间的指向
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;
        //产生环
        six.next = fourth;
//        seven.next = third;
        //查找环的入口结点
        Node<String> entrance = getEntrance(first);
        System.out.println("first链表中环的入口结点元素为:"+entrance.item);
    }
    /**
     * 查找有环链表中环的入口结点
     * @param first 链表首结点
     * @return 环的入口结点
     */
    public static Node getEntrance(Node<String> first) {
        //定义快慢指针
        Node<String> fast = first;
        Node<String> slow = first;
        Node<String> temp = null;
        Boolean flag = true;
        //遍历链表,先找到环(快慢指针相遇),准备一个临时指针,指向链表的首结点,继续遍历,直到慢指针和临时指针相遇,那么相遇时所指向的结点就是环的入口
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            //判断快慢指针是否相遇,注意这里应该只运行一次,不然会有bug
            if (flag) {
                if (fast == slow){
                    //运行一次后,不能再进来
                    flag = false;
                    //为临时结点
                    temp = first;
                    continue;
                }
            }
            //让临时结点变换
            if (temp != null){
                temp = temp.next;
                //判断临时指针是否和慢指针相遇
                if (temp == slow){
                    break;
                }
            }
        }
        return temp;
    }
    //结点类
    private static class Node<T> {
        //存储数据
        T item;
        //下一个结点
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

二、线性表:栈。

(1)定义:

0c1c215708ed4db8846c6e3ae583c37c.png

(2)代码实现。

package 线性表_栈;

import java.util.Iterator;

public class Stack <T> implements Iterable<T>{
    //记录首结点
    private Node head;
    //栈中元素的个数
    private int N;

    private class Node{
        public T item;
        public Node next;
        public Node(T item,Node next){
            this.item = item;
            this.next = next;
        }
    }
    public Stack(){
        this.head = new Node(null,null);
        this.N = 0;
    }

    //判断当前栈中元素个数是否为0
    public boolean isEmpty(){
        return N == 0;
    }
    //获取栈中元素的个数
    public int size(){
        return N;
    }
    //把t元素压入栈
    public void push(T t){
        //找到首结点指向的第一个结点
        Node oldFirst = head.next;
        //创建新结点
        Node newNode = new Node(t,null);
        //让首结点指向新结点
        head.next = newNode;
        //让新结点指向原来的第一个结点
        newNode.next = oldFirst;
        //元素个数+1
        N++;
    }
    //弹出栈顶元素
    public T pop(){
        //找到首结点指向的第一个结点
        Node oldFirst = head.next;
        if (oldFirst == null){
            return null;
        }
        //让首结点指向原来的第一个结点的下一个结点
        head.next = oldFirst.next;
        //元素个数-1
        N--;
        return oldFirst.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }
    private class SIterator implements Iterator{
        private Node n;
        public SIterator(){
            this.n = head;
        }

        @Override
        public boolean hasNext() {
            return n.next != null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}

测试代码:成功。

package 线性表_栈;

public class StackTest {
    public static void main(String[] args) {
        //创建栈对象
        Stack<String> stack = new Stack<>();
        //测试压栈
        stack.push("a");
        stack.push("b");
        stack.push("c");
        stack.push("d");
        for (String s : stack) {
            System.out.println(s);
        }
        System.out.println("--------------------------------");
        //测试弹栈
        String result = stack.pop();
        System.out.println("弹出的元素是:"+result);
        for (String s : stack) {
            System.out.println(s);
        }
    }
}

(3)案例-括号匹配问题。

53a103c9e337445aa34dbbf6b7a152dc.png

package 线性表_栈;

public class BracketsMatchTest {
    public static void main(String[] args) {
        String str = "(上海(长安)())";
        boolean match = isMatch(str);
        System.out.println(str+"中的括号是否匹配:"+match);
    }
    public static boolean isMatch(String str){
        //1.创建栈对象,用来存储左括号
        Stack<String> chars = new Stack<>();
        //2.从左往右遍历字符串
        for (int i = 0; i < str.length(); i++) {
            String currChar = str.charAt(i) + "";

            //3.判断当前字符串是否为左括号,如果是,则把字符放入栈中
            if (currChar.equals("(")){
                chars.push(currChar);
            }else if (currChar.equals(")")){
                //4.继续判断当前字符串是否是右括号,如果是,则从栈中弹出一个左括号,并判断弹出的结果是否为null,如果为null,证明没有匹配的左括号,如果不为null,则证明有匹配的左括号
                String pop = chars.pop();
                if (pop == null) {
                    return false;
                }
            }
        }
        //5.判断栈中还有没有左括号,如果有,则证明括号不匹配
        if (chars.size() == 0){
            return true;
        }
        return false;
    }
}

(4)案例-逆波兰表达式。

(1)定义:

2b569a38c4284a2898c0555fd4e3e462.png

(2)逻辑图。

fa1d088e3c0847c9935ef227b484b971.png

 (3)代码实现。

package 线性表_栈;

public class ReversePolishNotationTest {
    public static void main(String[] args) {
        //中缀表达式:3*(17-15)+18/6的逆波兰表达式如下
        String[] notation = {"3","17","15","-","*","18","6","/","+"};
        int result = caculate(notation);
        System.out.println("逆波兰表达式的结果为:"+result);
    }
    public static int caculate(String[] notation){
        //1.定义一个栈,用来存储操作数
        Stack<Integer> oprands = new Stack<>();
        //2.从左往右遍历逆波兰表达式,得到每一个元素
        for (int i = 0; i < notation.length; i++) {
            String curr = notation[i];
            //3.判断当前元素是运算符还是操作数
            Integer o1;
            Integer o2;
            Integer result;
            switch (curr){
                case "+":
                    //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 + o1;
                    oprands.push(result);
                    break;
                case "-":
                    //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    //注意:这里是最后一个出来的减第一个出来的,因为是先进后出,后出的在前面
                    result = o2 - o1;
                    oprands.push(result);
                    break;
                case "*":
                    //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 * o1;
                    oprands.push(result);
                    break;
                case "/":
                    //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    //注意:这里是最后一个出来的除以第一个出来的,因为是先进后出,后出的在前面
                    result = o2 / o1;
                    oprands.push(result);
                    break;
                default:
                    //5.操作数,把该操作数放入到栈中
                    oprands.push(Integer.valueOf(curr));
                    break;
            }
        }
        //6.得到栈中最后一个元素,就是逆波兰表达式的结果
        int result = oprands.pop();
        return result;
    }
}

三、线性表:队列。

c1a2ffefa151411ab789d3b3d6bb0353.png


队列(Queue)是一种数据结构,它是一个先进先出(First In First Out,FIFO)的有序集合。队列有以下几个名词:

入队(Enqueue):将数据放入队列的过程称为入队。

出队(Dequeue):从队列中取出数据的过程称为出队。

队首(Front):队列中第一个元素所在的位置称为队首。

队尾(Rear):队列中最后一个元素所在的位置称为队尾。

队列空(Empty):当队列中没有任何元素时,称为队列空。

队列满(Full):当队列中元素的数量达到队列的容量时,称为队列满。

队列容量(Capacity):队列的容量指的是队列能够存储的最大元素数量。

队列指针(Pointer):队列指针用于记录队首和队尾元素的位置,它随着入队和出队的操作而不断变化。

队列中的元素只能按照先进先出的顺序进行访问,因此队列在程序中应用广泛,如排队模拟、计算机网络中的数据传输、操作系统中的进程调度等。

 

package 线性表_队列;


import java.util.Iterator;

public class Queue<T> implements Iterable<T>{
    //记录首结点
    private Node head;
    //记录最后一个结点
    private Node last;
    //记录队列中元素个数
    private int N;

    private class Node{
        T item;
        Node next;

        public Node(T item,Node next){
            this.item = item;
            this.next = next;
        }
    }

    public Queue(){
        this.head = new Node(null,null);
        this.last  = null;
        this.N = 0;
    }
    //判断队列是否为空
    public boolean isEmpty(){
        return N == 0;
    }
    //返回队列中元素的个数
    public int size(){
        return N;
    }
    //向队列中插入元素t
    public void enqueue(T t){
        //当前尾结点last为null
        if (last == null){
            last = new Node(t,null);
            head.next = last;
        }else {
            //当前尾结点不为null
            Node oldLast = last;
            last = new Node(t,null);
            oldLast.next = last;
        }
        //元素个数+1
        N++;
    }
    //从队列中拿出一个元素
    public T dequeue(){
        if (isEmpty()){
            return null;
        }
        Node oldFirst = head.next;
        head.next = oldFirst.next;
        N--;
        //因为出队列其实是在删除元素,因此如果队列中的元素被删除完了,需要重置last=null;
        if (isEmpty()){
            last = null;
        }
        return oldFirst.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new QIterator();
    }
    private class QIterator implements Iterator{
        private Node n;

        public QIterator(){
            this.n = head;
        }
        @Override
        public boolean hasNext() {
            return n.next != null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}

 测试代码:

package 线性表_队列;

public class QueueTest {
    public static void main(String[] args) {
        //创建队列对象
        Queue<String> q = new Queue<>();
        //测试队列的enqueue方法
        q.enqueue("a");
        q.enqueue("b");
        q.enqueue("c");
        q.enqueue("d");
        for (String s : q) {
            System.out.println(s);
        }
        System.out.println("----------------------");
        //测试队列的dequeue方法
        String result = q.dequeue();
        System.out.println("出队列的元素是:"+result);
        System.out.println("剩余的元素个数:"+q.size());
    }
}

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值