Data Structures and Algorithms Four Linear Tables

a linear table

A linear table is the most basic, simplest, and most commonly used data structure. A linear list is a finite sequence of n data elements with the same properties.
insert image description here
Predecessor element:
If A element is in front of B element, then A is called B’s predecessor element
Subsequent element:
If B element is behind A element, then B is called A’s successor element

The characteristics of the linear table: There is a "one-to-one" logical relationship between data elements.

  1. The first data element has no predecessor, this data element is called the head node;
  2. The last data element has no successor, this data element is called the tail node;
  3. Except for the first and last data elements, other data elements have one and only one predecessor and one successor.

If the linear table is defined in mathematical language, it can be expressed as (a1,...ai-1,ai,ai+1,...an), ai-1 is ahead of ai, ai is ahead of ai+1, called ai-1 is the precursor element of ai, and ai+1 is the successor element of ai

insert image description here
Classification of linear tables:
The data storage method in linear tables can be sequential storage or chain storage. According to the different storage methods of data, linear tables can be divided into sequential tables and linked lists.

1.1 Sequence table

The sequential table is a linear table stored in the form of an array in the computer memory. The sequential storage of the linear table refers to using a group of storage units with continuous addresses to store each element in the linear table in sequence, so that the logical structure of the linear table is consistent. Adjacent data elements are stored in adjacent physical storage units, that is, the logical adjacency relationship between data elements is reflected through the adjacency relationship of physical storage of data elements.
insert image description here

1.1.1 Implementation of sequence table

Sequence Table API Design:
insert image description here
Code Implementation of Sequence Table:

//顺序表代码
public class SequenceList<T> {
    
    
	//存储元素的数组
	private T[] eles;
	//记录当前顺序表中的元素个数
	private int N;
	//构造方法
	public SequenceList(int capacity){
    
    
		eles = (T[])new Object[capacity];
		N=0;
	}
	//将一个线性表置为空表
	public void clear(){
    
    
		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){
    
    
			throw new RuntimeException("当前表已满");
		}
		eles[N++] = t;
	}
	//在i元素处插入元素t
	public void insert(int i,T t){
    
    
		if (i==eles.length){
    
    
			throw new RuntimeException("当前表已满");
		}
		if (i<0 || i>N){
    
    
			throw new RuntimeException("插入的位置不合法");
		}
		//把i位置空出来,i位置及其后面的元素依次向后移动一位
		for (int index=N;index>i;index--){
    
    
			eles[index]=eles[index-1];
		}
		//把t放到i位置处
		eles[i]=t;
		//元素数量+1
		N++;
	}
	//删除指定位置i处的元素,并返回该元素
	public T remove(int i){
    
    
		if (i<0 || i>N-1){
    
    
			throw new RuntimeException("当前要删除的元素不存在");
		}
		//记录i位置处的元素
		T result = eles[i];
		//把i位置后面的元素都向前移动一位
		for (int index=i;index<N-1;index++){
    
    
			eles[index]=eles[index+1];
		}
		//当前元素数量-1
		N--;
		return result;
	}
	//查找t元素第一次出现的位置
	public int indexOf(T t){
    
    
		if(t==null){
    
    
			throw new RuntimeException("查找的元素不合法");
		}
		for (int i = 0; i < N; i++) {
    
    
			if (eles[i].equals(t)){
    
    
				return i;
			}
		}
		return -1;
	}
}

//测试代码
public class SequenceListTest {
    
    
	public static void main(String[] args) {
    
    
		//创建顺序表对象
		SequenceList<String> sl = new SequenceList<>(10);
		//测试插入
		sl.insert("姚明");
		sl.insert("科比");
		sl.insert("麦迪");
		sl.insert(1,"詹姆斯");
		//测试获取
		String getResult = sl.get(1);
		System.out.println("获取索引1处的结果为:"+getResult);
		//测试删除
		String removeResult = sl.remove(0);
		System.out.println("删除的元素是:"+removeResult);
		//测试清空
		sl.clear();
		System.out.println("清空后的线性表中的元素个数为:"+sl.length());
	}
}

1.1.2 Traversal of sequence table

Generally, as a container to store data, it is necessary to provide a traversal method to the outside, so we need to provide a traversal method for the sequence table.
In java, the method of traversing a collection is generally the foreach loop. If we want our SequenceList to also support the foreach loop, we need to do the following:

  1. Let SequenceList implement the Iterable interface and rewrite the iterator method;
  2. Provide an internal class SIterator inside the SequenceList, implement the Iterator interface, and rewrite the hasNext method and the next method;

code:

