Data structure and linear table

Posted by Matth_S on Mon, 10 Jan 2022 12:33:48 +0100

Chapter I overview

Section I overview of data structure

Data structure is a programming discipline that studies non numerical calculation. Nicklaus Wirth, the father of Pascal who won the Turing Award, put forward a famous formula:

Algorithm + data structure = Program

Data structure is a way for computers to store and organize data. A data structure is a collection of data elements that have one or more specific relationships with each other. In general, carefully selected data structures can bring higher operation or storage efficiency. Data structure is often related to efficient retrieval algorithm and index technology.

1. Basic concepts

(1) Data

Data is the general name of the objects processed by computer programs. For example, when calculating differential equations, the processing object is some data, and in image processing, the object is encoded image information, etc.

(2) Data Element

The basic unit of data, which can be divided into several data items, and the data item is the smallest unit of data. For example, in a student performance management system, the performance of each student can be regarded as a data element, and the performance of each subject can be regarded as different data items.

(3) Data Object

It is a collection of data elements with the same properties and a subset of data. For example, the data object of real number is real number set R.

(4) Data Structure

Is a collection of data elements that have one or more specific relationships with each other.

2. Logical structure and physical structure

(1) Logical structure

Logical structure refers to the logical relationship between data elements in data objects, which is divided into the following four structures:

  • Set structure: the data elements in the set structure have no relationship except that they belong to the same set
  • Linear structure: the relationship between data elements in a linear structure is a one-to-one linear relationship
  • Tree structure: the elements in the tree structure have a one to many hierarchical relationship
  • Graphic structure: the elements in the graphic structure have any many to many relationship
(2) Physical structure

Physical structure refers to the storage form of data logical structure in the computer, which generally has the following two forms:

  1. Sequential storage structure: sequential storage structure is to store data elements in storage units with continuous addresses, and the logical relationship of data is consistent with the physical relationship. As shown below:

  2. Chain storage structure: chain storage structure stores data elements in any data unit. This group of storage units can be continuous or discontinuous. As shown below:

Section II algorithm overview

The algorithm is a description of the steps to solve a specific problem, which is expressed as a finite sequence of instructions in the calculation set, and each instruction represents one or more operations.

1. Characteristics of the algorithm

The algorithm has five basic characteristics: input, output, finiteness, certainty and feasibility

  • Input: the algorithm should have zero or more inputs
  • Output: the algorithm has at least one or more outputs
  • Finiteness: finiteness means that the algorithm ends automatically after executing limited steps without infinite loops, and each step can be completed in an acceptable time.
  • Certainty: every step of the algorithm has definite significance
  • Feasibility: every step of the algorithm must be feasible, and each step can be completed by executing a limited number of times

2. Requirements for algorithm design

The algorithm design has the following requirements:

  1. Correctness: the correctness of the algorithm means that the algorithm should at least have no ambiguity in input, output and processing, can correctly respond to the needs of the problem, and can get the correct answer to the problem
  2. Readability: another purpose of algorithm design is to facilitate reading, understanding and communication
  3. Robustness: when the input data is illegal, the algorithm can also make relevant processing, rather than producing abnormal or inexplicable results
  4. High time efficiency and low storage: the design algorithm should try to meet the requirements of high time efficiency and low storage

3. Measurement method of algorithm efficiency

(1) Post statistical method

This method mainly compares the running time of programs compiled by different algorithms by using computer timers through designed test programs and data, so as to determine the efficiency of the algorithm

Defects:

  • It must be realized that the program is well prepared
  • The comparison of time depends on environmental factors such as computer hardware and software
  • The test data design of the algorithm is difficult, and the running time of the algorithm is often closely related to the scale of the test data
(2) Ex ante analysis and estimation method

Prior analysis and estimation method before computer programming, a statistical method is used to estimate the algorithm

Depends on the following factors:

  1. The algorithm adopts strategies and methods
  2. Code quality generated by compilation
  3. Problem input scale
  4. The speed at which the machine executes commands

4. Algorithm time complexity

When analyzing the algorithm, the total execution times T(n) of the statement is a function of the problem scale n, and then analyze the change of T(n) with n to determine the order of magnitude of T(n). The time complexity of the algorithm, that is, the time measurement of the algorithm, is recorded as: T(n) = O(f(n))

