Data structure note 2: linear table

Posted by richie19rich77 on Sat, 30 Oct 2021 10:45:12 +0200

Basic concepts

  1. A linear table is a finite sequence of n data elements. The elements in the same linear table must have similarity
  2. Do not care about the specific data between elements, but about the relationship between elements
  3. Linear table operations: create, judge whether it is empty, determine the length, find, delete and insert

Array description (sequential storage)

  1. C + + class definition
     
    template<class T>
    class LinearList{
        public:
            LinearList(int MaxListSize=10); //Constructor
            ~LinearList(){delete[] element;) //Destructor
            bool IsEmpty() const{return length==0;}
            int Length() const(return length;)
            bool Find(int k,T& x) const; //Reference to return the k-th element of the list
            int Search(const T& x) const; //Returns the position of x (no duplicate elements)
            LinearList<T> Delete(int k,T& x); //Delete the k-th element and save it in x
            LinearList<T>& Insert(int k, const T& x); //Insert x after the k th element
            void Output(ostream& out) const;
        private:
            int length;
            int MaxSize; //Fixed part, ignore
            T *element; //One dimensional dynamic array
    }
  2. C + + implementation: create and release
    template<class T>
    LinearList<T>::LinearList(int MaxListSize){
        MaxSize=MaxListSize;
        element=new T[MaxSize]; //Time complexity: constant level
        length=0;
    }
    
    ~Linearlist(){ //Time complexity: constant level
        delete[] element;
    }
  3. lookup
     
    template<class T>
    bool LinearList<T>::Find(int k,T& x) const{
        if(k<1||k>length)
            return false;
        x=element[k-1];
        return true;
    } //Time complexity: O(1)
    
    template<class T>
    bool LinearList<T>::Search(int k,T& x) const{
        for(int i=0;i<length;i++)
            if(element[i]==x)
                return ++i;
        return 0;
    } //Time complexity: O(length)
  4. delete
     
    template<class T>
    LinearList<T>& LinearList<T>::Delete(int k,T& x){
        if(Find(k,x)){ //If the element can be found
            for(int i=k;i<length;i++)
                element[i-1]=element[i]; //Move the following elements forward one space
            length--;
            return *this;
        } //Time complexity: O(length-k)
        else
            throw OutOfBounds(); //Out of bounds, exception
        return *this;
    }
    
    template<class T>
    LinearList<T>& LinearList<T>::Insert(int k,const T& x){
        if(k<0||k>length)
            throw OutOfBounds(); //k illegal 
        if(length==MaxSize) 
            throw NoMem(); //Table space is full and cannot be inserted again
        for(int i=length-1;i>=k;i--) //General situation
            element[i+1]=element[i]; 
        element[k]=x;
        length++;
        return *this;
        //Problem: the element needs to be moved, and it is easy to overflow
    } //Time complexity: O(length-k)
  5. output
    template<class T>
    void LinearList<T>::Output(ostream& out) const{
        for (int i=0;i<length;i++) 
            out<<element[i]<<" ";
    }
    
    //heavy load
    template<class T>
    ostream& operator<<(ostream& out, const LinearList<T>& x){
        x.Output(out); 
        return out;
    }
  6. Evaluation: low space efficiency

