# [data structure and algorithm] Chapter 5: linear table (sequence table, linked list, stack, queue, symbol table)

Keywords: Algorithm data structure linked list

# 5. Linear table

Linear table is the most basic, simplest and most commonly used data structure. A linear table is a finite sequence of n data elements with the same characteristics Precursor element: if element A is in front of element B, A is called the precursor element of B

Successor element: if element B is after element a, it is called the successor element of A

Characteristics of linear table: there is a "one-to-one" logical relationship between data elements.

1. The first data element has no precursor. This data element is called a header node

2. The last data element has no successor. This data element is called the tail node

3. In addition to the first and last data elements, other data elements have and have only one precursor 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,
ai-1 is the precursor element of ai, and ai+1 is the successor element of ai Classification of linear table: the data in linear table can be stored in sequence or chain. According to different data storage methods, linear table can be divided into sequential table and chain table

## 5.1 sequence table

The sequential table is a linear table saved in the form of an array in the computer memory. The sequential storage of the linear table refers to the sequential storage of each element in the linear table with a group of storage units with continuous addresses, so that in the logical structure of the linear table, the adjacent data elements are stored in the adjacent physical storage units, That is, the logical adjacency relationship between data elements is reflected through the adjacency relationship physically stored by data elements

Physical storage and logical adjacency are the same ### 1) Implementation of sequence table

• API design • code implementation
```package chapter03;

/**
* @author Soil flavor
* Date 2021/9/3
* @version 1.0
* Sequence table
*/
public class SequenceList<T> {
/**
* An array of storage elements
*/
private T[] eles;

/**
* Record the number of elements in the current sequence table
*/
private int n;

/**
* Capacity of the current sequential table
*/
private int capacity;

/**
* Construct an array with capacity
*
* @param capacity
*/
public SequenceList(int capacity) {
this.eles = (T[]) new Object[capacity];
this.n = 0;
this.capacity = capacity；
}

/**
* Empty table
*/
public void clear() {
n = 0;
}

/**
* Judge whether the table is empty
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}

/**
* Get the length of the table: the number of elements in the table
*
* @return
*/
public int length() {
return n;
}

/**
* Returns the element at i position in the table
*
* @param i
* @return
*/
public T get(int i) {
if (i < 0 || i >= n) {
throw new RuntimeException("No index is" + i + "Element of");
}
return eles[i];
}

/**
* Insert the element t into the table at index i
*
* @param i
* @param t
*/
public void insert(int i, T t) {
// Judge whether the table is full
if (n == capacity) {
throw new RuntimeException("Table full!");
}

// Judge whether i is legal
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal insertion position!");
}

// Move the elements after i back: move back from back to front in turn
for (int index = n; index > i; index--) {
eles[index] = eles[index - 1];
}

// Put t at i
eles[i] = t;

// Number of elements n plus 1
n++;
}

/**
* Inserts the element t to the end of the table
*
* @param t
*/
public void insert(T t) {
if (n == capacity) {
throw new RuntimeException("Table full!");
}
eles[n++] = t;
}

/**
* Delete the element with index i in the table
*
* @param i
* @return
*/
public T remove(int i) {
// Judge whether i is legal
if (i < 0 || i >= n) {
throw new RuntimeException("No index is" + i + "Element of");
}

// Record the element of the i position
T res = eles[i];

// Move the elements after i position forward in turn
for (int index = i; index < n - 1; index++) {
eles[index - 1] = eles[index];
}

// Number of elements minus 1
n--;

// Returns the deleted element
return res;
}

/**
* Index of the first occurrence of the query element t in the table
*
* @param t
* @return
*/
public int indexOf(T t) {
// Judge whether t is legal
if (t == null) {
throw new RuntimeException("Illegal element!");
}

// Traverse the array, such as matching, and return the index
for (int i = 0; i < n; i++) {
if (eles[i].equals(t)) {
return i;
}
}

return -1;
}
}
```
```public class TestSequenceList {
@Test
public void test(){
SequenceList<String> sl = new SequenceList<>(5);
//Assert.assertEquals(3,sl.length());
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert(1,"James");
sl.insert("O'Neill");
//sl.insert("Yi Jianlian");

System.out.println(sl.get(2));
System.out.println(sl.remove(3));

sl.clear();
System.out.println(sl.length());
}
}
```
```Kobe
0
```

### 2) Traversal of sequence table

Generally, when storing data as a container, you need to provide traversal mode to the outside, so you need to provide traversal mode to the sequence table.

In java, foreach loops are generally used to traverse collections. If you want your SequenceList to support foreach loops, you need to do the following:

1. Let SequenceList implement Iterable interface and rewrite iterator method;

2. Provide an internal class SIterator inside the SequenceList, implement the Iterator interface, and override hasNext method and next method;

code:

```public class SequenceList<T> implements Iterable<T>{
// Omit other methods

/**
* Get iterator
* @return
*/
@Override
public Iterator<T> iterator() {
return new Siterator();
}

/**
* Inner class: iterator
*/
private class Siterator implements Iterator{
/**
* cursor
*/
private int cursor;

/**
* constructor
*/
public Siterator() {
this.cursor = 0;
}

/**
* Determine whether there is a next
* @return
*/
@Override
public boolean hasNext() {
return cursor<n;
}

/**
* Get the next element
* @return
*/
@Override
public T next() {
return eles[cursor++];
}
}
}
```
```public void test(){
SequenceList<String> sl = new SequenceList<>(5);
//Assert.assertEquals(3,sl.length());
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert(1,"James");
sl.insert("O'Neill");
//sl.insert("Yi Jianlian");

// The underlying implementation of the enhanced for loop is the iterator, which is a simplified version of the iterator
for (String s : sl) {
System.out.println(s);
}
}
```
```Yao Ming
James
Kobe
O'Neill
```