//顺序表代码
import java.util.Iterator;
public class SequenceList<T> implements Iterable<T>{
    
    
	//存储元素的数组
	private T[] eles;
	//记录当前顺序表中的元素个数
	private int N;
	//构造方法
	public SequenceList(int capacity){
    
    
		eles = (T[])new Object[capacity];
		N=0;
	}
	//将一个线性表置为空表
	public void clear(){
    
    
		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){
    
    
			throw new RuntimeException("当前表已满");
		}
		eles[N++] = t;
	}
	//在i元素处插入元素t
	public void insert(int i,T t){
    
    
		if (i==eles.length){
    
    
			throw new RuntimeException("当前表已满");
		}
		if (i<0 || i>N){
    
    
			throw new RuntimeException("插入的位置不合法");
		}
		//把i位置空出来,i位置及其后面的元素依次向后移动一位
		for (int index=N;index>i;index--){
    
    
			eles[index]=eles[index-1];
		}
		//把t放到i位置处
		eles[i]=t;
		//元素数量+1
		N++;
	}
	//删除指定位置i处的元素,并返回该元素
	public T remove(int i){
    
    
		if (i<0 || i>N-1){
    
    
			throw new RuntimeException("当前要删除的元素不存在");
		}
		//记录i位置处的元素
		T result = eles[i];
		//把i位置后面的元素都向前移动一位
		for (int index=i;index<N-1;index++){
    
    
			eles[index]=eles[index+1];
		}
		//当前元素数量-1
		N--;
		return result;
	}
	//查找t元素第一次出现的位置
	public int indexOf(T t){
    
    
		if(t==null){
    
    
			throw new RuntimeException("查找的元素不合法");
		}
		for (int i = 0; i < N; i++) {
    
    
			if (eles[i].equals(t)){
    
    
				return i;
			}
		}
		return -1;
	}
	//打印当前线性表的元素
	public void showEles(){
    
    
		for (int i = 0; i < N; i++) {
    
    
			System.out.print(eles[i]+" ");
		}
		System.out.println();
	}
	@Override
	public Iterator iterator() {
    
    
		return new SIterator();
	}
	private class SIterator implements Iterator{
    
    
		private int cur;
		public SIterator(){
    
    
			this.cur=0;
		}
		@Override
		public boolean hasNext() {
    
    
			return cur<N;
		}
		@Override
		public T next() {
    
    
			return eles[cur++];
		}
	}
}
//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		SequenceList<String> squence = new SequenceList<>(5);
		//测试遍历
		squence.insert(0, "姚明");
		squence.insert(1, "科比");
		squence.insert(2, "麦迪");
		squence.insert(3, "艾佛森");
		squence.insert(4, "卡特");
		for (String s : squence) {
    
    
			System.out.println(s);
		}
	}
}

1.1.3 The capacity of the sequence table is variable

In the previous implementation, when we use SequenceList, we first create an object with new SequenceList(5). When creating an object, we need to specify the size of the container and initialize an array of the specified size to store elements. When we insert an element, if it has been inserted If you want to continue inserting data after 5 elements have been entered, an error will be reported and you will not be able to insert. This design does not conform to the design concept of the container, so when we design the sequence table, we should consider the scalability of its capacity.

Considering the capacity scalability of the container is actually changing the size of the array storing data elements, so we need to consider when we need to change the size of the array?

1. When adding elements:
When adding elements, you should check whether the size of the current array can accommodate the new elements. If not, you need to create a new array with a larger capacity. Here we create a new one that is twice the capacity of the original array. Arrays store elements.
insert image description here
2. When removing elements:
When removing elements, you should check whether the size of the current array is too large. For example, if you are using an array with a capacity of 100 to store 10 elements, this will cause a waste of memory space. You should create a smaller capacity An array of storage elements. If we find that the number of data elements is less than 1/4 of the capacity of the array, create a new array storage element that is 1/2 of the capacity of the original array.

insert image description here
Variable capacity code for sequence table:

//顺序表代码
public class SequenceList<T> implements Iterable<T>{
    
    
	//存储元素的数组
	private T[] eles;
	//记录当前顺序表中的元素个数
	private int N;
	//构造方法
	public SequenceList(int capacity){
    
    
		eles = (T[])new Object[capacity];
		N=0;
	}
	//将一个线性表置为空表
	public void clear(){
    
    
		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(eles.length*2);
		}
		eles[N++] = t;
	}
	//在i元素处插入元素t
	public void insert(int i,T t){
    
    
		if (i<0 || i>N){
    
    
			throw new RuntimeException("插入的位置不合法");
		}
		//元素已经放满了数组,需要扩容
		if (N==eles.length){
    
    
			resize(eles.length*2);
		}
		//把i位置空出来,i位置及其后面的元素依次向后移动一位
		for (int index=N-1;index>i;index--){
    
    
			eles[index]=eles[index-1];
		}
		//把t放到i位置处
		eles[i]=t;
		//元素数量+1
		N++;
	}
	//删除指定位置i处的元素,并返回该元素
	public T remove(int i){
    
    
		if (i<0 || i>N-1){
    
    
			throw new RuntimeException("当前要删除的元素不存在");
		}
		//记录i位置处的元素
		T result = eles[i];
		//把i位置后面的元素都向前移动一位
		for (int index=i;index<N-1;index++){
    
    
			eles[index]=eles[index+1];
		}
		//当前元素数量-1
		N--;
		//当元素已经不足数组大小的1/4,则重置数组的大小
		if (N>0 && N<eles.length/4){
    
    
			resize(eles.length/2);
		}
		return result;
	}
	//查找t元素第一次出现的位置
	public int indexOf(T t){
    
    
		if(t==null){
    
    
			throw new RuntimeException("查找的元素不合法");
		}
		for (int i = 0; i < N; i++) {
    
    
			if (eles[i].equals(t)){
    
    
				return i;
			}
		}
		return -1;
	}
	//打印当前线性表的元素
	public void showEles(){
    
    
		for (int i = 0; i < N; i++) {
    
    
			System.out.print(eles[i]+" ");
		}
		System.out.println();
	}
	@Override
	public Iterator iterator() {
    
    
		return new SIterator();
	}
	private class SIterator implements Iterator{
    
    
		private int cur;
		public SIterator(){
    
    
			this.cur=0;
		}
		@Override
		public boolean hasNext() {
    
    
			return cur<N;
		}
		@Override
		public T next() {
    
    
			return eles[cur++];
		}
	}
	//改变容量
	private void resize(int newSize){
    
    
		//记录旧数组
		T[] temp = eles;
		//创建新数组
		eles = (T[]) new Object[newSize];
		//把旧数组中的元素拷贝到新数组
		for (int i = 0; i < N; i++) {
    
    
			eles[i] = temp[i];
		}
	}
	public int capacity(){
    
    
		return eles.length;
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		SequenceList<String> squence = new SequenceList<>(5);
		//测试遍历
		squence.insert(0, "姚明");
		squence.insert(1, "科比");
		squence.insert(2, "麦迪");
		squence.insert(3, "艾佛森");
		squence.insert(4, "卡特");
		System.out.println(squence.capacity());
		squence.insert(5,"aa");
		System.out.println(squence.capacity());
		squence.insert(5,"aa");
		squence.insert(5,"aa");
		squence.insert(5,"aa");
		squence.insert(5,"aa");
		squence.insert(5,"aa");
		System.out.println(squence.capacity());
		squence.remove(1);
		squence.remove(1);
		squence.remove(1);
		squence.remove(1);
		squence.remove(1);
		squence.remove(1);
		squence.remove(1);
		System.out.println(squence.capacity());
	}
}