Linked list description

  1. Linked list node: ChainNode
     
    template<class T>
    class ChainNode{
        friend Chain<T>; //Declared friend class
        friend ChainIterator<T>;
    private:
        T data; //Stored data
        ChainNode<T> *link; //Point to next node
    }
  2. Unidirectional linked list
    template<class T>
    class Chain{
        friend ChainIterator<T>;
        public:
            Chain(){first=0;} //Create an empty linked list
            ~Chain();
            bool IsEnmpty()const {return first==0;}
            int Length() const;
            bool Find(int k,T& x) const;
            int Search(const T& x) const;
            Chain<T>& Delete(int k,T& x);
            Chain<T>& Insert(int k,const T& x);
            void Output(ostream& out) const;
        private:
            ChainNode<T> *first; //First pointer
    }
    
    template<class T>
    Chain<T>::~Chain(){
        ChainNode<T> *next; //A temporary variable that controls the pointer
        while(first){ //The next node exists
            next=first->link;
            delete first;
            first=next;
        }
    } //Time complexity: O(n)
    
    template<class T>
    int Chain<T>::Length()const{
        ChainNode<T> *current=first;
        int len=0;
        while(current){
            len++;
            current=current->link;
        }
        return len;
    } //Time complexity: O(n)
    
    template<class T>
    bool Chain<T>::Find(int k, T& x) const{
        if(k<1)
            return false;
        ChainNode<T> *current=first;
        int index=1;
        while(index<k&&current){
            current=current->link;
            index++;
        }
        if(current){ //If the pointer points to something real
            x=current->data;
            return true;
        }
        return false;
    } //Time complexity: O(k)
    
    template<class T>
    bool Chain<T>::Search(const T& x) const{
        ChainNode<T> *current=first;
        int index=1; //Used to record where x is
        while(current&&current->data!=x){ //If you haven't found x
            current=current->link;
            index++;
        }
        if(current) //If the pointer points to something real
            return index;
        return 0;
    } //Time complexity: O(n)
    
    template<class T>
    void Chain<T>::Output(ostream& out)const{
        ChainNode<T> *current;
        for (current=first;current;current=current->link)
            out << current->data << " "; 
    }
    
    //Operator overloading 
    template <class T>
    ostream& operator<<(ostream& out, const Chain<T>& x){
        x.Output(out); 
        return out; 
    } //Time complexity: O(n)
    
    template<class T>
    Chain<T>&Chain<T>::Delete(int k, T& x){
        if(k<1||first) //If k is illegal or the linked list is empty
            throw OutOfBounds();
        ChainNode<T> *p=first; //A temporary variable that eventually points to the k-th element
        if(k==1) //If you want to delete the first element
            first=first->link; //Update linked list header element
        else{ //If you want to delete the middle element
            ChainNode<T> *q=first; //You want it to point to the previous element of the element you want to delete
            for(int index=1;index<k-1&&q;index++)
                q=q->link; //Pointer shift right
            if(!q||!q->link) //k> Linked list length
                throw OutOfBounds();
            p=q->link;
            q->link=p->link;
        }
        x=p->data;
        delete p; //Release the memory space occupied by item k, but do not delete the data. It may be used elsewhere
        return *this;
    } //Time complexity: O(n)
    
    template<class T>
    Chain<T>&Chain<T>::Insert(int k, const T& x){
        if(k<0) //If k is illegal
            throw OutOfBounds();
        ChainNode<T> *p=first; //A temporary variable that eventually points to the k-th element
        for(int index=1;index<k&&p;index++)
            p=p->link;
        if(k>0&&!p)
            throw OutOfBounds();//k> Linked list length
        ChainNode<T> *y=new ChainNode<T>;
        y->data=x; //Add a new node
        if(k){ 
            y->link=p->link;
            p->link=y; //Insert operation
        }
        else{ //When k=0, the chain header element is inserted
            y->link=first;
            first=y; //Remember to update the header node
        }
        return *this;
    }
    
    template<class T>
    void Chain<T>::Erase(){ //Delete all nodes of the linked list
        ChainNode<T> *next; 
        while (first){
            next = first->link; 
            delete first;
            first = next;
        }
    }
    
    template<class T>
    Chain<T>& Chain<T>::Append(const T& x){ //Add an element at the end of the linked list
        ChainNode<T> *y;
        y = new ChainNode<T>; 
        y->data = x; 
        y->link = NULL;
        if (first) {//If the linked list is not empty
            last->link = y;
            last = y;
        }
        else //The linked list is empty
            first = last = y; 
        return *this;
    }
  3. Two improved forms
    (1) Point the tail node to the head node
    (2) Add a header node that does not store data
    template<class T>
    bool Chain<T>::Search(const T& x) const{ //Optimization of Search function of leading node
        ChainNode<T> *current=first->link;
        int index=1; //Used to record where x is
        first->data=x; //Save the value of x in an empty header node and set an termination condition
        while(current->data!=x){ //If you haven't found x
            current=current->link;
            index++;
        }
        return ((current==first)?0:index); //If it points to the head node, it means that it is not found in the linked list
    }
  4. Bidirectional linked list
    template <class T> 
    class DoubleNode {
        friend Double<T>; 
    private:
        T data;
        DoubleNode<T> *left, *right; 
    };
    
    template<class T> 
    class Double {
    public:
        Double() {LeftEnd = RightEnd = 0;}; 
        ~Double();
        int Length() const;
        bool Find(int k, T& x) const;
        int Search(const T& x) const; 
        Double<T>& Delete(int k, T& x); 
        Double<T>& Insert(int k, const T& x); 
        void Output(ostream& out) const;
    private:
        DoubleNode<T> *LeftEnd, *RightEnd; //* RightEnd can be omitted from the two-way circular linked list
    };
     
  5. summary
    (1) Spatial complexity
    ​​​​​​​           The formulaic space is all used to save the list data, while the linked list has additional space to save the link pointer
               The linked list is dynamically allocated, the occupied space is proportional to the current linked list size, and the utilization rate is high; The formulation is static distribution, which can not predict the actual demand, resulting in waste or shortage
    (2) Time complexity
    ​​​​​           The performance of inserting and deleting operations is better described by linked list, while the performance of random access is better described by formula