### 3) The capacity of the sequence table is variable

When designing the sequence table, we should consider its capacity scalability

Considering the capacity scalability of the container is actually changing the size of the array storing data elements

You should check whether the size of the current array can accommodate new elements. If not, you need to create a new array with larger capacity. Here, create a new array storage element with twice the capacity of the original array • When removing elements

You should check whether the size of the current array is too large. For example, you are using an array with 100 capacity to store 10 elements, which will waste memory space. You should create an array with smaller capacity to store elements. If the number of data elements is found to be less than 1 / 4 of the capacity of the array, a new array storage element is created that is 1 / 2 of the capacity of the original array • code implementation

```public void insert(int i, T t) {
// Judge whether the table is full. If it is full, expand the capacity
if (n == capacity) {
//throw new RuntimeException("table full!");
resize(2 * capacity);
}
//....
}

public void insert(T t) {
// Judge whether the table is full. If it is full, expand the capacity
if (n == capacity) {
//throw new RuntimeException("table full!");
resize(2 * capacity);
}
eles[n++] = t;
}

public T remove(int i) {
// ....
// If the number of elements is less than 1 / 4 of the capacity, change the capacity to 1 / 2
if (n < capacity / 4) {
resize(capacity / 2);
}
// ....
}

/**
* Change sequence table capacity
*
* @param newCapacity
*/
private void resize(int newCapacity) {
// Record old array
T[] temp = eles;

// Create a new array
eles = (T[]) new Object[newCapacity];

// Copy old array elements to new array
for (int i = 0; i < n; i++) {
eles[i] = temp[i];
}

// Update array capacity value
capacity = newCapacity;
}
```
```public class TestSequenceList {
@Test
public void test(){
SequenceList<String> sl = new SequenceList<>(3);
//Assert.assertEquals(3,sl.length());
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert(1,"James");
sl.insert("O'Neill");
sl.insert("Yi Jianlian");

for (String s : sl) {
System.out.println(s);
}
}
}
```
```Yao Ming
James
Kobe
O'Neill
Yi Jianlian
```

### 4) Time complexity of sequence table

• get(i): no matter how large the number of data elements N is, the corresponding elements can be obtained only once eles[i], so the time complexity is O(1)
• Insert (int i, t, t): every time you insert, you need to move the elements behind I. as the number of elements N increases, the more elements you move, and the time complexity is O(n)
• remove(int i): every time you delete, you need to move the elements behind I. as the amount of data N increases, more elements will be moved, and the time complexity is O(n)
• Because the bottom layer of the sequence table is implemented by an array, and the length of the array is fixed, the container expansion operation is involved in the process of operation. In this way, the time complexity of the sequence table in the use process is not linear. At some nodes that need to be expanded, the time will increase sharply, especially the more elements, the more obvious the problem is

### 5) Implementation of ArrayList in java

The bottom layer of ArrayList set in java is also a sequence table, which is implemented by array. It also provides functions such as addition, deletion, modification, query and capacity expansion

### 1) Introduction

The sequential storage structure realizes the linear table. Although the query of the sequential table is very 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 the movement of a large number of data elements

Is there a solution to this problem? Yes, you can use another storage structure to realize linear list and chain storage structure

It is a discontinuous and non sequential storage structure on the physical storage unit. Its physical structure can not intuitively represent the logical order of data elements,
The logical order of data elements is realized through the pointer link order in the linked list.

The linked list consists of a series of nodes (each element in the linked list is called a node), which can be generated dynamically at run time   How do we use the linked list? According to the object-oriented idea, we can design a class to describe the node, use one attribute to describe the element stored in the node, and use another attribute to describe the next node of the node • Node class implementation
```public class Node<T> {
/**
* Storage element
*/
public T item;
/**
* Point to the next node
*/
public Node next;

/**
* constructor
*
* @param item
* @param next
*/
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
```
```public static void main(String[] args) {
Node<Integer> n1 = new Node<>(11, null);
Node<Integer> n2 = new Node<>(13, null);
Node<Integer> n3 = new Node<>(15, null);
Node<Integer> n4 = new Node<>(18, null);
Node<Integer> n5 = new Node<>(9, null);
Node<Integer> n6 = new Node<>(5, null);

n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
}
```

Unidirectional linked list is a kind of linked list. It 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 subsequent nodes. 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 #### 1. One way linked list API design #### 2. Code implementation

