数据结构与算法——线性表、顺序表、链表、栈、队列

一、线性表

前驱元素:若A元素在B元素的前面,则称A为B的前驱元素。

后继元素:若B元素在A元素的后面,则称B为A的后继元素

线性表的特征:数据元素之间具有一种一对一的逻辑关系。
1.第一个数据元素没有前驱,这个数据元素被称为头结点;
2.最后一个数据元素没有后继,这个数据元素被称为尾结点;
3.除了第一个元素和最后一个元素外,其他数据元素有且仅有一个前驱和一个后继。

线性表的分类:线性表中数据元素存储的方式可以是顺序存储,也可以是链式存储,按照不同的存储方式可分为顺序表和链表。

1.1顺序表

1.1.1顺序表的实现

顺序表的API设计:

类名 SequenceList
构造方法 SequenceList(int capacity):创建容量为capacity的SequenceList对象
成员方法 1.public void clear():空置线性表
2.public boolean isEmpty():判断线性表是否为空,是返回true,不是返回false
3.public int length():返回线性表中的元素个数
4.public T get(int i):读取并返回线性表中的第i个元素的值
5.public void insert(int i,T t ):在线性表的第i个元素插入一个值为t的数据元素
6.public void insert(T t):在线性表中插入一个值为t的数据元素
7.public T remove(int i):删除并返回线性表中的第i个元素
8.public int indexOf(T t):返回线性表中首次出现指定的数据元素的位置,若不存在返回-1
成员变量 1.private T[] else:存储元素的数组
2.private int N :当前线性表的长度

代码实现:

public class SequenceList<T> {
    private T[] eles;
    private int N;
    public SequenceList(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){
        return eles[i];
    }
    public void insert(int i,T t ){
        //先把i索引处的元素及后面的元素一次向后移动一位
        for (int index = N-1;index > i;index--){
            eles[index] = eles[index-1];
        }
        eles[i] = t;
        N++;
    }
    public void insert(T t){
        eles[N++] = t;
    }
    public T remove(int i){
        T current = eles[i];
        //索引i后的元素依次移动一位
        for (int index = i; index < N-1; index++) {
            eles[index] = eles[index+1];
        }
        N--;
        return current;
    }
    public int indexOf(T t){
        for (int i = 0; i < N; i++) {
            if(eles.equals(t)){
                return i;
            }
        }
        return -1;
    }

}

1.1.2顺序表的遍历

在java中,遍历集合的方式一般都使用的foreach循环,如果想让我们的SequenceList支持foreach循环,则需要:
1.让SequenceList实现Iterable接口,重写Iterable方法;
2.在SequenceList内部提供一个内部类SIterator,实现Iterator接口,重写hasNext方法和next方法;

代码实现:

import java.util.Iterator;

public class SequenceList<T> implements Iterable{
    private T[] eles;
    private int N;
    public SequenceList(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){
        return eles[i];
    }
    public void insert(int i,T t ){
        //先把i索引处的元素及后面的元素一次向后移动一位
        for (int index = N-1;index > i;index--){
            eles[index] = eles[index-1];
        }
        eles[i] = t;
        N++;
    }
    public void insert(T t){
        eles[N++] = t;
    }
    public T remove(int i){
        T current = eles[i];
        //索引i后的元素依次移动一位
        for (int index = i; index < N-1; index++) {
            eles[index] = eles[index+1];
        }
        N--;
        return current;
    }
    public int indexOf(T t){
        for (int i = 0; i < N; i++) {
            if(eles.equals(t)){
                return i;
            }
        }
        return -1;
    }

    @Override
    public Iterator iterator() {
        return new SIterator();
    }
    private class SIterator implements Iterator{
        private int cusor;
        public SIterator(){
            this.cusor = 0;
        }
        @Override
        public boolean hasNext() {
            return cusor<N;
        }

        @Override
        public Object next() {
            return eles[cusor++];
        }
    }
}

1.1.3顺序表的可变