Application of linear table

bin sort

  1. A lot of data is stored
  2. Limited range of stored data
  3. Sorting method realized by linked list
    Delete each node (head node) of the linked list one by one
    Put the deleted node into the appropriate box (insert the head position of the corresponding linked list)
    Collect and link all boxes to generate a sorting linked list (adjacent boxes are connected from beginning to end)  
    class Node {
        friend ostream& operator<<(ostream&, const Node &); 
        friend void BinSort(Chain<Node>&, int);
    public:
        int operator !=(Node x) const{ //Operator overloading 
            return (score != x.score);
        } 
    private:
        int score;
        char *name; 
    };
    
    ostream& operator<<(ostream& out, const Node& x) { //Operator overloading 
        out << x.score << ' '; 
        return out;
    }
    
    void BinSort(Chain<Node>& X,int range){ //Value range of element
        int len=X.length();
        Node x;
        Chain<Node> *bin;
        bin=new Chain<Node>[range+1];
        for(int i=1;i<len;i++){
            X.Delete(1,x); //Delete header node
            bin[x.score].Insert(0,x); //Insert into the head of the new linked list
        } //O(n)
        for(int j=range;j>=0;j--) //Starting from the last box, insert the large data into the ordered linked list first, because each insertion is at the head, so the large elements will go to the back
            while(!bin[j].IsEmpty()){ //If the box is not empty (it is already empty or has been taken)
                bin[j].Delete(1,x); //Delete header node
                X.Insert(0,x); //Insert into the head of the ordered linked list
            } //O(n+range)
        delete[] bin;
    } //Time complexity: 2n+range
    
    //Problem: there are many times of new and Delete in Delete and Insert, and this is required for each cycle
    //update: avoid frequent calls to Delete and Insert
    
    template<class T>
    void Chain<T>::BinSort(int range){ //Sort the box as a member function of the linked list
        int b;
        ChainNode<T> **bottom, **top; //Initialize box
        bottom=new ChainNode<T>* [range+1];
        top=new ChainNode<T>* [range+1];
        for(b=0;b<=range;b++)
            bottom[b]=NULL;
        for(;first;first=first->link){
            b=first->data;
            if(bottom[b]){ //If the box is not empty
                top[b]->link=first;
                top[b]=first;
            }
            else
                bottom[b]=top[b]=first;
        }
        ChainNode<T> *y=0;
        for(b=0;b<=range;b++)
            if(bottom[b]){
                if(y)
                    y->link=bottom[b];
                else
                    first=bottom[b];
                y=top[b];
            }
        if(y)
            y->link=0;
        delete[] bottom;
        delete[] top;
    } //Time complexity: n+2range

Cardinality sort

Assignment 2