```package chapter03;

import java.util.Iterator;

/**
* @author Soil flavor
* Date 2021/9/3
* @version 1.0
*/
public class LinkList<T> implements Iterable<T> {
/**
*/
/**
*/
private int n;

/**
* constructor
*/
this.n = 0;
}

/**
*/
public void clear() {
n = 0;
}

/**
*
* @return
*/
public int length() {
return n;
}

/**
* Determine whether the linked list is empty
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}

/**
* Gets the element at i
*
* @param i
* @return
*/
public T get(int i) {
// Validity of test i
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal location!");
}

// Traverse the linked list and find i
for (int index = 0; index < i; index++) {
node = node.next;
}
return node.item;
}

/**
*
* @param t
*/
public void insert(T t) {
// Find the last node
while (lastNode.next != null) {
lastNode = lastNode.next;
}

// Create a new node
Node newNode = new Node(t, null);

// Point the last node to the new node
lastNode.next = newNode;

// Number of links plus 1
n++;
}

/**
*
* @param i
* @param t
*/
public void insert(int i, T t) {
// Test parameter validity
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal location!");
}

// Find the previous node at i
for (int index = 0; index < i; index++) {
preNode = preNode.next;
}

// Get the node at i
Node node = preNode.next;

// Create a new node and point to the node at i
Node newNode = new Node(t, node);

// Point the previous node at i to the new node
preNode.next = newNode;

// Number of linked lists plus 1
n++;
}

/**
* Delete the element at i in the linked list
*
* @param i
* @return
*/
public T remove(int i) {
// Test parameter validity
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal location!");
}

// Find the previous node at i
for (int index = 0; index < i; index++) {
preNode = preNode.next;
}

// Get the node at i
Node node = preNode.next;

// Point the node before i to the next node at i
preNode.next = node.next;

// List length minus 1
n--;

return node.item;
}

/**
* Gets the position where the element first appears in the linked list
*
* @param t
* @return
*/
public int indexOf(T t) {
// The cycle condition can be: n.next= null
for (int i = 0; i < n; i++) {
if (node.next.item.equals(t)) {
return i;
}
}
return -1;
}

/**
* Get iterator
*
* @return
*/
@Override
public Iterator<T> iterator() {
return new LIterator();
}

/**
* Inner class: iterator
*/
private class LIterator implements Iterator {
/**
* current node
*/
private Node node;

/**
* constructor
*/
public LIterator() {
}

/**
* Judge whether the current node has a next node
* @return
*/
@Override
public boolean hasNext() {
return node.next != null;
}

/**
* Get the data of the next node
* @return
*/
@Override
public T next() {
// Set the next node as the current node
node = node.next;
return node.item;
}
}

/**
* Internal node class
*/
private class Node {
// Store data
T item;
// Next node
Node next;

/**
* constructor
*
* @param item
* @param next
*/
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
```
```@Test
public void test(){
//Assert.assertEquals(3,sl.length());
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert(1,"James");
sl.insert("O'Neill");
sl.insert("Yi Jianlian");

for (String s : sl) {
System.out.println(s);
}

System.out.println();
System.out.println(sl.get(2));
System.out.println(sl.remove(3));

sl.clear();
System.out.println(sl.length());
}
```
```Yao Ming
James
Kobe
O'Neill
Yi Jianlian

Kobe
0
```

Bidirectional linked list, also known as bidirectional list, is a kind of linked list. It is composed of multiple nodes. Each node is composed of a data field and two pointer fields

The data field is used to store data. One pointer field is used to point to its successor node, and the other 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 pointer field value 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 #### 1. Node API design #### 2. Bi directional linked list API design #### 3. Code implementation