1.添加元素时:
添加元素时,应该检查当前数组的大小是否能容纳新的元素,吐过不能容纳,则需要创建新的容量更大的数组。创建的新数组一般是原数组容量的2倍。
2.移除元素时:
移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存空间的浪费,应该创建一个容量更小的数组存储元素。如果发现数据元素不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素。

代码实现:

import java.util.Iterator;

public class SequenceList<T> implements Iterable{
    private T[] eles;
    private int N;
    public SequenceList(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){
        return eles[i];
    }
    public void insert(int i,T t ){
        if(N == eles.length){
            resize(2*eles.length);
        }
        //先把i索引处的元素及后面的元素一次向后移动一位
        for (int index = N-1;index > i;index--){
            eles[index] = eles[index-1];
        }
        eles[i] = t;
        N++;
    }
    public void insert(T t){
        if(N == eles.length){
            resize(2*eles.length);
        }
        eles[N++] = t;
    }
    public T remove(int i){
        T current = eles[i];
        //索引i后的元素依次移动一位
        for (int index = i; index < N-1; index++) {
            eles[index] = eles[index+1];
        }
        N--;
        if(N < eles.length/4){
            resize(eles.length/2);
        }
        return current;
    }
    public int indexOf(T t){
        for (int i = 0; i < N; i++) {
            if(eles.equals(t)){
                return i;
            }
        }
        return -1;
    }

    //根据参数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 Iterator iterator() {
        return new SIterator();
    }
    private class SIterator implements Iterator{
        private int cusor;
        public SIterator(){
            this.cusor = 0;
        }
        @Override
        public boolean hasNext() {
            return cusor<N;
        }

        @Override
        public Object next() {
            return eles[cusor++];
        }
    }
}

1.2链表

结点API设计:

类名 Node
构造方法 Node(T t,Node next):创建Node对象
成员变量 T item:存储数据
Node next:指向下一个结点

代码实现:

public class Node {
    //存储元素
    public T item;
    //指向下一个结点
    public Node next;
    public Node(T item,Node next){
        this.item = item;
        this.next = next;
    }
}

生成链表:

public static void main(String[] args) throws Exception{
        //构造结点
        Node<Integer> first = new Node<Integer>(11,null);
        Node<Integer> second = new Node<Integer>(13,null);
        Node<Integer> third = new Node<Integer>(12,null);
        Node<Integer> fourth = new Node<Integer>(8,null);
        Node<Integer> fifth = new Node<Integer>(9,null);
        //生成链表
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
}

1.2.1单向链表

单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。

1.2.1.1单向链表API设计

类名 LinkList
构造方法 LinkList():创建LinkList对象
成员方法 1.public void clear():空置线性表
2.public boolean isEmpty():判断线性表是否为空,是返回true,否返回false
3.public int length():获取线性表中的元素个数
4.public T get(int i):读取并返回线性表中的第i个元素的值
5.public void insert(T t):往线性表中添加一个元素
6.public void insert(int i,T t):在线性表的第i个元素前插入一个值为t的数据元素
7.public T remove(int i):删除并返回线性表中的第i个数据元素
8.public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在返回-1
成员内部类 private class Node:结点类
成员变量 1.pivate Node head:记录首结点
2.private int N:记录链表的长度

