目录
一、线性表:顺序表与链表。
(1)顺序表。
(1)顺序表的代码实现。
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)时间复杂度。
(3)ArrayList的底层原理。
我们添加数据时调用的方法,它会调用add私有重载方法。
如果数组还没满就添加数据,满了就先扩容,再添加数据。
扩容1个数据。
(2)链表。
(1)单向链表。
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)双向链表。
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实现。
平时调用的方法:
对应的方法体:
内部类:
(4)链表的时间复杂度分析。
(5)单链表反转。
//单链表反转实现
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)快慢指针。
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)单向链表是否有环。
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个结点也是环的入口,所以两者这个时候同时走,相遇的结点就是环的入口。
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)定义:
(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)案例-括号匹配问题。
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)定义:
(2)逻辑图。
(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;
}
}
三、线性表:队列。
队列(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());
}
}