1.1.4 Time complexity of sequence table

get(i): It is not difficult to see that no matter how large the number of data elements N is, only one eles[i] is needed to obtain the corresponding elements, so the time complexity is O(1);

insert(int i, T t): For each insertion, the element behind the i position needs to be moved once. As the number of elements N increases, more elements are moved, and the time complexity is O(n);

remove(int i): Every time you delete, you need to move the element behind the i position once. As the amount of data N increases, more elements are moved, and the time complexity is O(n);

Since the bottom layer of the sequence table is implemented by an array, the length of the array is fixed, so the container expansion operation is involved in the operation process. This will cause the time complexity of the sequential table to be non-linear during use. At some nodes that need to be expanded, the time-consuming will suddenly increase, especially the more elements, the more obvious this problem

1.1.5 Implementation of ArrayList in java

The bottom layer of the ArrayList collection in java is also a sequential table, which is implemented using an array, and also provides functions such as addition, deletion, modification, query, and capacity expansion.

  1. Whether to implement it with an array;
  2. Is there any expansion operation?
  3. Is there a traversal method provided;

1.2 Linked list

We have implemented a linear table using a sequential storage structure before, and we will find that although the query of the sequential table is fast and the time complexity is O(1), the efficiency of addition and deletion is relatively low, because each addition and deletion operation is accompanied by a large number of The data elements move. Is there any solution to this problem? Yes, we can use another storage structure to implement a linear table and a chained storage structure.

A linked list is a non-sequential and non-sequential storage structure on a physical storage unit. Its physical structure cannot intuitively represent the logical order of data elements. The logical order of data elements is realized through the link order of pointers in the linked list. The linked list is composed of a series of nodes (each element in the linked list is called a node), and the nodes can be dynamically generated at runtime.
insert image description here
insert image description here
So how do we use linked lists? According to the object-oriented thinking, we can design a class to describe the node, use an attribute to describe the elements stored in this node, and use another attribute to describe the next node of this node.

Node API design:
insert image description here
node class implementation:

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

Generate linked list:

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 Singly linked list

Singly linked list is a kind of linked list, which is composed of multiple nodes, each node is composed of a data field and a pointer field, the data field is used to store data, and the pointer field is used to point to its successor node. The data field of the head node of the linked list does not store data, and the pointer field points to the first node that actually stores data .

insert image description here

1.2.1.1 Singly linked list API design

insert image description here

1.2.1.2 One-way linked list code implementation

//单向列表代码
import java.util.Iterator;
public class LinkList<T> implements Iterable<T> {
    
    
	//记录头结点
	private Node head;
	//记录链表的长度
	private int N;
	public LinkList(){
    
    
		//初始化头结点
		head = new Node(null,null);
		N=0;
	}
	//清空链表
	public void clear(){
    
    
		head.next=null;
		head.item=null;
		N=0;
	}
	//获取链表的长度
	public int length(){
    
    
		return N;
	}
	//判断链表是否为空
	public boolean isEmpty(){
    
    
		return N==0;
	}
	//获取指定位置i出的元素
	public T get(int i){
    
    
		if (i<0||i>=N){
    
    
			throw new RuntimeException("位置不合法!");
		}
		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;
		}
		Node newNode = new Node(t, null);
		n.next = newNode;
		//链表长度+1
		N++;
	}
	//向指定位置i处,添加元素t
	public void insert(int i,T t){
    
    
		if (i<0||i>=N){
    
    
			throw new RuntimeException("位置不合法!");
		}
		//寻找位置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);
		//让之前的结点指向新结点
		pre.next = newNode;
		//长度+1
		N++;
	}
	//删除指定位置i处的元素,并返回被删除的元素
	public T remove(int i){
    
    
		if (i<0 || i>=N){
    
    
			throw new RuntimeException("位置不合法");
		}
		//寻找i之前的元素
		Node pre = head;
		for (int index = 0; index <=i-1; index++) {
    
    
			pre = pre.next;
		}
		//当前i位置的结点
		Node curr = pre.next;
		//前一个结点指向下一个结点,删除当前结点
		pre.next = curr.next;
		//长度-1
		N--;
		return curr.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;
	}
	//结点类
	private class Node{
    
    
	//存储数据
	T item;
	//下一个结点
	Node next;
	public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
	}
	@Override
	public Iterator iterator() {
    
    
		return new LIterator();
	}
	private class LIterator implements Iterator<T>{
    
    
		private Node n;
		public LIterator() {
    
    
			this.n = head;
		}
		@Override
		public boolean hasNext() {
    
    
			return n.next!=null;
		}
		@Override
		public T next() {
    
    
		n = n.next;
			return n.item;
		}
	}
}
//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		LinkList<String> list = new LinkList<>();
		list.insert(0,"张三");
		list.insert(1,"李四");
		list.insert(2,"王五");
		list.insert(3,"赵六");
		//测试length方法
		for (String s : list) {
    
    
			System.out.println(s);
		}
		System.out.println(list.length());
		System.out.println("-------------------");
		//测试get方法
		System.out.println(list.get(2));
		System.out.println("------------------------");
		//测试remove方法
		String remove = list.remove(1);
		System.out.println(remove);
		System.out.println(list.length());
		System.out.println("----------------");;
		for (String s : list) {
    
    
			System.out.println(s);
		}
	}
}