代码实现:

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;
    }

    public T get(int i){
        //通过循环,从头结点开始,寻找i次,就可以找到对应元素
        Node n = head.next;
        for (int index = 0; index < i; index++) {
            n = n.next;
        }
        return n.item;
    }

    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-1; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点,并且新结点需要指向原来i位置的结点
        Node newNode = new Node(t,curr);
        //原来i位置的前一个结点指向新结点
        pre.next = newNode;
        //元素的个数+1
        N++;
    }

    public T remove(int i){
        //先找到i位置的前一个结点
        Node pre = head;
        for (int index = 0; index < i-1; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nextNode = curr.next;
        //前一个结点指向下一个结点
        pre.next = nextNode;
        N--;
        return curr.item;
    }

    public int indexOf(T t){
        //遍历列表,取出item和t比较,如果相同则找出首次的结点位置
        Node n = head;
        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(){
            this.n = head;
        }
        @Override
        public boolean hasNext() {
            return n.next != null;
        }

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

1.2.2双向链表

双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点豆油一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。

1.2.2.1 结点API设计

类名 Node
构造方法 Node(T t,Node pre,Node next):创建Node对象
成员变量 T item:存储数据
Node next:指向下一个结点
Node pre:指向上一个结点

1.2.2.2双向链表API设计

类名 TowWayLinkList
构造方法 TowWayLinkList():创建TowWayLinkList对象
成员方法 1.public void clear():空置线性表
2.public boolean isEmpty():判断线性表是否为空,是返回true,否返回false
3.public int length():获取线性表中的元素个数
4.public T get(int i):读取并返回线性表中的第i个元素的值
5.public void insert(T t):往线性表中添加一个元素
6.public void insert(int i,T t):在线性表的第i个元素前插入一个值为t的数据元素
7.public T remove(int i):删除并返回线性表中的第i个数据元素
8.public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在返回-1
9.public T getFirst():获取第一个元素
10.public T getLast():获取最后一个元素
成员内部类 private class Node:结点类
成员变量 1.private Node first:记录首结点
2.private Node last:记录尾结点
3.private int N:记录链表的长度

代码实现:

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;
    }
    //插入元素
    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;
        }
        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;
        //创建新结点
        Node newNode = new Node(t,pre,curr);
        //让i位置的前一个结点的下一个结点变为新结点
        pre.next = newNode;
        //让i位置的前一个结点变为新结点
        curr.pre = newNode;
        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.next.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;
        N--;
        return curr.item;
    }
    @Override
    public Iterator 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;
        }
    }
}

1.3链表反转

链表反转的API设计:

public void reverse():对整个链表进行反转
public Node reverse(Node curr):反转链表中的某个结点curr,并把反转后的curr结点返回

使用递归可以完成反转,递归反转其实就是从原链表的第一个存数据的结点开始,依次递归调用反转每一个结点,知道把最后一个结点反转完毕,整个链表就反转完毕。

代码实现:

    //用来反转整个链表
    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;
        return curr;
    }
}

1.4快慢指针

快慢指针找中间值(链表):

public static String getMid(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;
    }
    return slow.item;
}

快慢指针查找单向链表是否有环问题:

public static boolean isCirlce(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;
        if(fast.equals(slow)){
            return true;
        }
    }
    return false;
}

快慢指针查找有环链表入口问题:

public static Node getEntrance(Node<String> first){
    //定义两个指针
    Node<String> fast = first;
    Node<String> slow = first;
    Node<String> temp = null;
    //遍历列表,先找到环,准备一个临时指针,指向链表的首结点,继续遍历,当慢指针与临时指针相遇的结点就是环入口
    while(fast != null && fast.next != null){
        //变换fast和slow的值
        fast = fast.next.next;
        slow = slow.next;
        if(fast.equals(slow)){
            temp = first;
            continue;
        }
        //让临时结点变换
        if(temp != null){
            temp = temp.next;
            //找出临时指针与慢指针相遇的位置
            if(temp.equals(slow)){
                break;
            }
        }
    }
    return temp;
}

1.3栈

栈是一种基于先进后出的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。

压栈:数据进入到栈的动作。
弹栈:数据从栈中出去的动作。

1.3.1栈的实现

栈的API设计

类名 Stack
构造方法 Stackt():创建Stack对象
成员方法 2.public boolean isEmpty():判断栈是否为空,是返回true,否返回false
3.public int size():获取栈中的元素个数
4.public T pop(): 弹出栈顶元素
5.public void push(T t):向栈中压入元素
成员内部类 private class Node:结点类
成员变量 1.pivate Node head:记录首结点
2.private int N:记录栈的元素个数

代码实现:

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;
    }
    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;
        N++;
    }
    //弹出栈顶元素
    public T pop() {
        //找到首结点指向的第一个结点
        Node oldFirst = head.next;
        if (oldFirst == null) {
            return null;
        }
        //让首结点指向原来第一个结点的下一个结点
        head.next = oldFirst.next;
        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;
        }
    }
}