Splice the two linked lists alternately into a linked list

 //Splice the two linked lists alternately into a linked list
 template <typename T>
 class extendedChain{
     struct node{
         T data;
         node *next;
     };
 public:
     node *head; //Head node
     extendedChain(); //Constructor
     void append(T t); //Splice the data at the end of the linked list
     void display(); //Output linked list
     bool empty(); //Determine whether the linked list is empty
     void deleteHead(); //Delete header element
 };
 
 template <typename T>
 extendedChain<T>::extendedChain(){
     head=NULL;
 }
 
 template <typename T>
 void extendedChain<T>::append(T t){
     node *p=new node;
     p->data=t;
     p->next=NULL;
     if(head==NULL){ //If the linked list has not been created
         head=p;
         return;
     }
     node *tail=head;
     while(tail->next) //Find the location of the tail node
         tail=tail->next;
     tail->next=p; //Connect the new node to the back
 }
 
 template <typename T>
 void extendedChain<T>::display(){
     node *p=head;
     while(p){ //Sequential output
         cout<<p->data<<" ";
         p=p->next;
     }
     cout<<endl;
 }
 
 template <typename T>
 bool extendedChain<T>::empty(){
     if(head)
         return false;
     else
         return true;
 }
 
 template <typename T>
 void extendedChain<T>::deleteHead(){
     node *p=head;
     head=p->next; //Update header node
 }
 
 template <typename T>
 void meld(extendedChain<T> a,extendedChain<T> b){
     extendedChain<T> c;
     while(!a.empty()&&!b.empty()){ //When neither is empty
         c.append(a.head->data);
         c.append(b.head->data);
         a.deleteHead();
         b.deleteHead();
     }
     while(!b.empty()){ //Add all the remaining elements to c
         c.append(b.head->data);
         b.deleteHead();
     }
     while(!a.empty()){
         c.append(a.head->data);
         a.deleteHead();
     }
     c.display();
 }
 
 int main(){
     extendedChain<int> a,b;
     cout<<"Please input 7 integers in entended chain a!"<<endl;
     int t;
     for(int i=1;i<=7;i++){
         cin>>t;
         a.append(t);
     }
     cout<<"Please input 10 integers in entended chain b!"<<endl;
     for(int i=1;i<=10;i++){
         cin>>t;
         b.append(t);
     }
     meld(a, b);
     return 0;
 }

Split a linked list into two linked lists according to the index

//Split a linked list into two lists according to the index
template <typename T>
class extendedChain{
    struct node{
        T data;
        node *pre; //Bidirectional cycle
        node *next;
    };
public:
    node *head;
    node *mark;
    extendedChain();
    void append(T t);
    void display();
    bool empty();
    void deleteHead();
    void split();
};
 
template <typename T>
extendedChain<T>::extendedChain(){
    head=NULL;
}
 
template <typename T>
void extendedChain<T>::append(T t){
    node *p=new node;
    p->data=t;
    p->next=NULL;
    if(head==NULL){
        head=p;
        return;
    }
    node *tail=head;
    while(tail->next)
        tail=tail->next;
    tail->next=p;
    p->pre=tail;
}
 
template <typename T>
void extendedChain<T>::display(){
    node *p=head;
    while(p){
        cout<<p->data<<" ";
        p=p->next;
    }
    cout<<endl;
}
 
template <typename T>
bool extendedChain<T>::empty(){
    if(head)
        return false;
    else
        return true;
}
 
template <typename T>
void extendedChain<T>::deleteHead(){
    node *p=head;
    head=p->next;
    p->pre=NULL;
}
 
template <typename T>
void extendedChain<T>::split(){
    
}
 
void split(extendedChain<int>c){ //Non member function method
    extendedChain<int>a,b;
    c.mark=c.head;
    int index=1;
    while(c.mark){
        if(index%2==0) //Index is double
            b.append(c.mark->data);
        else //Index is single
            a.append(c.mark->data);
        c.mark=c.mark->next;
        index++;
    }
    a.display();
    b.display();
}
 
int main(){
    extendedChain<int> c;
    cout<<"Please input 10 integers in entended chain c!"<<endl;
    int t;
    for(int i=1;i<=10;i++){
        cin>>t;
        c.append(t);
    }
    split(c);
    return 0;
}

Topics: data structure