1.2.2 Doubly linked list

A doubly linked list is also called a doubly linked list. It is a kind of linked list. It consists of multiple nodes. Each node consists of a data field and two pointer fields. The data field is used to store data, and one of the pointer fields is used to store data. Point to its successor node, another pointer field is used to point to the predecessor node. The data field of the head node of the linked list does not store data, the value of the pointer field pointing to the predecessor node is null, and the pointer field pointing to the successor node points to the first node that actually stores data. According to the object-oriented thinking, we need to
insert image description here
design A class to describe the node. Since the node belongs to the linked list, we implement the node class as an internal class of the linked list class

1.2.2.1 Node API design

insert image description here

1.2.2.2 Doubly linked list API design

insert image description here

1.2.2.3 Doubly linked list code implementation

//双向链表代码
import java.util.Iterator;
public class TowWayLinkList<T> implements Iterable<T>{
    
    
	//首结点
	private Node head;
	//最后一个结点
	private Node last;
	//链表的长度
	private int N;
	public TowWayLinkList() {
    
    
		last = null;
		head = new Node(null,null,null);
		N=0;
	}
	//清空链表
	public void clear(){
    
    
		last=null;
		head.next=last;
		head.pre=null;
		head.item=null;
		N=0;
	}
	//获取链表长度
	public int length(){
    
    
		return N;
	}
	//判断链表是否为空
	public boolean isEmpty(){
    
    
		return N==0;
	}
	//插入元素t
	public void insert(T t){
    
    
		if (last==null){
    
    
			last = new Node(t,head,null);
			head.next = last;
		}else{
    
    
			Node oldLast = last;
			Node node = new Node(t, oldLast, null);
			oldLast.next = node;
			last = node;
		}
		//长度+1
		N++;
	}
	//向指定位置i处插入元素t
	public void insert(int i,T t){
    
    
		if (i<0 || i>=N){
    
    
			throw new RuntimeException("位置不合法");
		}
		//找到位置i的前一个结点
		Node pre = head;
		for (int index = 0; index < i; index++) {
    
    
			pre = pre.next;
		}
		//当前结点
		Node curr = pre.next;
		//构建新结点
		Node newNode = new Node(t, pre, curr);
		curr.pre= newNode;
		pre.next = newNode;
		//长度+1
		N++;
	}
	//获取指定位置i处的元素
	public T get(int i){
    
    
		if (i<0||i>=N){
    
    
			throw new RuntimeException("位置不合法");
		}
		//寻找当前结点
		Node curr = head.next;
		for (int index = 0; index <i; index++) {
    
    
			curr = curr.next;
		}
		return curr.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){
    
    
		if (i<0 || i>=N){
    
    
			throw new RuntimeException("位置不合法");
		}
		//寻找i位置的前一个元素
		Node pre = head;
		for (int index = 0; index <i ; index++) {
    
    
			pre = pre.next;
		}
		//i位置的元素
		Node curr = pre.next;
		//i位置的下一个元素
		Node curr_next = curr.next;
		pre.next = curr_next;
		curr_next.pre = pre;
		//长度-1;
		N--;
		return curr.item;
	}
	//获取第一个元素
	public T getFirst(){
    
    
		if (isEmpty()){
    
    
			return null;
		}
		return head.next.item;
	}
	//获取最后一个元素
	public T getLast(){
    
    
		if (isEmpty()){
    
    
			return null;
		}
		return last.item;
	}
	@Override
	public Iterator<T> iterator() {
    
    
		return new TIterator();
	}
	private class TIterator implements Iterator{
    
    
		private Node n = head;
		@Override
		public boolean hasNext() {
    
    
			return n.next!=null;
		}
		@Override
		public Object next() {
    
    
			n = n.next;
			return n.item;
		}
	}
	//结点类
	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 class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		TowWayLinkList<String> list = new TowWayLinkList<>();
		list.insert("乔峰");
		list.insert("虚竹");
		list.insert("段誉");
		list.insert(1,"鸠摩智");
		list.insert(3,"叶二娘");
		for (String str : list) {
    
    
			System.out.println(str);
		}
		System.out.println("----------------------");
		String tow = list.get(2);
		System.out.println(tow);
		System.out.println("-------------------------");
		String remove = list.remove(3);
		System.out.println(remove);
		System.out.println(list.length());
		System.out.println("--------------------");
		System.out.println(list.getFirst());
		System.out.println(list.getLast());
	}
}

1.2.2.4 Implementation of LinkedList in java

The LinkedList collection in java is also implemented using a doubly linked list, and provides related methods such as adding, deleting, modifying and checking

  1. Whether the bottom layer is implemented with a doubly linked list;
  2. Does the node class have three fields