//测试代码
import algorithm.linear.Stack;

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");
        stack.push("e");

        for (String item:stack
             ) {
            System.out.println(item);
        }
        System.out.println("-------------------------------------------");
        //测试弹栈
        String  result = stack.pop();
        System.out.println("弹出的元素是"+result);
        System.out.println("剩余元素个数"+stack.size());
    }
}

1.3.2栈的应用案例

1.3.2.1括号匹配问题

问题描述:给定一个字符串,里面可能包含()小括号和其他字符,请编写程序检查该字符串的中的小括号是否成对出现

代码实现:

import java.util.Scanner;
import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.next();
        boolean match = isMatch(s);
        System.out.println(match);
    }
    public static boolean isMatch(String s){
        //1.创建栈对象,用来存储左括号
        Stack<String> chars = new Stack<>();
        //2.从左往右遍历字符串
        for (int i = 0; i < s.length(); i++) {
            String currChar = s.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;
        }else{
            return false;
        }
    }
}

1.3.2.2逆波兰表达式求值问题

中缀表达式:二元运算符总是置于两个操作数中间
逆波兰表达式(后缀表达式):运算符总是放在跟它相关的操作数之后

中缀表达式 逆波兰表达式
a+b ab+
a+(b-c) abc-+
a+(b-c)*d abc-d*+
a*(b-c)+d abc-*d+

需求:给定一个只包含加减乘除四种运算的逆波兰表达式的数组表达方式,求出该逆波兰表达式的结果。

import java.util.Stack;

public class ReversePolishBitationTest {
    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[] notaion){
        //1.定义一个栈,用来存储操作数
        Stack<Integer> oprands = new Stack<>();
        //2.从左往右遍历逆波兰表达式,得到每一个元素
        for (int i = 0; i < notaion.length; i++) {
            String curr = notaion[i];
            //3.判断当前元素式运算符还是操作数
            Integer o1;
            Integer o2;
            Integer result;
            switch (curr){
                //4.运算符从栈中弹出两个操作数完成运算,运算完的结果再压入栈中
                case "+":
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 + o1;
                    oprands.push(result);
                    break;
                case "-":
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 - o1;
                    oprands.push(result);
                    break;
                case "*":
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 * o1;
                    oprands.push(result);
                    break;
                case "/":
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 / o1;
                    oprands.push(result);
                    break;
                default:
                    //5.操作数把该操作数放入到栈中
                    oprands.push(Integer.parseInt(curr));
                    break;
            }
        }

        //6.得到栈中最后一个元素,就是逆波兰表达式的结果
        int result = oprands.pop();
        return result;
    }
}

1.4队列

队列是一种先进先出的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特许线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先被取出。

队列的API设计:

类名 Queue
构造方法 Queue():创建Queue对象
成员方法 1.public boolean isEmpty():判断列表是否为空,是返回true,反之返回false
2.public int size():获取队列中元素的个数
3.public T dequeue():从队列中取出一个元素
4.public void enqueue(T t):往队列中插入一个元素
成员变量 1.private Node head:记录首结点
2.private int N:当前栈的元素个数
3.private Node last:记录最后一个结点
成员内部类 private class Node:结点类

代码实现:

import java.util.Iterator;

public class Queue<T> implements Iterable<T>{
    private Node head;
    private Node last;
    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 Queue(){
        this.head = new Node(null,null);
        this.last = null;
        this.N = 0;
    }

    public boolean isEmpty(){
        return N==0;
    }

    public int size(){
        return N;
    }
    //向队列中插入元素
    public void enqueue(T t){
        //当前尾结点last为null
        if(last == null){
            last = new Node(t,null);
            head.next = last;
        }else{
            //当前尾结点last为null
            Node oldlast = last;
            last = new Node(t,null);
            oldlast.next = last;
        }
        N++;
    }
    //从队列中取出一个元素
    public T dequeue(){
        if(isEmpty()){
            return null;
        }
        Node oldfirst = head.next;
        head.next = oldfirst;
        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;
        }
    }
}

//测试代码
import algorithm.linear.Queue;

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

你可能感兴趣的:(数据结构,算法,链表)