Common time complexity:

O ( 1 ) < O ( l o g n ) < O ( n l o g n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) {O(1) < O(log n) < O(nlog n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)} O(1)<O(logn)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

Chapter 2 linear table (List)

Linear table (List): a linear table represents a finite sequence of zero or more data elements

Section 1 sequential storage of linear tables (ArrayList)

1. General

Definition of sequential storage: the sequential storage structure of linear table refers to storing the data elements of linear table at one time with a section of storage unit with continuous addresses, which is equivalent to one-dimensional array

Linear tables stored in sequential storage are also called ArrayList

Properties of sequence table:

  • data []: an array that implements the sequence table
  • size: the number of data stored in the sequence table

Method of sequence table:

·

2. Implement List and ArrayList through Java code

(1) Create List interface
package p1.interfaces;

import java.util.Comparator;

public interface List<E> extends Iterable<E> {
    public void add(E element);//Add element at end of table
    public void add(int index,E element);//Adds an element at the specified subscript
    public void remove(E element);//Deletes the specified element
    public E remove(int index);//Deletes the element at the specified subscript
    public E get(int index);//Gets the element at the specified subscript
    public E set(int index,E element);//Modify the element at the specified subscript
    public int size();//Get the number of elements
    public int indexOf(E element);//View the subscript position of the first occurrence of the element (from left to right)
    public boolean contains(E element);//See if the linear table contains elements
    public boolean isEmpty();//Judge whether the linear table is empty
    public void clear();//Empty linear table
    public void sort(Comparator<E> c);//The linear table is sorted according to the contents of the comparator
    public List<E> subList(int fromIndex, int toIndex);//In the original linear table, the sub list is intercepted by [fromIndex,toIndex) interval
}
(2) Create an ArrayList class to implement the List interface
package p2.array.lists;

import p1.interfaces.List;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;

public class ArrayList<E> implements List<E> {
    private E[] data;
    private int size = 0;
    //Default capacity
    private static int DEFAULT_CAPACITY = 10;

    //The default constructor creates a linear table of default capacity
    public ArrayList(){
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    //Creates a linear table of the specified size
    public ArrayList(int capacity) {
        if (capacity < 0){
            throw new IllegalArgumentException("capacity are not be null");
        }
        DEFAULT_CAPACITY = capacity;
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    //Convert an array to a linear table
    public ArrayList(E[] array) {
        if (array == null || array.length == 0){
            throw new IllegalArgumentException("capacity are not be null");
        }
        data = (E[]) new Object[DEFAULT_CAPACITY];
        for (int i = 0; i < array.length ; i++) {
            add(array[i]);
        }
    }

    @Override
    public void add(E element) {
        add(size,element);
    }

    @Override
    public void add(int index, E element) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index out of range");
        }
        if (size == data.length) {
            resize(2 * data.length);
        }
        for (int i = size -1; i >= index ; i--) {
            data[i + 1] = data[i];
        }
        data[index] = element;
        size ++;
    }
    //Expansion or contraction, not provided to the outside world
    private void resize(int newlen) {
        E[] newData = (E[]) new Object[newlen];
        for (int i = 0; i < size; i++) {//No subscript out of bounds
            newData[i] = data[i];
        }
        data = newData;
    }

    @Override
    public void remove(E element) {
        int index = indexOf(element);
        if (index == -1) {
            throw new IllegalArgumentException("The element to be deleted does not exist");
        }
        remove(index);
    }