1.2.3 Complexity Analysis of Linked List

get(int i): Each query needs to start from the head of the linked list and search backward in turn. With the increase of data elements N, the more elements are compared, the time complexity is O(n)

insert(int i, T t): For each insertion, you need to find the previous element at position i, and then complete the insertion operation. As the number of data elements N increases, more elements are searched, and the time complexity is O(n) ;

remove(int i): For each removal, you need to find the previous element at position i, and then complete the insertion operation. As the number of data elements N increases, more elements are searched, and the time complexity is O(n)

Compared with the sequential list, although the time complexity of the insertion and deletion of the linked list is the same, it still has a great advantage, because the physical address of the linked list is not continuous, it does not need to pre-specify the size of the storage space, or involve in the storage process Operations such as expansion, while it does not involve the exchange of elements.

Compared with the sequential table, the query operation performance of the linked list will be relatively low. Therefore, if there are many query operations in our program, it is recommended to use a sequence table, and if there are many addition and deletion operations, it is recommended to use a linked list.

1.2.4 Linked list reversal

The inversion of a singly linked list is a high-frequency topic in interviews.

Requirement:
The data in the original linked list is: 1->2->3>4

The data in the linked list after inversion is: 4->3->2->1
Inversion API:

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

Use recursion to complete the reversal. Recursive reversal actually starts from the first node storing data in the original linked list, and recursively calls and reverses each node in turn until the last node is reversed, and the entire linked list is reversed. Turn over.
insert image description here

the code

public void reverse(){
    
    
	if (N==0){
    
    
		//当前是空链表,不需要反转
		return;
	}
	reverse(head.next);
}
/**
*
* @param curr 当前遍历的结点
* @return 反转后当前结点上一个结点
*/
public Node reverse(Node curr){
    
    
	//已经到了最后一个元素
	if (curr.next==null){
    
    
		//反转后,头结点应该指向原链表中的最后一个元素
		head.next=curr;
		return curr;
	}
	//当前结点的上一个结点
	Node pre = reverse(curr.next);
	pre.next = curr;
	//当前结点的下一个结点设为null
	curr.next=null;
	//返回当前结点
	return curr;
}
//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		LinkList<Integer> list = new LinkList<>();
		list.insert(1);
		list.insert(2);
		list.insert(3);
		list.insert(4);
		for (Integer i : list) {
    
    
			System.out.print(i+" ");
		}
		System.out.println();
		System.out.println("--------------------");
		list.reverse();
		for (Integer i : list) {
    
    
			System.out.print(i+" ");
		}
	}
}

1.2.5 Fast and slow pointer

The fast and slow pointer refers to the definition of two pointers. The moving speed of these two pointers is one by one, and the speed of the two pointers is slow, so as to create the difference we want. This difference can let us find the corresponding node on the linked list. In general, the moving step of the fast pointer is twice that of the slow pointer

1.2.5.1 The median problem

Let's first look at the following piece of code, and then complete the requirements.

//测试类
public class Test {
    
    
	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;
		//查找中间值
		String mid = getMid(first);
		System.out.println("中间值为:"+mid);
	}
	
	/**
	* @param first 链表的首结点
	* @return 链表的中间结点的值
	*/
	public static String getMid(Node<String> first) {
    
    
		return null;
	}
	//结点类
	private static class Node<T> {
    
    
		//存储数据
		T item;
		//下一个结点
		Node next;
		public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
   }
}

Requirement:
Please improve the getMid method in the test class Test, which can find out the value of the middle element of the linked list and return it.
Using the speed pointer, we regard a linked list as a runway. Assuming that the speed of a is twice that of b, then when a runs through the distance, b runs just halfway, so as to achieve the purpose of finding the intermediate node.
As shown in the figure below, at the beginning, both slow and fast pointers point to the first node of the linked list, and then slow moves one pointer at a time, and fast moves two pointers at a time.
insert image description hereinsert image description here
the code

/**
* @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=fast.next.next;
		slow=slow.next;
	}
	return slow.item;
}

1.2.5.2 Whether the one-way linked list has a ring problem

insert image description here
Look at the following code to complete the requirements:

//测试类
public class Test {
    
    
	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;
		//判断链表是否有环
		boolean circle = isCircle(first);
		System.out.println("first链表中是否有环:"+circle);
	}
	/**
	* 判断链表中是否有环
	* @param first 链表首结点
	* @return ture为有环,false为无环
	*/
	public static boolean isCircle(Node<String> first) {
    
    
		return false;
	}
	//结点类
	private static class Node<T> {
    
    
		//存储数据
		T item;
		//下一个结点
		Node next;
		public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
	}
}

Requirement:
Please improve the isCircle method in the test class Test to return whether there is a circle in the linked list.

Using the idea of ​​fast and slow pointers, it is better to compare the linked list to a runway. There are rings in the linked list, then this runway is a circular runway. In a circular runway, two people have a speed difference. If they meet, it means that there is a ring.
insert image description here
insert image description here
insert image description here
insert image description here

/**
* 判断链表中是否有环
* @param first 链表首结点
* @return ture为有环,false为无环
*/
public static boolean isCircle(Node<String> first) {
    
    
	Node<String> slow = first;
	Node<String> fast = first;
	while(fast!=null && fast.next!=null){
    
    
		fast = fast.next.next;
		slow = slow.next;
		if (fast.equals(slow)){
    
    
			return true;
		}
	}
	return false;
}

1.2.5.3 Entrance problem of linked list

Also look at the following code to complete the requirements:

//测试类
public class Test {
    
    
	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;
		//查找环的入口结点
		Node<String> entrance = getEntrance(first);
		System.out.println("first链表中环的入口结点元素为:"+entrance.item);
	}
	/**
	* 查找有环链表中环的入口结点
	* @param first 链表首结点
	* @return 环的入口结点
	*/
	public static Node getEntrance(Node<String> first) {
    
    
		return null;
	}
	//结点类
	private static class Node<T> {
    
    
		//存储数据
		T item;
		//下一个结点
		Node next;
		public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
	}
}

Requirement:
Please improve the getEntrance method in the Test class to find the entry node of the ring in the ring list.

When the fast and slow pointers meet, we can judge that there is a ring in the linked list. At this time, reset a new pointer to point to the starting point of the linked list, and the step size is 1 as the slow pointer, then the place where the slow pointer meets the "new" pointer is Entrance to the ring. The proof of this conclusion involves the knowledge of number theory, which is omitted here, and only the realization is mentioned.
insert image description here
insert image description here
code:

/**
* 查找有环链表中环的入口结点
* @param first 链表首结点
* @return 环的入口结点
*/
public static Node getEntrance(Node<String> first) {
    
    
	Node<String> slow = first;
	Node<String> fast = first;
	Node<String> temp = null;
	while(fast!=null && fast.next!=null){
    
    
		fast = fast.next.next;
		slow=slow.next;
		if (fast.equals(slow)){
    
    
			temp = first;
			continue;
		}
		if (temp!=null){
    
    
			temp=temp.next;
			if (temp.equals(slow)){
    
    
				return temp;
			}
		}
	}
	return null;
}

1.2.6 Circular linked list

Circular linked list, as the name suggests, the linked list should form a circular ring as a whole. In a singly linked list, the pointer of the last node is null and does not point to any node, because there is no next element. To implement a circular linked list, we only need to make the pointer of the last node of the singly linked list point to the head node.

insert image description here
Construction of circular linked list:

public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		//构建结点
		Node<Integer> first = new Node<Integer>(1, null);
		Node<Integer> second = new Node<Integer>(2, null);
		Node<Integer> third = new Node<Integer>(3, null);
		Node<Integer> fourth = new Node<Integer>(4, null);
		Node<Integer> fifth = new Node<Integer>(5, null);
		Node<Integer> six = new Node<Integer>(6, null);
		Node<Integer> seven = new Node<Integer>(7, null);
		//构建单链表
		first.next = second;
		second.next = third;
		third.next = fourth;
		fourth.next = fifth;
		fifth.next = six;
		six.next = seven;
		//构建循环链表,让最后一个结点指向第一个结点
		seven.next = first;
	}
}

1.2.7 Joseph problem

Description of the problem:
There is a legend that after the Romans occupied Jotapat, 39 Jews hid in a cave with Joseph and his friends. The 39 Jews decided that they would rather die than be caught by the enemy, so they decided to A suicide method, 41 people line up in a circle, the first person starts counting from 1, and then in turn, if someone counts to 3, then this person must commit suicide, and then his next person starts from 1 again. Start counting until everyone commits suicide. However Joseph and his friends did not want to comply. Therefore, Joseph asked his friend to pretend to obey first, and he arranged his friend and himself in the 16th and 31st positions, thereby escaping the death game.

Problem conversion:
41 people sit in a circle, the first person is numbered 1, the second person is numbered 2, and the nth person is numbered n.

  1. The person numbered 1 starts counting from 1, and then goes backwards, and the person who counts 3 exits the circle;
  2. The next person starting from the person who exited starts counting from 1 again, and so on;
  3. Find the number of the person who exited last.

Graphic:
insert image description here
Problem-solving ideas:

  1. Construct a one-way circular linked list with 41 nodes, store the values ​​from 1 to 41, representing the 41 people respectively;
  2. Use the counter count to record the value of the current report;
  3. Traversing the linked list, each cycle, count++;
  4. Determine the value of count, if it is 3, delete this node from the linked list and print the value of the node, and reset count to 0;
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
	//1.构建循环链表
	Node<Integer> first = null;
	//记录前一个结点
	Node<Integer> pre = null;
	for (int i = 1; i <= 41; i++) {
    
    
		//第一个元素
		if (i==1){
    
    
		first = new Node(i,null);
			pre = first;
			continue;
		}
		Node<Integer> node = new Node<>(i,null);
		pre.next = node;
		pre = node;
		if (i==41){
    
    
			//构建循环链表,让最后一个结点指向第一个结点
			pre.next=first;
		}
	}
	//2.使用count,记录当前的报数值
	int count=0;
	//3.遍历链表,每循环一次,count++
	Node<Integer> n = first;
	Node<Integer> before = null;
	while(n!=n.next){
    
    
		//4.判断count的值,如果是3,则从链表中删除这个结点并打印结点的值,把count重置为0;
		count++;
		if (count==3){
    
    
			//删除当前结点
			before.next = n.next;
			System.out.print(n.item+",");
			count=0;
			n = n.next;
		}else{
    
    
			before=n;
			n = n.next;
			}
		}
		/*打印剩余的最后那个人*/
		System.out.println(n.item);
	}
}

1.3 stack

1.3.1 Stack overview

1.3.1.1 The stack in life

The place where goods are stored or where passengers are accommodated can be extended to warehouses and transfer stations. For example, the hotel we live in today was called an inn in ancient times, and it was a place for tourists to rest. Passengers can enter the inn to rest, and leave the inn after the rest.