```package chapter03;

import java.util.Iterator;

/**
* @author Soil flavor
* Date 2021/9/3
* @version 1.0
*/
public class TwoWayLinkList<T> implements Iterable<T> {
/**
*/
/**
* Tail node
*/
private Node last;
/**
*/
private int n;

/**
* constructor
*/
this.head = new Node(null, null, null);
this.last = null;
this.n = 0;
}

/**
* Empty table
*/
public void clear() {
last = null;
n = 0;
}

/**
* Determine whether the linked list is empty
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}

/**
* Get the length of the linked list
*
* @return
*/
public int length() {
return n;
}

/**
* Get i elements
*
* @param i
* @return
*/
public T get(int i) {
// Validity of test parameters
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal location!");
}

// ergodic
for (int index = 0; index < i; index++) {
node = node.next;
}
return node.item;
}

/**
* Insert an element at the end of the linked list
*
* @param t
*/
public void insert(T t) {
if (isEmpty()) {
// The linked list is empty
// Create a new node
Node newNode = new Node(t, head, null);
// Make the new node the tail node
last = newNode;

// Let the head node point to the tail node
} else {
// Linked list is not empty
Node oldLast = last;
// Create a new node
Node newNode = new Node(t, oldLast, null);

// Make the current tail node point to the new node
oldLast.next = newNode;

// Make the new node the tail node
last = newNode;
}
// Number of linked lists plus 1
n++;
}

/**
* Insert element t at i
*
* @param i
* @param t
*/
public void insert(int i, T t) {
// Test parameter validity
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal insertion position!");
}

// Find the previous node at position i
for (int index = 0; index < i; index++) {
preNode = preNode.next;
}

// Get the node at i position
Node currentNode = preNode.next;

// Create a new node
Node newNode = new Node(t, preNode, currentNode);

// Modify the next pointer of the previous node at i position to a new node
preNode.next = newNode;

// Modify the pre pointer of the i position node to a new node
currentNode.pre = newNode;

// Number of links plus 1
n++;
}

/**
* Delete element at i
*
* @param i
* @return
*/
public T remove(int i) {
// Test parameter validity
if (i < 0 || i >= n) {
throw new RuntimeException("Illegal deletion location!");
}

// Find the previous node at position i
for (int index = 0; index < i; index++) {
preNode = preNode.next;
}

// Find i location node
Node currentNode = preNode.next;

// Find the next node at i position; If currentNode is the last node, nextNode=null
Node nextNode = currentNode.next;

// Modify the next pointer of the previous node at i position to the next node at i position
preNode.next = nextNode;

// Modify the pre pointer of the next node at i position to the previous node at i position
if (i < n - 1) {
// Non tail node
nextNode.pre = preNode;
} else {
// Tail node
// nextNode is null and nextNode.pre is a null pointer
last = preNode;
}

// The number of linked lists minus 1
n--;

return currentNode.item;
}

/**
* Get the position where element t first appears
*
* @param t
* @return
*/
public int indexOf(T t) {
for (int i = 0; i < n; i++) {
if (node.next.item.equals(t)) {
return i;
}
}
return -1;
}

/**
* Get the first element (non head node)
*
* @return
*/
public T getFirst() {
if (isEmpty()) {
return null;
}
}

/**
* Get end element
*
* @return
*/
public T getLast() {
if (isEmpty()) {
return null;
}
return last.item;
}

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

/**
* Internal node class
*/
private class Node {
/**
* Store data
*/
private T item;
/**
* Previous node
*/
private Node pre;
/**
* Next node
*/
private Node next;

/**
* constructor
*
* @param item
* @param pre
* @param next
*/
public Node(T item, Node pre, Node next) {
this.item = item;
this.pre = pre;
this.next = next;
}
}

/**
* Inner class: iterator
*/
private class TIterator implements Iterator {
/**
* current node
*/
private Node node;

/**
* constructor
*/
public TIterator() {
}

/**
* Is there a next element
*
* @return
*/
@Override
public boolean hasNext() {
return node.next != null;
}

/**
* Get the next element
*
* @return
*/
@Override
public T next() {
node = node.next;
return node.item;
}
}
}
```
```@Test
public void test2(){
//Assert.assertEquals(3,sl.length());
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert(1,"James");
sl.insert("O'Neill");
sl.insert("Yi Jianlian");

for (String s : sl) {
System.out.println(s);
}

System.out.println();
System.out.println(sl.get(0));
System.out.println(sl.remove(0));

System.out.println();
System.out.println(sl.getFirst());
System.out.println(sl.getLast());

sl.clear();
System.out.println(sl.length());
}
```
```Yao Ming
James
Kobe
O'Neill
Yi Jianlian

Yao Ming
Yao Ming

James
Yi Jianlian
0
```

#### 4. Implementation of LinkedList in java

The LinkedList set in java is also implemented using a two-way linked list, and provides related methods such as addition, deletion, modification and query

### 3) Linked list complexity analysis

• get(int i): each query needs to start from the head of the linked list and look back in turn. As the number of data elements N increases, 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 first, and then complete the insertion operation. With the increase of data element N, the more elements to find, and the time complexity is O(n)

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

Compared with the sequential list, the insertion and deletion time complexity of the linked list is the same, but it still has great advantages, because the physical address of the linked list is discontinuous, the storage space size does not need to be specified in advance, does not involve operations such as capacity expansion, and does not involve the exchange of elements
Compared with the sequential list, the query performance of the linked list will be lower. Therefore, if there are many query operations in the program, it is recommended to use sequential list, and there are many addition and deletion operations, it is recommended to use linked list

The reversal of single linked list is a high-frequency topic in the interview

• Requirement: the data in the original linked list is: 1 - > 2 - > 3 > 4. After inversion, the data in the linked list is: 4 - > 3 - > 2 - > 1
• Reverse API: • principle

Using recursion can complete the inversion. In fact, recursive inversion starts from the first node of the original linked list to store data, and recursively calls to reverse each node in turn until the last node is reversed, and the whole linked list is reversed

Inversion is to modify the point of the node pointer • code implementation
```	/**
*/
public void reverse() {
// If the element is empty or there is only one element, there is no need to reverse
if (n < 2) {
return;
}

}

/**
* Invert the current node
*
* @param curr
* @return Reverse curr and return
*/
private Node reverse(Node curr) {
// If it is a tail node, point the head node to the tail node and return
if (curr.next == null) {
// The head node points to the tail node
return curr;
}
//The previous node of the current node
Node pre = reverse(curr.next);
pre.next = curr;
//The next node of the current node is set to null
curr.next = null;
//Returns the current node
return curr;
}
```
```@Test
public void test3(){
sl.insert("Yao Ming");
sl.insert("Kobe");
//sl.insert("James");
//sl.insert("O'Neill");
//sl.insert("Yi Jianlian");

for (String s : sl) {
System.out.println(s);
}

System.out.println("----------------");

sl.reverse();
for (String s : sl) {
System.out.println(s);
}
}
```
```Yao Ming
Kobe
----------------
Kobe
Yao Ming
```

Use the debug debugging mode in Idea, cooperate with F7 and F8, carefully analyze the operation process, and run it several times

### 5) Speed pointer

Speed pointer refers to defining two pointers. The movement speed of the two pointers is fast and slow, so as to create the desired difference. 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. Intermediate value problem

• principle