    @Override
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove index out of range");
        }

        E ret = data[index];
        for (int i = index + 1; i < size  ; i++) {
            data[i - 1] = data[i];
        }
        data[size-1] = null;
        size --;
        //When the effective element is one fourth of the capacity and the capacity is less than or equal to the default capacity, shrink the capacity
        if (size == data.length/4 && data.length > DEFAULT_CAPACITY) {
            resize(data.length/2);
        }
        return ret;
    }

    @Override
    public E get(int index) {
        if (index < 0 ||index >= size){
            throw new IllegalArgumentException("get index out of range");
        }
        return data[index];
    }

    @Override
    public E set(int index, E element) {
        if (index < 0 ||index >= size){
            throw new IllegalArgumentException("set index out of range");
        }
        E ret = element;
        //data[index] = element;
        return ret;
    }

    @Override
    public int size() {
        return size;
    }

    private int capacity(){
        return data.length;
    }

    @Override
    public int indexOf(E element) {
        /*
        * == Reference data type comparison address
        * == Basic data type comparison value
        * */
        for (int i = 0; i < size; i++) {
            if (data[i] == element) {
                return i;
            }
        }
        return -1;
    }


    @Override
    public boolean contains(E element) {
        int count = indexOf(element);
        boolean flag = false;
        if (count != -1) {
            flag = true;
        }
        return flag;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public void clear() {
        data = (E[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }
    @Override
    public void sort(Comparator<E> c){

        if (c == null) {
            throw new IllegalArgumentException("comparator can not be null");
        }
        for (int i = 1; i < size; i++) {
            E e = data[i];
            int j = 0;
            for (j = i; j > 0 && c.compare(data[j - 1], e) > 0; j--) {
                data[j] = data[j - 1];
            }
            data[j] = e;
        }
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        if (fromIndex < 0 ||toIndex >= size || fromIndex > toIndex){
            throw new IllegalArgumentException("index out of range");
        }
        ArrayList<E> list = new ArrayList<>();
        for (int i = fromIndex; i < toIndex; i++) {
            list.add(data[i]);
        }

        return list;
    }

    @Override
    public boolean equals(Object o) {
       //1. Empty judgment first
        if (o == null) {
            return false;
        }
        if (this == null) {
            return false;
        }
        if (o instanceof ArrayList) {
            ArrayList<E> other = (ArrayList<E>) o;
            if (this.size != other.size) {
                return false;
            }
            for (int i = 0; i < size; i++) {
                if (!(data[i].equals(other.data[i]))) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append('[');
        if(isEmpty()){
            stringBuilder.append(']');
        }else{
            for (int i = 0; i < size; i++) {
                stringBuilder.append(data[i]);
                if (i == size-1){
                    stringBuilder.append(']');
                }else{
                    stringBuilder.append(',');
                    stringBuilder.append(' ');
                }
            }
        }
        return stringBuilder.toString();
    }

    @Override
    public Iterator<E> iterator() {
        return new ArrayListIterator();
    }
    class ArrayListIterator implements Iterator<E>{
        private int cur = 0;
        @Override
        public boolean hasNext() {
            return cur < size;
        }

        @Override
        public E next() {
            return data[cur++];
        }
    }
}

Section 2 stack

1. Definition of stack

Stack is a special linear table. It is limited to insert and delete operations only in one section of the linear table. One end that allows insertion and deletion is called the top of the stack and the other end is called the bottom of the stack. Because only one end is inserted or deleted, the later inserted elements will be deleted before the first inserted elements, so the stack is also called "last in first out" linear table

Stack entry (stack entry, stack pressing): the process of adding elements to the stack

Out of stack (pop stack): delete the elements in the stack

2. Realize Stack and ArrayStack through code

(1) Define Stack interface
package p1.interfaces;

public interface Stack<E> extends Iterable<E> {
    public int size();//Returns the number of elements currently stored in the stack
    public boolean isEmpty();//Determine whether the stack is empty
    public void push(E element);//Stacking (pressing) operation
    public E pop();//Out of stack (pop stack) operation
    public E peek();//Get stack top element
    public void clear();//Empty stack
}
(2) Implement the Stack interface through the ArrayStack class
package p2.array.lists;

import p1.interfaces.Stack;

import java.util.Iterator;

public class ArrayStack<E> implements Stack<E> {
    ArrayList<E> list ;
    public ArrayStack(){
        list = new ArrayList<>();
    }
    public ArrayStack(int capacity){
        list = new ArrayList<>(capacity);
    }
    public ArrayStack(E[] arr){
        list = new ArrayList<>(arr);
    }


    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @Override
    public E pop() {
        return list.remove(list.size()-1);
    }

    @Override
    public E peek() {
        return list.get(list.size() - 1);
    }

    @Override
    public void clear() {
        list.clear();
    }

    @Override
    public void push(E element) {
        list.add(list.size(), element);
    }

    @Override
    public String toString() {
        return list.toString();
    }

    @Override
    public Iterator iterator() {
        return list.iterator();
    }
}

3. Stack application: infix expression operation

(1) Definition of infix expression
  • It is a general expression method of arithmetic or logic formula. The operator is in the middle of the operand in the form of infix (example: 3 + 4)
  • Compared with prefix expression (e.g. + 34) or suffix expression (e.g. 34 +), infix expression is not easy to be parsed by computer, but it is still used by many programming languages because it conforms to people's common usage.
  • Unlike prefix or suffix notation, infix notation brackets are required.
(2) Overview of infix expression operation implementation process

with ( 10 + 20 / 2 ∗ 3 ) / 2 + 8 {(10+20/2*3)/2+8} (10 + 20 / 2 * 3) / 2 + 8 as an example

First, you can add an appropriate delimiter to the infix expression to split the expression and initialize the string

public static String initString(String expression){
        //Add spaces at both ends of all non numeric characters
        //Traverse the string from left to right and use stringbuilder to splice
        StringBuilder stringBuilder = new StringBuilder();
        char c;
        for (int i = 0; i < expression.length(); i++) {
            c = expression.charAt(i);
            if (c=='('||c==')'||c=='+'||c=='-'||c=='*'||c=='/'){
                stringBuilder.append(',');
                stringBuilder.append(c);
                stringBuilder.append(',');
            }else{
                stringBuilder.append(c);
            }
        }
        return stringBuilder.toString();
    }

Secondly, divide the string, define two stacks, one for storing operators and the other for storing numbers, and then operate on these stacks.

private static int eValuete(String expression) {
        ArrayStack<Character> operator = new ArrayStack<>();
        ArrayStack<Integer> integers = new ArrayStack<>();
        //1. Initialize the expression and add spaces on both sides of each symbol
        expression = initString(expression);
        //2. Cut the string xz
        String[] tokens = expression.split(",");
        for (String token:tokens) {
            //Filter empty strings
            if (token.length() == 0){
                continue;
            }else if (token.equals("+") || token.equals("-")){//When traversing to the plus and minus sign, look at the top of the stack. There are no elements at the top of the stack. Go directly in
                while(!operator.isEmpty() && (operator.peek() == '+' || operator.peek() == '-' || operator.peek() == '*' || operator.peek() == '/')){
                    //If the previous operator is + - * / or other operators, pop stack calculation
                    processAnOperator(integers,operator);
                }
                operator.push(token.charAt(0));
            }else if (token.equals("*") || token.equals("/")){
                while(!operator.isEmpty() && (operator.peek() == '*' || operator.peek() == '/')){
                    //If the previous operator is + - * / or other operators, pop stack calculation
                    processAnOperator(integers,operator);
                }
                operator.push(token.charAt(0));
            }else if(token.equals("(")){
                operator.push(token.charAt(0));
            }else if(token.equals(")")){
                while(operator.peek() != '('){
                    //If the previous operator is + - * / or other operators, pop stack calculation
                    processAnOperator(integers,operator);
                }
                operator.pop();
            }else{
                integers.push(Integer.parseInt(token));
            }

        }
        //Process the last operator
        while(!operator.isEmpty()){
            processAnOperator(integers,operator);
        }
        return integers.pop();
    }
    //The operator stack bounces one element, the number stack bounces two elements and operates, and then the result is put on the stack
    private static void processAnOperator(ArrayStack<Integer> integers, ArrayStack<Character> operator) {
        char op = operator.pop();
        int num1 = integers.pop();
        int num2 = integers.pop();
        if (op == '+') {
            integers.push(num2 + num1);
        } else if (op == '-') {
            integers.push(num2 - num1);
        } else if (op == '*') {
            integers.push(num2 * num1);
        } else {
            integers.push(num2 / num1);
        }
    }

Finally, we are testing these functions with a main()

 public static void main(String[] args) {
        String expression = "(10+20/2*3)/2+8";
        try {
            int result = eValuete(expression);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Wrong expression :" + expression);
        }

    }

Topics: Java Algorithm data structure