1.3.1.2 The stack in the computer

We introduce the concept of the stack in life into the computer, which is a place for data to rest. It is a data structure, and data can enter and exit the stack.

The stack is a data structure based on first-in-last-out (FILO), which is a special linear table that can only perform insertion and deletion operations at one end. It stores data according to the principle of first-in-last-out. The data that enters first is pushed to the bottom of the stack, and the last data is on the top of the stack. When data needs to be read, data is popped from the top of the stack (the last data is read out first).

We call the action of data entering the stack as pushing, and the action of data going out of the stack as popping (popping).
insert image description here

1.3.2 Implementation of the stack

1.3.2.1 Stack API design

insert image description here

1.3.2.2 Stack code implementation

//栈代码
import java.util.Iterator;
public class Stack<T> implements Iterable<T>{
    
    
	//记录首结点
	private Node head;
	//栈中元素的个数
	private int N;
	public Stack() {
    
    
		head = new Node(null,null);
		N=0;
	}
	//判断当前栈中元素个数是否为0
	public boolean isEmpty(){
    
    
		return N==0;
	}
	//把t元素压入栈
	public void push(T t){
    
    
		Node oldNext = head.next;
		Node node = new Node(t, oldNext);
		head.next = node;
		//个数+1
		N++;
	}
	//弹出栈顶元素
	public T pop(){
    
    
		Node oldNext = head.next;
		if (oldNext==null){
    
    
			return null;
		}
		//删除首个元素
		head.next = head.next.next;
		//个数-1
		N--;
		return oldNext.item;
	}
	//获取栈中元素的个数
	public int size(){
    
    
		return N;
	}
	@Override
	public Iterator<T> iterator() {
    
    
		return new SIterator();
	}
	private class SIterator implements Iterator<T>{
    
    
		private Node n = head;
		@Override
		public boolean hasNext() {
    
    
			return n.next!=null;
		}
		@Override
		public T next() {
    
    
			Node node = n.next;
			n = n.next;
			return node.item;
		}
	}
	private class Node{
    
    
		public T item;
		public Node next;
		public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Stack<String> stack = new Stack<>();
		stack.push("a");
		stack.push("b");
		stack.push("c");
		stack.push("d");
		for (String str : stack) {
    
    
			System.out.print(str+" ");
		}
		System.out.println("-----------------------------");
		String result = stack.pop();
		System.out.println("弹出了元素:"+result);
		System.out.println(stack.size());
	}
}

1.3.3 Case

1.3.3.1 Bracket matching problem

Problem Description

Given a string, which may contain "()" parentheses and other characters, please write a program to check whether the parentheses in the string appear in pairs.
For example:

"(上海)(长安)":正确匹配
"上海((长安))":正确匹配
"上海(长安(北京)(深圳)南京)":正确匹配
"上海(长安))":错误匹配
"((上海)长安":错误匹配

sample code

public class BracketsMatch {
    
    
	public static void main(String[] args) {
    
    
		String str = "(上海(长安)())";
		boolean match = isMatch(str);
		System.out.println(str+"中的括号是否匹配:"+match);
	}
	/**
	* 判断str中的括号是否匹配
	* @param str 括号组成的字符串
	* @return 如果匹配,返回true,如果不匹配,返回false
	*/
	public static boolean isMatch(String str){
    
    
		return false;
	}
}

Please improve the isMath method.

analyze:

  1. Create a stack to store left parentheses
  2. Traverse the string from left to right, get each character
  3. Determine whether the character is a left parenthesis, if so, store it on the stack
  4. Determine whether the character is a right bracket, if not, continue to the next cycle
  5. If the character is a right parenthesis, an element t is popped from the stack;
  6. Determine whether the element t is null, if not, prove that there is a corresponding left parenthesis, if not, prove that there is no corresponding left parenthesis
  7. After the loop ends, judge whether there are left parentheses left in the stack, if there is, it will not match, if not, it will match

insert image description here

public class BracketsMatch {
    
    
    public static void main(String[] args) {
    
    
        String str = "(fdafds(fafds)())";
        boolean match = isMatch(str);
        System.out.println(str + "中的括号是否匹配:" + match);
    }

    /**
     * 判断str中的括号是否匹配
     *
     * @param str 括号组成的字符串
     * @return 如果匹配,返回true,如果不匹配,返回false
     */
    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.判断该字符是不是右括号,如果不是,继续下一次循环
                //5.如果该字符是右括号,则从栈中弹出一个元素t;
                String t = chars.pop();
                //6.判断元素t是否为null,如果不是,则证明有对应的左括号,如果不是,则证明没有对应的左括号
                if (t == null) {
    
    
                    return false;
                }
            }
        }
        //7.循环结束后,判断栈中还有没有剩余的左括号,如果有,则不匹配,如果没有,则匹配
        if (chars.size() == 0) {
    
    
            return true;
        } else {
    
    
            return false;
        }
    }
}

1.3.3.2 Inverse Polish expression evaluation problem

The reverse Polish expression evaluation problem is a kind of problem that we often encounter in computers. To study and understand this problem, we must first figure out what is a reverse Polish expression? To understand reverse Polish expressions, we have to start with infix expressions.

Infix expressions:
Infix expressions are expressions used in our daily life, for example: 1+3*2, 2-(1+3), etc. The characteristics of infix expressions are: binary operators always is placed between the two operands.

Infix expressions are people's favorite expression because they are simple and easy to understand. But this is not the case for computers, because the order of operations of infix expressions is not regular. Different operators have different priorities. If the computer executes infix expressions, it needs to analyze the expression semantics and do a lot of priority-related operations.