Using the speed pointer, a linked list is regarded as a runway. Assuming that the speed of a is twice that of b, then when a runs the full range, b just runs half, so as to find the intermediate node

As shown in the following figure, at first, the slow and fast pointers point to the first node of the linked list, then slow moves one pointer at a time, and fast moves two pointers at a time • code implementation
```    /**
* Find intermediate nodes with fast and slow pointers
* @return
*/
public T getMid(){
// Define speed pointers: both point to the first element node
// Traverse the linked list: when the fast pointer reaches the end of the linked list, it ends
while(fast!=null && fast.next!=null){
// Go two steps at a time
fast = fast.next.next;
// The slow pointer takes one step at a time
slow = slow.next;
}
return slow.item;
}
```
```    @Test
public void testGetMid(){
sl.insert("Yao Ming");
sl.insert("Kobe");
sl.insert("James");
sl.insert("O'Neill");
//sl.insert("Yi Jianlian");

for (String s : sl) {
System.out.println(s);
}

System.out.println("-----------");
System.out.println(sl.getMid());
}
```
```Yao Ming
Kobe
James
O'Neill
-----------
```

#### 2. Is there a ring problem with the one-way linked list • principle

Using the idea of speed pointer, compare the linked list to a runway. If there are rings in the linked list, then this runway is a circular runway. In a circular runway, two people have a speed difference, so sooner or later, two people will meet. As long as they meet, it means there are rings   • code implementation
```    /**
* @param first First node of linked list
* @return ture Is ring, false is acyclic
*/
public static boolean isCircle(Node<String> first) {
Node<String> slow = first;
Node<String> fast = first;
// If there is a loop, fast will never be null, and there must be exit judgment conditions in the loop
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if (fast.equals(slow)){
return true;
}
}
return false;
}
```

Another way to judge whether a one-way linked list has a ring:

Traverse the linked list. If the number of traversals exceeds the number of elements in the linked list, it is a ring; If the number of traversals is equal to the number of elements and can exit, it is acyclic; In this way, you only need to traverse one cycle; When the fast and slow pointer has a ring, the traversal times will exceed one cycle, because after the fast pointer reaches one cycle, it will have to catch up with the slow pointer to end

#### 3. Linked list entry problem

• principle

When the fast and slow pointers meet, it can be judged that there is a ring in the linked list. At this time, reset a new pointer 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 the entrance of the ring

Proving this conclusion involves the knowledge of number theory, which is omitted here and only about realization  Circular linked list, as the name suggests, the whole linked list should form a circular ring. In a one-way linked list, the pointer of the last node is null and does not point to any node, because there is no next element. To realize the circular linked list, you only need to make the pointer of the last node of the one-way linked list point to the head node ### 7) Joseph problem

• Problem description

It is said that after the Romans occupied jotapat, 39 Jews hid in a cave with Joseph and his friends. 39 Jews decided to die rather than be caught by the enemy, so they decided to commit suicide. 41 people lined up in a circle. The first person counted off from 1, and then, if someone counted off to 3, Then this person must commit suicide, and then his next person starts counting again from 1 until everyone commits suicide. However, Joseph and his friends did not want to comply. So Joseph asked his friend to pretend to obey first. He arranged his friend and himself in the 16th and 31st positions, so as to escape 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 with number 1 starts to count back from 1, and the person with number 3 exits the circle

2. The next person starting from the person who quit will count off from 1 again, and so on

3. Find the number of the last person to quit • Problem solving ideas

1. A one-way circular linked list with 41 nodes is constructed, and the values of 1 ~ 41 are stored respectively to represent the 41 people

2. Use the counter count to record the value of the current number of messages

3. Traverse the linked list once per cycle, count++

4. Judge the value of count. If it is 3, delete the node from the linked list, print the value of the node, and reset the count to 0

• code implementation

```package chapter03;

import org.junit.Test;

/**
* @author Soil flavor
* Date 2021/9/4
* @version 1.0
* josephus problem
*/
public class JosephTest<T> {
@Test
public void test() {
// 1. A one-way circular linked list with 41 nodes is constructed, and the values of 1 ~ 41 are stored respectively to represent the 41 people

// Record first node
Node<Integer> first = null;
// Record previous node
Node<Integer> pre = null;
for (int i = 1; i < 42; i++) {
// First node
if (i < 2) {
first = new Node<>(i, null);
pre = first;
// Jump out of this cycle
continue;
}

// Intermediate node
Node<Integer> node = new Node<>(i, null);
pre.next = node;
pre = node;

// Tail node
if (i > 40) {
node.next = first;
}
}

// 2. Use the counter count to record the value of the current number of messages
int count = 0;

// 3. Traverse the linked list once per cycle, count++

// current node
Node<Integer> n = first;
// The previous node of the current node
Node<Integer> before = null;
while (n != n.next) {
// Analog alarm
count++;
// Judge whether the count is 3
if (count == 3) {
// Equal to 3, delete the current node, print the current node, reset count, and move the current node back

// Delete current node: directly use the previous node to point to the next node
before.next = n.next;
System.out.print(n.item + ",");
// Reset count
count = 0;
// Move current node backward
n = n.next;
} else {
// Not equal to 3, the current node moves back
// Move the previous node backward and record it as the current node
before = n;
// Move current node backward
n = n.next;
}
}

// Print last element
System.out.print(n.item);
}

/**
* Internal node class
*/
private class Node<T> {
/**
* Store data
*/
private T item;
/**
* Next node
*/
private Node next;

/**
* constructor
*
* @param item
* @param next
*/
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
```
```3,6,9,12,15,18,21,24,27,30,33,36,39,1,5,10,14,19,23,28,32,37,41,7,13,20,26,34,40,8,17,29,38,11,25,2,22,4,35,16,31
```