Reverse Polish expression (suffix expression):
Reverse Polish expression is a representation method of expression first proposed by Polish logician J. Lukasewicz in 1929. The characteristics of suffix expression The : operator is always placed after its associated operand.

insert image description here
Requirements:
Given an array representation of an inverse Polish expression that only includes four operations of addition, subtraction, multiplication, and division, find the result of the inverse Polish expression.

public class ReversePolishNotation {
    
    
	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);
	}
	/**
	* @param notaion 逆波兰表达式的数组表示方式
	* @return 逆波兰表达式的计算结果
	*/
	public static int caculate(String[] notaion){
    
    
		return -1;
	}
}

Improve the caculate method to calculate the result of the reverse Polish expression.

analyze:

  1. Create a stack object oprands to store operands
  2. Traverse the reverse Polish expression from left to right to get each string
  3. Determine whether the string is an operator, if not, push the operand into the oprands stack
  4. If it is an operator, pop the two operands o1, o2 from the oprands stack
  5. Use this operator to calculate o1 and o2 and get the result result
  6. Push the result onto the oprands stack
  7. After the traversal, take out the final result in the stack and return

insert image description here
the code

public class ReversePolishNotation {
    
    
	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);
	}
/**
* @param notaion 逆波兰表达式的数组表示方式
* @return 逆波兰表达式的计算结果
*/
public static int caculate(String[] notaion) {
    
    
	//1.创建一个栈对象oprands存储操作数
	Stack<Integer> oprands = new Stack<>();
	//2.从左往右遍历逆波兰表达式,得到每一个字符串
	for (int i = 0; i < notaion.length; i++) {
    
    
		String curr = notaion[i];
		//3.判断该字符串是不是运算符,如果不是,把该该操作数压入oprands栈中
		Integer o1;
		Integer o2;
		Integer result;
		switch (curr) {
    
    
			case "+":
				//4.如果是运算符,则从oprands栈中弹出两个操作数o1,o2
				o1 = oprands.pop();
				o2 = oprands.pop();
				//5.使用该运算符计算o1和o2,得到结果result
				result = o2 + o1;
				//6.把该结果压入oprands栈中
				oprands.push(result);
				break;
			case "-":
				//4.如果是运算符,则从oprands栈中弹出两个操作数o1,o2
				o1 = oprands.pop();
				o2 = oprands.pop();
				//5.使用该运算符计算o1和o2,得到结果result
				result = o2 - o1;
				//6.把该结果压入oprands栈中
				oprands.push(result);
				break;
			case "*":
				//4.如果是运算符,则从oprands栈中弹出两个操作数o1,o2
				o1 = oprands.pop();
				o2 = oprands.pop();
				//5.使用该运算符计算o1和o2,得到结果result
				result = o2 * o1;
				//6.把该结果压入oprands栈中
				oprands.push(result);
				break;
			case "/":
				//4.如果是运算符,则从oprands栈中弹出两个操作数o1,o2
				o1 = oprands.pop();
				o2 = oprands.pop();
				//5.使用该运算符计算o1和o2,得到结果result
				result = o2 / o1;
				//6.把该结果压入oprands栈中
				oprands.push(result);
				break;
			default:
				oprands.push(Integer.parseInt(curr));
				break;
		}
	}
	//7.遍历结束后,拿出栈中最终的结果返回
	Integer result = oprands.pop();
	return result;
	}
}

1.4 Queue

Queue is a data structure based on first-in-first-out (FIFO). It is a special linear table that can only be inserted at one end and deleted at the other end. It stores data according to the principle of first-in-first-out, and the data that enters first , when reading data, the first read is read out.
insert image description here

1.4.1 API design of the queue

insert image description here

1.4.2 Implementation of Queue

//队列代码
import java.util.Iterator;
public class Queue<T> implements Iterable<T>{
    
    
	//记录首结点
	private Node head;
	//记录最后一个结点
	private Node last;
	//记录队列中元素的个数
	private int N;
	public Queue() {
    
    
		head = new Node(null,null);
		last=null;
		N=0;
	}
	//判断队列是否为空
	public boolean isEmpty(){
    
    
		return N==0;
	}
	//返回队列中元素的个数
	public int size(){
    
    
		return N;
	}
	//向队列中插入元素t
	public void enqueue(T t){
    
    
		if (last==null){
    
    
			last = new Node(t,null);
			head.next=last;
		}else{
    
    
			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--;
		if (isEmpty()){
    
    
			last=null;
		}
		return oldFirst.item;
	}
	@Override
	public Iterator<T> iterator() {
    
    
		return new QIterator();
	}
	private class QIterator implements Iterator<T>{
    
    
		private Node n = head;
		@Override
		public boolean hasNext() {
    
    
			return n.next!=null;
		}
		@Override
		public T next() {
    
    
			Node node = n.next;
			n = n.next;
			return node.item;
		}
	}
	private class Node{
    
    
		public T item;
		public Node next;
		public Node(T item, Node next) {
    
    
			this.item = item;
			this.next = next;
		}
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Queue<String> queue = new Queue<>();
		queue.enqueue("a");
		queue.enqueue("b");
		queue.enqueue("c");
		queue.enqueue("d");
		for (String str : queue) {
    
    
			System.out.print(str+" ");
		}
		System.out.println("-----------------------------");
		String result = queue.dequeue();
		System.out.println("出列了元素:"+result);
		System.out.println(queue.size());
	}
}

Guess you like

Origin blog.csdn.net/qq_33417321/article/details/121967181