## 5.3 stack

### 1) Stack concept

• Stack in life

The place where goods are stored or passengers stay can be extended to warehouse and transfer station. For example, hotels in modern life were called inns in ancient times. They are places for passengers to rest. Passengers can enter the inn to rest and leave the inn after rest

• Stack in computer

• We introduce the concept of stack in life into the computer, which is a place for data to rest. It is a data structure. Data can enter and exit the stack
• Stack is a data structure based on first in last out (FILO). It is a special linear table that can only be inserted and deleted at one end. It stores data according to the principle of first in and last out. The first entered data is pressed into the bottom of the stack, and the last data is at the top of the stack. When data needs to be read, data will pop up from the top of the stack (the last data will be read out first)
• We call the action of data entering the stack as pressing the stack, and the action of data leaving the stack as bouncing the stack ### 2) Implementation of stack

• API design • code implementation
```package chapter03;

import java.util.Iterator;

/**
* @author Soil flavor
* Date 2021/9/4
* @version 1.0
* Stack
*/
public class Stack<T> implements Iterable<T> {
/**
* First node
*/
/**
* Number of elements in stack
*/
private int n;

/**
* constructor
*/
public Stack() {
this.n = 0;
}

/**
* Is the stack empty
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}

/**
* Number of elements in stack
*
* @return
*/
public int size() {
return n;
}

/**
* Stack pressing
*
* @param t
*/
public void push(T t) {
// Find the first node pointed to by the first node

// Create a new node
Node newNode = new Node(t, null);

// Point the first node to the new node

// Point the new node to the original first node
newNode.next = oldFirst;

// Number of elements plus 1
n++;
}

/**
* Bomb stack
*
* @return
*/
public T pop() {
// Find the first node pointed to by the first node

// Point the first node to the second node: judge whether the first node exists
if (first != null) {
// Number of elements minus 1
n--;
// Returns the first element
return first.item;
}

// Empty stack, return null
return null;
}

/**
* iterator
*
* @return
*/
@Override
public Iterator<T> iterator() {
return new SIterator();
}

/**
* Inner class: iterator
* @param
*/
private class SIterator implements Iterator {
private Node node;

public SIterator() {
}

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

@Override
public T next() {
node = node.next;
return node.item;
}
}

/**
* Internal node class
*/
private class Node {
T item;
Node next;

public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
```
```public class StackTest {
@Test
public void test(){
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("--------");
System.out.println("eject:"+stack.pop());
System.out.println("surplus:"+stack.size());
System.out.println("--------");
for (String s : stack) {
System.out.println(s);
}
}
}
```
```d
c
b
a
--------
eject: d
Remaining: 3
--------
c
b
a
```

### 3) Case

#### 1. Bracket matching problem

A string may contain "()" parentheses and other characters. Write a program to check whether the parentheses in the string appear in pairs
For example:
"(Shanghai) (Chang'an)": correct match
"Shanghai (Chang'an)": correct match
"Shanghai (Chang'an (Beijing) (Shenzhen) Nanjing)": correct match
"Shanghai (Chang'an)": wrong match
"((Shanghai) Chang'an": wrong match

• thinking

1. Create a stack to store the left parenthesis

2. Traverse the string from left to right to get each character

3. Judge whether the character is an open bracket. If so, put it into the stack for storage

4. Judge whether the character is a closing bracket. If not, continue the next cycle

5. If the character is a closing parenthesis, an element t pops up from the stack

6. Judge whether the element t is null. If not, it proves that there is a corresponding left parenthesis. If yes, it proves that there is no corresponding left parenthesis

7. After the loop ends, judge whether there are left left parentheses in the stack. If there are, they do not match. If not, they match • code implementation
```@Test
public void test1(){
String str = "(Shanghai)((Chang'an)";
boolean match = isMatch(str);
System.out.println(str + " Whether the brackets in match:" + match);
}

/**
* Use the stack to determine whether the left and right parentheses in str match
* @param str
* @return
*/
private boolean isMatch(String str){
// Create a stack to store the left parenthesis
Stack<String> stack = new Stack<>();

// Traverse the string from left to right and take out each character
String s;
for(int i=0;i<str.length();i++){
s = str.charAt(i)+"";

// Determine whether the character is an open parenthesis
if("(".equals(s)){
// If so, push into the stack
stack.push(s);
}else if(")".equals(s)){
// If it is a closing bracket, an element pops up from the stack
String t = stack.pop();
// If the pop-up element is null, there is no corresponding left parenthesis and false is returned
if(t==null){
return false;
}
// The pop-up element is not null, indicating that there is a corresponding left parenthesis. Continue the next cycle
}
// Not left and right parentheses, continue the next cycle
}

// After the loop ends, judge whether there are extra left parentheses in the stack. If so, it will not match and return false. Otherwise, it will return true
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
```
```(Shanghai)((Chang'an) Whether the brackets in match: false
```

#### 2. Inverse Polish expression evaluation problem

The evaluation of inverse Polish expression is a kind of problem we often encounter in computers. To study and understand this problem, we must first understand what inverse Polish expression is? To understand inverse Polish expression, we must start with infix expression

• Infix expression
• Infix expressions are expressions used in our daily life, such as 1 + 3 * 2, 2 - (1 + 3), and so on
• Infix expressions are characterized by binary operators always placed between two operands
• Infix expression is people's favorite expression method, because it is simple and easy to understand. But it is not the case for computers, because the operation order of infix expression is not regular. Different operators have different priorities. If computers execute infix expression, they need to parse the expression semantics and do a lot of priority related operations
• Inverse Polish expression (suffix expression)
Inverse Polish expression is an expression representation first proposed by Polish logician J. lukasewicz in 1929. The characteristic of suffix expression is that the operator is always placed after its related operands • Requirements: given an array representation of an inverse Polish expression that contains only four operations of addition, subtraction, multiplication and division, find the result of the inverse Polish expression

• analysis

1. Create a stack object oprands to store operands

2. Traverse the inverse Polish expression from left to right to get each string

3. Judge whether the string is an operator. If not, press the operand into the oprands stack

4. If it is an operator, two operands o1,o2 will pop up from the oprands stack

5. Use this operator to calculate o1 and o2 to get the result result

6. Push the result into the oprands stack

7. After traversal, take out the final result from the stack and return it • code implementation
```public class StackTest {

@Test
public void test2(){
// The inverse Polish expression of infix expression 3 * (17-15) + 18 / 6 is as follows: result 9
String[] notation = {"3", "17", "15", "-", "*", "18", "6", "/", "+"};
int result = caculate(notation);
System.out.println("The result of the inverse Polish expression is:" + result);
}

/**
* Calculate the result of the inverse Polish expression
* @param notation  Array representation of inverse Polish expressions
* @return
*/
private int caculate(String[] notation){
// Create a stack object to store operands
Stack<Integer> oprands = new Stack<>();

// Traverse the inverse Polish expression from left to right to get each string
String curr;
Integer o1;
Integer o2;
for (int i = 0; i < notation.length; i++) {
curr = notation[i];
switch (curr){
case "+":
// If it is an operator, two operands o1,o2 will pop up from the oprands stack
o1 = oprands.pop();
o2 = oprands.pop();

// Use this operator to calculate o1 and o2, get the result, and push the result into the stack
oprands.push(o2 + o1);
break;

case "-":
// If it is an operator, two operands o1,o2 will pop up from the oprands stack
o1 = oprands.pop();
o2 = oprands.pop();

// Use this operator to calculate o1 and o2, get the result, and push the result into the stack
oprands.push(o2 - o1);
break;

case "*":
// If it is an operator, two operands o1,o2 will pop up from the oprands stack
o1 = oprands.pop();
o2 = oprands.pop();

// Use this operator to calculate o1 and o2, get the result, and push the result into the stack
oprands.push(o2 * o1);
break;

case "/":
// If it is an operator, two operands o1,o2 will pop up from the oprands stack
o1 = oprands.pop();
o2 = oprands.pop();

// Use this operator to calculate o1 and o2, get the result, and push the result into the stack
oprands.push(o2 / o1);
break;

default:
// Instead of an operator, push the operand into the oprands stack
oprands.push(Integer.parseInt(curr));
}
}

// After traversal, take out the final result from the stack and return it
return oprands.pop();
}
}
```
```The result of the inverse Polish expression is: 9
```

## 5.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. The data that enters first is read out first when reading data • API design • code implementation
```public class Queue<T> implements Iterable<T> {
/**
* First node
*/
/**
* Tail node
*/
private Node last;
/**
* Number of elements
*/
private int n;

/**
* constructor
*/
public Queue() {
this.last = null;
this.n = 0;
}

/**
* Determine whether the queue is empty
*
* @return
*/
public boolean isEmpty() {
return n == 0;
}

/**
* Number of elements in the queue
*
* @return
*/
public int size() {
return n;
}

/**
* Save data to queue
*
* @param t
*/
public void enQueue(T t) {
if (isEmpty()) {
// Queue is empty
last = new Node(t, null);
} else {
// Queue is not empty
Node oldLast = last;
last = new Node(t, null);
oldLast.next = last;
}
// Number of elements plus 1
n++;
}

/**
* Fetch data from queue
*
* @return
*/
public T deQueue() {
// When the queue is empty
if (isEmpty()) {
return null;
}

// If it is not empty, take the first node and point the first node to the second

// After fetching, judge whether the queue is empty
if (isEmpty()) {
last = null;
}

// Number of elements minus 1
n--;
return oldFirst.item;
}

/**
* iterator
*
* @return
*/
@Override
public Iterator<T> iterator() {
return new QIterator();
}

/**
* Iterator inner class
*/
private class QIterator implements Iterator<T> {
Node node;

public QIterator() {
}

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

@Override
public T next() {
node = node.next;
return node.item;
}
}

/**
* Node inner class
*/
private class Node {
T item;
Node next;

public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
```
```public class QueueTest {
@Test
public void test() {
Queue<String> queue = new Queue<>();
queue.enQueue("a");
queue.enQueue("b");
queue.enQueue("c");
queue.enQueue("d");

for (Object q : queue) {
System.out.println(q);
}
System.out.println("------------");
String s = queue.deQueue();
System.out.println("The out of queue elements are:" + s);
System.out.println("Number of queues:" + queue.size());
}
}
```
```a
b
c
d
------------
The out of queue elements are: a
Number of queues: 3
```

# 6. Symbol table

The main purpose of the symbol table is to associate a key with a value. The symbol table can store data elements as key value pair data composed of a key and a value. We can find the corresponding value according to the key In the symbol table, the key is unique

Symbol tables are widely used in real life, as shown in the following table: ## 6.1 symbol table API design ## 6.2 implementation of symbol table

```package chapter04;

/**
* @author Soil flavor
* Date 2021/9/6
* @version 1.0
* Symbol table
*/
public class SymbolTable<K, V> {
/**
* First node
*/
/**
* Number of elements
*/
private int n;

/**
* constructor
*/
public SymbolTable() {
this.head = new Node(null, null, null);
this.n = 0;
}

/**
* Get value corresponding to key
*
* @param key
* @return
*/
public V get(K key) {
node = node.next;
if (node.key.equals(key)) {
return node.value;
}
}
return null;
}

/**
* Store chain value pairs into the symbol table
*
* @param key
* @param value
*/
public void put(K key, V value) {
// If there is a node with key in the table, replace the value with value
while (node.next != null) {
// Move node pointer
node = node.next;
// judge
if (node.key.equals(key)) {
node.value = value;
return;
}
}

// If there is no node with key in the table, create a node and put it in front of the table (or at the end of the table, which is cumbersome to implement), and add 1 to the number of elements
Node newNode = new Node(key, value, oldFirst);

n++;
}

/**
* Delete key value pair of key
*
* @param key
*/
public void delete(K key) {
while (node.next != null) {
// Judge whether the key of the next node of the node is equal to the key
if (node.next.key.equals(key)) {
// Equal; point the next node of the node to the next node
node.next = node.next.next;
// Number of elements minus 1
n--;
return;
}
// Move node batch needle
node = node.next;
}
}

/**
* Get the number of elements
*
* @return
*/
public int size() {
return n;
}

/**
* Internal node class
*/
private class Node {
private K key;
private V value;
private Node next;

public Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}
```
```package chapter03;

import chapter04.SymbolTable;
import org.junit.Test;

/**
* @author Soil flavor
* Date 2021/9/6
* @version 1.0
* Test symbol table
*/
public class SymbolTableTest {
@Test
public void test(){
SymbolTable<Integer, String> st = new SymbolTable<>();
st.put(1, "a");
st.put(3, "b");
st.put(5, "c");
System.out.println(st.size());
st.put(1,"a1");
System.out.println(st.get(1));
System.out.println(st.size());
st.delete(1);
System.out.println(st.size());
}
}
```
```3
a1
3
2
```

## 6.3 ordered symbol table

The previous symbol table is called unordered symbol table, because the order of key value pairs is not considered when inserting. In real life, it is sometimes necessary to sort according to the size of keys. The order should be considered when inserting data. Next, we will implement the ordered symbol table • code implementation
```package chapter04;

/**
* @author Soil flavor
* Date 2021/9/6
* @version 1.0
* Ordered symbol table
* Sort by key when inserting
*/
public class OrderSymbolTable<K extends Comparable<K>, V> {
/**
* First node
*/
/**
* Number of elements
*/
private int n;

/**
* constructor
*/
public OrderSymbolTable() {
this.head = new Node(null, null, null);
this.n = 0;
}

/**
* Get value corresponding to key
*
* @param key
* @return
*/
public V get(K key) {
node = node.next;
if (node.key.equals(key)) {
return node.value;
}
}
return null;
}

/**
* Store chain value pairs into the symbol table
*
* @param key
* @param value
*/
public void put(K key, V value) {
// Record current node

// Record previous node

// Traverse the symbol table and skip nodes smaller than key
while (curr != null && curr.key.compareTo(key) < 0) {
pre = curr;
curr = curr.next;
}

// After skipping nodes smaller than the key, the following are nodes equal to or greater than the key
// When it is equal to key, replace value and return
if (curr != null && curr.key.compareTo(key) == 0) {
curr.value = value;
return;
}
// When it is greater than key, a new node is created and inserted in front of the current node. The number of elements is increased by 1
Node newNode = new Node(key, value, curr);
pre.next = newNode;
n++;
}

/**
* Delete key value pair of key
*
* @param key
*/
public void delete(K key) {
while (node.next != null) {
// Judge whether the key of the next node of the node is equal to the key
if (node.next.key.equals(key)) {
// equal; Point the next node to the next node
node.next = node.next.next;
// Number of elements minus 1
n--;
return;
}
// Move node batch needle
node = node.next;
}
}

/**
* Get the number of elements
*
* @return
*/
public int size() {
return n;
}

/**
* Internal node class
*/
private class Node {
private K key;
private V value;
private Node next;

public Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}
```
```public class SymbolTableTest {
@Test
public void testOrderSymbolTable(){
OrderSymbolTable<Integer, String> st = new OrderSymbolTable<>();

st.put(1,"Zhang San");
st.put(2,"Li Si");
st.put(4,"Zhao Liu");
st.put(5,"four seven");

st.put(3,"Wang Wu");
}
}
``` Posted by emediastudios on Sun, 19 Sep 2021 14:01:16 -0700