zoukankan      html  css  js  c++  java
  • 数据结构笔记(第二章)

    第二章:线性结构之线性表

    数据的逻辑结构 :

    • 集合

    • 线性结构—>线性表、栈、队列、优先队列

    • 树结构

    • 图结构

    线性表的存储结构 :

    • 线性表的基于数组的存储表示叫做顺序表(SeqList),线性表的基于指针的存储表示叫做链表(LinkedList)(单链表、双链表、循环链表等)

    • 数据的操作:插入、删除、修改、检索、排序等

    • 注意其算法时间复杂度

    顺序表(SeqList)

    存储要点:

    • 用一段地址连续的存储单元

    • 依次存储线性表中的数据元素

    用什么属性来描述顺序表:

    • 存储空间的起始位置

    • 顺序表的容量(最大长度)

    • 顺序表的当前长度

    顺序表的优点:

    ⑴ 无需为元素之间的逻辑关系而增加额外存储空间;

    ⑵ 随机存取:可以快速地存取表中任一位置的元素。

    顺序表的缺点:

    ⑴ 插入和删除操作需要移动大量元素;

    ⑵ 表的容量难以确定,表的容量难以扩充;

    ⑶ 造成存储空间的碎片。

    顺序表的静态存储和动态存储
    #define maxSize 100
    typedef int T;
    typedef struct {
        T data[maxSize];     //顺序表的静态存储表示
        int n;
    } SeqList;
    
    typedef int T;
    typedef struct {
        T *data;                  //顺序表的动态存储表示
        int maxSize, n;
    } SeqList;
    
    顺序表(SeqList)类的定义
    const int defaultsize=100;
    template <class T>	
    class SeqList: public LinearList<T> {
    protected:
          T *data;       //顺序表存储数组
    	   int MaxSize;	 //最大允许长度	
    	   int last; 	         //当前最后元素下标
    	   void reSize(int newSize);
    public:
    	   SeqList ( int sz= defaultSize );
    	   SeqList(SeqList<T>& L);
    	   ~SeqList ( ) { delete [ ] data; }	
    	   int Size()const{return maxSize;}	
    	   int Length ( ) const { return last+1; }
           int Search ( T& x ) const;         //查找
    	   int Locate ( int i ) const;	        //定位
    	   bool getData(int i,T& x)const
    	   {if(i>0&&i<=last+1) {x=data[i-1];return true; }
    	   else return false;}
    	   void setData (int i, T&  x)
    	   {if(i>0 && i<=last+1) {  data[i-1]=x;  } 
    	   int Insert (int i, T & x);       //插入
    	   int Remove (int i, T & x );	       //删除	
    	   bool IsEmpty ( ) { return (last ==-1)?true:false; }	
    	   bool IsFull ( ) { return last == MaxSize-1?true:false; }
    	   void input();
    	   void output();
    	   SeqList<T> operator=(SeqList<T>& L);
               };
    
    
    
    顺序表部分公共操作的实现
    template <class T>	          //构造函数
     SeqList<T> :: SeqList ( int sz ) {    
         if ( sz > 0 ) {
    	      MaxSize = sz; 
             last = -1;			
    	      data = new T [MaxSize];
              if ( data == NULL ) {
                  cerr<<"存储分配失败!"<<endl;
                 exit(1);
              }		
    	  }
     }
     
    template <class T>	     //复制构造函数
     SeqList<T> :: SeqList( SeqList<T>& L ){    
    maxSize=L.Size();
    last=L.Length()-1;
    T value;
    data=new T[maxSize];
    if ( data == NULL ) {
                  cerr<<"存储分配失败!"<<endl;exit(1);
              }
    for(int i=1;i<=last+1;i++)
    { L.getData(i,value);data[i-1]=value; }
    }
    
    
    template <class T>	     //重定义大小
    void  SeqList<T> :: reSize(int newSize){    
    if(newSize<=0){cerr<<"无效的数组大小"<<endl;return;}
    if(newSize!=maxSize){
    	T *newarray=new T[newSize];
    	if ( newarray == NULL ) {
                  cerr<<"存储分配失败!"<<endl;exit(1);          }
    	int n=last+1;
    	T *srcptr=data;
    	T *destptr=newarray;
    	while(n- -)*destptr++=*srcptr++;
    	delete []data;
    	data=newarray; maxSize=newSize;
    	}
    }
    
    
    template <class T>	
    int SeqList<T> :: search ( T & x ) const {
    //搜索函数:在顺序表中从头查找结点值等于
    //给定值x的结点所在位置,如果没找到返回0
    for(int  i = 0; i <= last ; i++)
    	if (data[i]==x) return i+1 ;		
    return 0;					
    }
    
    
    template <class T> //顺序表的表项的插入insert算法
    bool SeqList<T> :: Insert (T& x, int i ) 
    {
    if (last+1 >= MaxSize|| (i < 0 || i > last + 1) ) return false;
    
    for (int j = last; j >= i; j- -)  data[j+1] = data[j]; 
    
     data[i] = x;
     last++;
    return true;
    }
    
    template <class T> //Remove:从顺序表中删除第i项,其值赋给x
    
     bool SeqList<T> :: Remove ( int i, T& x ) {
     //在表中删除已有元素 x	
    	if(last==-1 ||i<1 || i>last+1) return false;	x=data[i-1]; 
    	for ( int j = i; j <= last; j++ )  
                    data[j-1] = data[j];
    	last- - ;	
             return true;	               //成功删除	
    }
    
    template <class T> //顺序表的输入算法
     void SeqList<T> ::input() {
    	cout<<“请输入元素个数减一";
    	while(1){
    		cin>>last;
    		if(last<=maxSize-1)break;
    		cout<<"个数输入有误";
                         }
              cout<<“0:”<<endl;
    	for(int i=0;i<=last; i++)
    		{cin>>data[i]; cout<<i+1<<endl;}           	
    		}
    
    template <class T> //顺序表的输出算法
     void SeqList<T> ::output() {
    	cout<<"当前元素最后位置为"<<last+1<<endl;
    	
    	for(int i=0;i<=last;i++)
    		{cout<<"#"<<i+1<<":"<<data[i]<<endl;}               	
    }
    
    
    
    
    顺序表的应用:集合的“并”运算
    void Union ( SeqList<int> & A, SeqList<int> & B) 
    {
         int n = A.Length ( ), x;
         int m = B.Length ( );
         for ( int i = 1; i < =m; i++ ) {
    	 B.getData(i,x);         //在B中取一元素
    	 int k = A.Search (x);     //在A中搜索它
    	 if ( k == 0 )              //若未找到插入它
                 { A.Insert (n, x);  n++; }
         }
     }
     
    void main(){
    SeqList<int> s1,s2;
    s1.input();
    s2.input();
    Union(s1,s2);
    s1.output();
    
    }
    
    
    顺序表的应用:集合的“交”运算
    void Intersection ( SeqList<int> & A,
                                     SeqList<int> & B ) {
         int n = A.Length ( );
         int m = B.Length ( );  int i = 1, x;
         while ( i < =n ) {
    	A.get Data(i, x);      //在A中取一元素
     	int k = B.search (x);    //在B中搜索它	
    	if ( k == 0 ) { A.Remove (i,x);  n- - ; }
    	                               //未找到在A中删除它
            else i++;
           }
     }
     
     
    
    测试后的一份完整代码
    #include <stdio.h>
    #include <assert.h>
    #include <iostream>
    #define DefaultSize 100
    using namespace std;
    
    template <class Type> class SeqList {
    
    public:
        SeqList( int size = DefaultSize ){
    	assert ( size >= 0 );
        if ( size > 0 ) {
           MaxSize = size;  last = -1;
           data = new Type[MaxSize];
        }
    	}
    
        ~SeqList() { delete[] data; }
        int Length() const { return last + 1; }
        int Find( Type & x ) const;
        int IsIn ( Type & x);
        int Insert ( Type & x, int i );
        int Remove ( Type & x);
        int Next ( Type & x );
        int Prior ( Type & x );
        int IsEmpty() { return last == -1; }
        int IsFull()  { return last == MaxSize - 1; }
        Type Get( int i ) { return i < 0 || i > last ? NULL:data[i]; }
        void Print();
    private:
        Type *data;
        int MaxSize;
        int last;
    };
    
    /*template < class Type >
     SeqList <Type>::SeqList( int size = DefaultSize ) {
        assert ( size >= 0 );
        if ( size > 0 ) {
           MaxSize = size;  last = -1;
           data = new Type[MaxSize];
        }
    }
    */
    template < class Type > int SeqList <Type>::Find( Type & x ) const {
        int i = 0;
        while ( i <= last && data[i] != x ) i++;
        if ( i > last ) return -1;
        else return i;
    }
    
    template < class Type > int SeqList <Type>::IsIn( Type & x ) {
        int i = 0, found = 0;
        while ( i <= last && !found)
    	if ( data[i] != x ) i++;
    	else found = 1;
        return found;
    }
    
    template < class Type > int SeqList <Type>::Insert( Type & x, int i ) {
        if ( i < 0 || i > last+1 || last == MaxSize - 1 ) return 0;
        else {
    	last++;
    	for ( int j = last; j > i; j-- ) data[j] = data[j-1];
    	data[i] = x;
    	return 1;
        }
    }
    
    template < class Type > int SeqList <Type>::Remove( Type & x ) {
        int i = Find(x);
        if ( i >= 0 ) {
    	last--;
    	for ( int j = i; j <= last; j++ ) data[j] = data[j+1];
    	return 1;
        }
    }
    
    template < class Type > int SeqList <Type>::Next( Type & x ) {
        int i = Find(x);
        if ( i >= 0 && i < last ) return i+1;
        else return -1;
    }
    
    template < class Type > int SeqList <Type>::Prior( Type & x ) {
        int i = Find(x);
        if ( i > 0 && i <= last ) return i-1;
        else return -1;
    }
    
    template < class Type > void Union( SeqList <Type> & LA, SeqList <Type> & LB ) {
        int n = LA.Length(); int m = LB.Length();
        for ( int i=0; i <= m; i++ ) {
    	Type x = LB.Get(i);
    	int k = LA.Find(x);
    	if ( k == -1 ) { LA.Insert( x, n );  n++;}
        }
    }
    
    template < class Type > void Intersection ( SeqList <Type> & LA, SeqList <Type> & LB ) {
        int n = LA.Length();  int m = LB.Length();  int i = 0;
        while ( i < n ) {
    	Type x = LA.Get(i);
    	int k = LB.Find(x);
    	if ( k == -1 ) { LA.Remove(x); n--; }
    	else i++;
        }
    }
    
    template < class Type > void SeqList <Type>::Print() {
        if ( last == -1 ) cout<<"It is empty" ;
        else for ( int i=0; i<=last; cout << "  data[" << i++ << "] = " << data[i] );
        cout << endl;
    }
    
    int main(){
    	int length;
    SeqList<int>* sq=new SeqList<int>;
    
    cout<<"请输入元素个数";
    cin>>length;
    int result;
    //cout<<length;
    for(int i=1;i<=length;i++){
      result=sq->Insert(i,i-1);
      cout<<result<<endl;
    }
    sq->Print();
    }
    
    

    链表(Linked List)

    单链表 (Singly Linked List)

    单链表是最简单的链表,也叫线性链表,它用指针表示结点间的逻辑关系。特点:

    • 每个元素(表项)由结点(Node)构成。数据域和指针域。

    • 线性结构(first为头指针)

    • 结点可以连续,可以不连续存储

    • 结点数据元素顺序与物理顺序可能不一致。元素之间的逻辑关系用指针表示

    • 表扩充很方便

    单链表的类定义

    多个类表达一个概念(单链表):

    • 链表结点(ListNode)类型

    • 链表(List)类型

    定义两种类型关系的方式:

    • 嵌套方式

    • 继承方式

    • 复合方式**

    1、嵌套类(不合适)
    class List {           //链表类定义(嵌套方式)
    public:
    ………               //链表操作
    private:
        class LinkNode {     //嵌套链表结点类
        public:
            int data;      //可被两个类的成员访问
            LinkNode *link;		
        };
        LinkNode *first ;    //表头指针
    };
    
    
    2、继承方式(不合适)
    链表类和链表结点类定义(继承方式)
    
    class LinkNode {	   //链表结点类	
    protected:
        int data;		       
        LinkNode * link;          	
    };
    
    class List : public class LinkNode {	                   //链表类, 继承链表结点类的数据和操作	
     private:
         LinkNode *first;       //表头指针
    };
    
    
    3、复合类 
    (友元类不具有对称性)
    
    class List;	     //链表类定义(复合方式)
    
     class LinkNode {	        //链表结点类	
     friend class List;	       //链表类为其友元类
     private:
         int data;		   //结点数据, 整型	
         LinkNode * link;        //结点指针		
     };
    
     class List {	                   //链表类		
     private:
         LinkNode *first;       //表头指针
    };
    
    
    
    
    
    
    复合类2: 用struct定义linkNode类(最佳)
    
    struct LinkNode {	   //链表结点类	
        int  data;		       
        LinkNode * link;          	
    };
    
    class List {	                   //链表类
    private:
         LinkNode *first;       //表头指针
    public:
    ……
    };//虽然结构使得LinkNode失去了封装性,但是所有属于List对象的LinkNode结点只能用first 进行访问
    
    
    单链表中的插入与删除(part 1)

    插入:单链表(a1,a2,a3……an)希望在ai之后插入新元素x

    第一种情况:在第一个结点前插入

    LinkNode* newnode=new LinkNode(x);   
    newnode->link = first ;    
    first = newnode;
    
    

    第二种情况:在链表中间插入(p指向ai)

    newnode->link = p->link;	
    p->link = newnode
    

    第三种情况:在链表末尾插入( p指向ai )

    newnode->link = p->link;	
    p->link = newnode;
    
    

    从上面的分析看出,后两种情况(即在表中间插入和在表尾插入)的操作是一样的,可以合并处理,但首节点前插入操作不同。(因为首节点没有前驱)

    • 删除:在单链表中删除ai结点

    第一种情况: 删除表中第一个元素

    del=first;first=first->link;//del指向被删结点
    delete del;
    
    

    第二种情况: 删除表中或表尾元素

    del=p->link;
    p->link=del->link;
    (或p->link=p->link->plink)
    delete del;
    //  用P指向被删结点前一个结点,del指向被删结点
    
    

    单链表中的插入与删除(part 2)

    在带附加头结点的单链表第一个结点前插入新结点

    newnode->link = p->link; 
    p->link = newnode;
    //  在空表或非空表的第1个结点前插入可以统一操作
    
    

    在带附加头结点的单链表中删除第一个结点

    del = p->link;
    p->link = del->link;
    delete del; 
    // 用P指向被删结点前一个结点,del指向被删结点
      
    

    带附加头结点的单链表类
    template <class T>  //结点结构定义
    struct LinkNode {	
    T data;                         //结点数据 
    LinkNode<T> *link;    //结点链接指针
    
    LinkNode(LinkNode<T> *ptr=NULL )      	 	{link=ptr; }  //	仅初始化指针成员的构造函数
     
    LinkNode (const T& item, LinkNode<T> *ptr=NULL ) {data=item;link=ptr;
          } 
    };
    
    
    
    template <class T>  //链表类
    class List :public LinearList<T>{    
    protected:
        LinkNode<T> *first; //链表的头指针
    
    public:
    List () { first = new LinkNode<T>; }
    List (const T& x) {  first = new LinkNode<T> (x ); }
    List (List<T>& L);
    ~List () { makeEmpty(); }
    void MakeEmpty ( );	//将链表置为空表
    int Length( )const;	//计算链表的长度
    
    
    
    LinkNode<T> *getHead()const { return first; }
    LinkNode<T> *Search( T x );	
          //搜索含数据x的元素
    LinkNode<T> * Locate( int i );
    	   //搜索第 i 个元素的地址
    bool  GetData ( int i, T& x );				 
          //取出表中第 i 个元素的值
    bool Insert (int i ,T& x);	 
          //将x插在表中第 i 个元素后
    bool Remove (int i ,T& x);
    	  //删除第i个元素,x返回该元素的值
    
    
    bool  IsEmpty()const
    	{ return first->link==NULL?true:false; }
    bool  IsFull() const {return false;}
    void Sort();
    void input();
    void output();
    List<T>& operator=(List<T>& L);
    };   //list类定义到此结束
    
    
    
    
    链表类部分操作的实现
    template <class T> 
    List <T> ::List(List<T>& L) { //复制构造函数
    T value;    
    LinkNode<T> *srcptr=L.getHead();
    LinkNode<T> *destptr=first=new LinkNode<T>;
    while ( srcptr->link != NULL ) {
     	value=srcptr->link->data;
    	destptr->link=new LinkNode<T>(value);
    	destptr=destptr->link;
    	srcptr = strptr->link; 
    }
    destptr->link=NULL; 
    }
    
    
    
    template <class T> //置空表
    void List <T> :: MakeEmpty ( ) {
    //删去链表中除附加头结点外的所有其他结点
    //即把表变为有附加头结点的空表
        LinkNode<T> *q;
        while ( first->link != NULL ) {
     	     q = first->link;  first->link = q->link;	//将表头结点后第一个结点q从链中摘下
    	     delete q;        //释放它 
        }   
    }
    
    
    template <class T>//链表长度
    int List<T> :: Length ( ) const {
                                    //求单链表的长度
         LinkNode<T> *p = first->link;
                   //检测指针 p 指示第一个结点
         int count = 0; 
         while ( p != NULL ) {      //逐个结点检测
           count++; 
           p = p->link;
    }			
         return count; //注意count的初始化和返回值之间的关系
    }
    
    
    template <class T> //搜索
    LinkNode<T> *List <T> :: Search (T x ) {
    //在链表中从头搜索其数据值为x的结点 
         LinkNode<T> * p = first->link;
         //检测指针 p 指示第一个结点
         while ( p != NULL )
                if ( p->data == x ) break; 
             else p = p->link;
         return p; 
         // p 在搜索成功时返回找到的结点地址
         // p 在搜索不成功时返回空值
    }
    
    
    template <class T> //定位
    LinkNode<T> *List<T> :: Locate ( int i ) {
    //定位函数。返回表中第 i 个元素的地址
    //若 i < 0或 i 超出,则返回NULL
        if ( i < 0 ) return NULL;   // i 值不合理
        LinkNode<T> * p =first; 
        int k = 0; 
        while ( p != NULL && k < i ) 
           {p = p->link ; k++;}	    //找第 i 个结点 
        return p;    //返回第 i 个结点地址或NULL
    }  
    
    
    template <class T> //取值
    bool * List<T> :: GetData ( int i, T& x ) {
                                //取出链表中当前元素的值
    if (i<=0) return NULL;     
    LinkNode<T> *p = Locate ( i );
                               // p 指向链表第 i 个结点
         if ( p == NULL )
             return false;		
         else { x=p->data; return true; }
    }
    
    
    template <class T>  //单链表的实现———插入
     bool  LinkList<T> :: Insert(int i, T&  x) //在第i个位置后插入x
     {
         LinkNode<T> * p=Locate(i);
         if (p == NULL) return false ;      //没有找到
        else { 
            s = new LinkNode<T>(x);   //申请一个结点s
           if(s==null){cerr<<"store is error"<<endl; exit(1);}
            s->link= p->link;
            p->link = s;   //结点s插入结点p之后
            return true;
        }
     }
    
    
    
    template <class T>  //删除
    bool   List<T> :: Remove ( int i ,T& x)
    {
     LinkNode<T> *p = Locate (i-1);
      if (p == NULL || p->link == NULL)  return false;  
      
      LinkNode<T> *q= p->link; 
       x = q->data;         
       p->link = q->link;              
       delete q;
       return true;  
           }
    
    
    template <class T> //重载
    List<T>& List<T> :: Operator= (List<T>& L) {
    //赋值操作,A=B,A是调用者,B是实参
    T value;    
    LinkNode<T> *srcptr=L.getHead();
    LinkNode<T> *destptr=first=new LinkNode<T>;
    while ( srcptr->link != NULL ) {
     	value=srcptr->link->data;
    	destptr->link=new LinkNode<T>(value);
    	destptr=destptr->link;
    	srcptr = strptr->link; 
    }
    destptr->link=NULL;
    return * this;//加此句是使得表达式"A=B"的值为A,可做连续赋值  }
    
    
    template <class T>  //前插法建立单链表
    void List <T> :: inputFront (T endTag ) {
    LinkNode<T> *newNode;  T val;
    makeEmpty();     
    cin>>val;
    while (val != endTag) {
    	newNode = new LinkNode<T>(val);
    	if(newNode==NULL)
    		{cerr<<"error"<<endl;exit(1);}
    	newNode->link=first->link;
    	first->link=newNode;
    	cin>>val;
        }  }    
    
    
    template <class T>  //后插法建立单链表
    void List <T> :: inputRear(T endTag ) {
    LinkNode<T> *newNode,*last,T val;
    makeEmpty();     
    cin>>val; last=first;
    while (val != endTag) {
    	newNode = new LinkNode<T>(val);
    	if(newNode==NULL)
    		{cerr<<"error"<<endl;exit(1);}
    	last->link=newNode;
    	last=newNode;
    	cin>>val;
        }  }    
    
    
    
    

    单向循环链表(简称循环链表)

    循环链表1:将单链表的首尾相接,将终端结点的指针域由空指针改为指向开始结点,构成单循环链表,简称循环链表。

    循环链表2:带尾指针的循环链表,在循环链表里设置last不仅仅有利于插入,删除也很方便。所以,在后面的循环链表的定义里,封装了两个指针first和last

    循环链表3: 要使空表和非空表的处理一致,可附设头结点**

    循环链表中没有明显的尾端,如何避免死循环?

    循环条件:

    (单链表)p != NULL->p != first (循环链)
    
    (单链表)p->link != NULL->p->link != first(循环链)
    
    循环链表类的定义
    template <class T>    //结点定义
    struct CircLinkNode {
    T  data;			 //结点数据
    CircLinkNode<T> *link;      //链接指针
    
    CircLinkNode ( CircLinkNode<T> *next = NULL ) : 
        link ( next ) { }     
    CircLinkNode ( T d ,CircLinkNode<T> *next = NULL ) : data ( d ), link ( next ) { }
    };
    
    
    template <class T> 
    class CircList :pulic LinearList<T> {
    private: 
         CircLinkNode<T> *first, *last;
         //链表的表头指针、当前指针和表尾指针
    public: 
         CircList ( const T& x );	
         CircList ( CircList<T>& L);			
         ~CircList ( );				
         int Length ( ) const;				
         bool IsEmpty ( ) { return first->link == first; }
    
    
    CircLinkNode<T> * getHead( )const;
    void setHead(CircLinkNode<T> *p); 
    CircLinkNode<T> * Search(T x);
    CircLinkNode<T> * Locate(int i);
    T *getData ( int i );			
    void setData( int i, T& x);
    bool Insert (int i, T& x ); 
    bool Remove (int i, T& x );
    };
    //循环链表与单链表的操作实现,最主要的不同就是扫描到链尾,遇到的不是NULL,而是表头first
    
    
    
    
    循环链表类部分操作的实现
    template <class T> //循环链表的搜索算法
    CircListNode<T> * CircList<T>::Search( T x ) 
    {
    //在链表中从头搜索其数据值为 x 的结点
         current = first->link;
         while ( current != first && current->data != x )  
             current = current->link;
         return current;
    }
    
    
    template <class T>  //循环链表——插入
     bool CircList<T> ::Insert(int i, T x)//在i项后面插入一项
     {   if i<0 return false; 
        CircLinkNode<T> *  p ;  int count;   
        if (i==0) {p=first;count=0}else{ p=first->link; count=1;}
    //第一个位置特别处理。避免指针出界状态与初始状态重合
         while (p != first && count < i )
         {
              p = p->link;    count++;
         }
         if (p == first&&i!=0) return false;
         else { 
              s = new CircLinkNode<T>; s->data = x;  
              s->link = p-> link; p-> link = s;
              return true;
         }
    }     
    //循环链表,循环指针结束条件不同于单链表,所以初始化不同。需要特别考虑第一个元素
    
    
    
    
    用循环链表求解约瑟夫问题

    约瑟夫问题的提法
    n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数, 报到第 m 个人,令其出列。然后再从下一 个人开始,从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去, 直到圆圈中只剩一个人为止。此人即为优胜者。

    1、带头结点的完整代码 
    #include <iostream>
    using namespace std;
    
    template <class T>    //结点定义
    struct CircLinkNode {
    T  data;			 //结点数据
    CircLinkNode<T> *link;      //链接指针
    
    CircLinkNode ( CircLinkNode<T> *next = NULL ):link ( next ) { }     
    CircLinkNode ( T d,CircLinkNode<T> *next = NULL ):data(d), link(next) { }
    };
    
    template <class T> 
    class CircList{
    private: 
         CircLinkNode<T> *first, *last;//链表的表头指针、当前指针和表尾指针
    public: 
        	CircList(){first=new CircLinkNode<T>(); first->link=first;}; 
         //CircList ( const T& x );	
        // CircList ( CircList<T>& L);			
        // ~CircList ( );
        // void setHead(CircLinkNode<T> *p); 
        // CircLinkNode<T> * Search(T x);
        // CircLinkNode<T> * Locate(int i);
        // T *getData ( int i );			
        // void setData( int i, T& x);
            bool insert (int i, T& x ); 
        // bool Remove (int i, T& x );
    	//bool IsEmpty ( ) { return first->link == first; }
        // int Length ( ) const;				
    	   CircLinkNode<T> * getHead( )const{return first;};
    	 //void input(int i); //输入i个元素
    
        };
    
    template <class T>  
    bool CircList<T>::insert(int i, T& x)//在i项后面插入一项
     {   if(i<0)return false; 
        CircLinkNode<T> *  p,s ;  
    	int count ;   
        if(i==0) {p=first;count=0;}
    	else {p=first->link;count=1;}  //第一个位置特别处理。避免循环指针出界状态与初始状态重合
        
    	while (p != first && count < i )
         {
              p = p->link; 
              count++;
         }
         if (p == first&&i!=0) return false;
         else { 
             // s = new CircLinkNode<T>(); s->data = x;  
             // s->link = p->link; p->link = s;
    		  p->link=new CircLinkNode<T>(x,p->link);
    		  cout<<"input:"<<x<<endl;
              return true;
         }
    }     
    
    
    template <class T> 
    void Josephus(CircList<T>& Js, int n, int m) {
        CircLinkNode<T> *p,*first, *pre = NULL;
    	first= Js.getHead(); 
    	p=first->link; 
    	if(p==first) exit(1); //表空退出
         int i, j;
         for ( i = 0; i < n-1; i++ ) {     	//执行n-1次
    		  
    		 if(p==first)p=p->link;
             for ( j = 1; j < m; j++) 		//数m-1个人
                  { 
    			    pre = p;  p = p->link; 
    		        if(p==first)j--;
    		  }
              cout << "出列的人是" << p->data << endl; 
              pre->link = p->link;  delete p;     	//删去
              p = pre->link; 		
          }
    };
    
    void main() {		
         CircList<int> clist;
         int i,n,m;		
         cout << "输入游戏者人数和报数间隔 : ";
         cin >> n >> m;
         for (i = 1; i <= n; i++ ) clist.insert(i-1, i);    //约瑟夫环    
    	 // inclist.input(n);  //函数需考虑第一个元素插入的特殊性
         Josephus(clist, n, m);                 //解决约瑟夫问题
    }
    
    
    2 不带头结点的完整代码
    #include <iostream>
    using namespace std;
    
    template <class T>    //结点定义
    struct CircLinkNode {
    T  data;			 //结点数据
    CircLinkNode<T> *link;      //链接指针
    
    CircLinkNode ( CircLinkNode<T> *next = NULL ):link ( next ) { }     
    CircLinkNode ( T& d,CircLinkNode<T> *next = NULL ):data(d), link(next) { }
    };
    
    template <class T> 
    class CircList{
    private: 
         CircLinkNode<T> *first, *last;//链表的表头指针、当前指针和表尾指针
    public: 
        	CircList(){first=last=NULL;}
         //CircList ( const T& x );	
        // CircList ( CircList<T>& L);			
        // ~CircList ( );
    	// void setHead(CircLinkNode<T> *p); 
        // CircLinkNode<T> * Search(T x);
        // CircLinkNode<T> * Locate(int i);
        // T *getData ( int i );			
        // void setData( int i, T& x);
        //    bool insert (int i, T& x ); 
        // bool Remove (int i, T& x );
    	//bool IsEmpty ( ) { return first->link == first; }
        // int Length ( ) const;				
    		CircLinkNode<T> * getHead( )const{return first;};
    	    void input(int i); //从空表开始,输入i个元素
    
        };
    
    template <class T>  
    void CircList<T>::input(int i)//插入i项
    { if(i<=0) return;
      //makeEmpty(); 清空原表 
       T temp;
       cin>>temp;
      first=last=new CircLinkNode<T>(temp); first->link=first; //特别处理第一个元素
      	cout<<"输入元素"<<temp<<endl;
      for(int j=2;j<=i;j++)
      {
    	cin>>temp;
        last->link=new CircLinkNode<T>(temp,last->link);
    	last=last->link;
    	cout<<"输入元素"<<temp<<endl;
      }
    
    }
    
    template <class T> 
    void Josephus(CircList<T>& Js, int n, int m) {
        CircLinkNode<T> *p,*first, *pre = NULL;
    	p=first= Js.getHead(); 
    	if(first==NULL) exit(1); //表空退出
         int i, j;
         for ( i = 0; i < n-1; i++ ) {     	//执行n-1次
    		  
             for ( j = 1; j < m; j++) 		//数m-1个人
                  { 
    			    pre = p;  p = p->link; 
    		  }
              cout << "出列的人是" << p->data << endl; 
              pre->link = p->link;  delete p;     	//删去
              p = pre->link; 		
          }
    };
    
    void main() {		
         CircList<int> clist;
         int n,m;		
         cout << "输入游戏者人数和报数间隔 : ";
         cin >> n >> m;
         //for (i = 1; i <= n; i++ ) clist.insert(i-1, i);    //约瑟夫环    
    	 clist.input(n);  //函数需考虑第一个元素插入的特殊性
         Josephus(clist, n, m);                 //解决约瑟夫问题
    }
    
    
    
    
    

    双向循环链表(简称双链表)

    双向链表简称双链表,为了解决链表在前驱和后继方向都能遍历的线性链表。

    双向链表通常采用带附加头结点的循环链表形式。




    双向循环链表类的定义
    template <class T> 
    struct DblNode {           //链表结点的定义
       T data;                                           //数据
       DblNode<T> * lLink, * rLink;     //指针
       
      DblNode( DblNode<T> * left=NULL, DblNode<T> * right =NULL ) :   lLink (left), rLink (right){ }
       DblNode (T value, DblNode<T> * left=NULL, 
            DblNode<T> *  right=NULL ) :
             data (value), lLink (left), rLink (right) { }
    };
    
    
    
    template <class T>
    class DblList:Public LinearList<T> {
    private:
        DblNode<T> * first;
    public:
        DblList ( T uniqueVal );    //构造函数
        ~DblList ( );                             //析构函数
         int Length ( ) const;                 //计算长度
         bool IsEmpty ( )                    //判链表空否
            { return first->rlink == first; }   
    
    
    DblNode<T>* getHead( )const {return first; }
    void setHead(DblNode<T> *ptr){ first=ptr; }
    DblNode<T>* Search(const t& x);
         //沿后继方向寻找等于x的结点
    DblNode<T> *Locate(int i, int d);
        //定位序号为i的结点,
       //d=0按前驱方向,否则按后继方向
    void Insert (int i, T& x,int d );			
       //在第i个结点后插入一个包含值x的新结点
        bool Remove (int i, T& x,int d );	//删除第i个结点
    };
    
    
    
    
    双向循环链表的部分实现
    template <class T> //-定位算法
     DblNode<T>* DblList::Locate(int i,int d) {
    //定位第i个结点, d=0为前驱方向,否则为后继方向。
      if(i<0) return NULL;
       if(i==0)return first ; 
        DblNode<T> * current ;
        if(d==0) current=first->lLink;
        else current=first->rLink;
        for(int j=1;j<i;j++)
    	 if ( current == first )  break; //越界
             else if(d==0) current = current->lLink;	
                     else current = current->rLink;	
        if(current != first) return current;
        else return NULL ;
    }
    
    //删除算法(非空表)
    current->rLink->lLink = current->lLink;        
    current->lLink->rLink = current->rLink;
    delete current ;
    
    
    

    注意双向链表的插入:

    双向循环链表的插入分前驱方向的插入和后继方向的插入。
    要求在第i个结点之后插入新结点NewNode,则
    1.后继方向:从前往后找第i个结点current,在current之后插入NewNode
    2.前驱方向:从后往前找第i个结点current,在current之前插入NewNode。

    静态链表

    静态链表:用数组来表示单链表,用数组元素的下标来模拟单链表的指针。

    data:存储放数据元素;
    next:也称游标,存储该元素的后继在数组的下标。

    静态链表的结构

    0号是表头结点,link给出首元结点地址。
    循环链表收尾时link = 0,回到表头结点。如果不是循环链表,收尾结点指针link=-1。
    link指针是数组下标,因此是整数。

    //静态链表的存储结构定义如下:
    const int MaxSize = 100;        //100只是示例数据
    template <class DataType>
    struct SNode
     {
        DataType data;           // DataType表示不确定的数据类型
        int next;                       //指针域(也称游标)
    } SList[MaxSize];
    
    
    静态链表的应用——多项式 (Polynomial)

    n阶多项式 Pn(x) 有 n+1 项。
    系数 a0, a1, a2, …, an
    指数 0, 1, 2, …, n。按升幂排列

    多项式的存储表示:

    //1.用一维静态数组表示,下标就是指数,值是系数
    private: 
    	int degree;		
    	float coef [maxDegree+1];
    Pn(x)可以表示为: 
          pl.degree = n
          pl.coef[i] = ai //0=<i<=n
          
    //2.用一维动态数组存储多项式的系数,下标即指数
    private: 	
    int degree;
    float * coef;
    
    Polynomial::Polynomial (int sz) {//在构造函数里初始化并分配空间。
    		    degree = sz;
    		    coef = new float [degree + 1];
      	        }
    //以上两种存储表示适用于指数连续排列的多项式。对于指数跳跃很大的稀疏多项式来说则太浪费空间了。
    
    
    //3.设一元多项式多大可能阶为maxDegree,当前的多项式的最高阶为n。数组元素存放非零项的系数和指数,下标不再是指数
    
    //4.用链表表示多项式,每个项term为如下结构:	
    Term |coef|exp|link|
    优点是: 多项式的项数可以动态地增长,不存在存储溢出问题,也不浪费空间。 插入、删除方便。
    
    
    

    多项式(polynomial)类的链表定义:

    struct Term {	            //多项式结点定义	
        float coef;	            //系数		
        int exp;		            //指数
        Term *link;		  //链接指针
        
       Term (float c, int e, Term *next = NULL)
            { coef = c;  exp = e;  link = next;}
    	Term *InsertAfter ( float c, int e); 
        friend ostream& operator << (ostream&, 
                      const Term& );
    }; 
    
    Term *Term::InsertAfter ( float c, int e ) {
    //在调用此函数的对象后插入一个新项
         link = new Term (c, e, link);	
                    	       //创建一个新结点,自动链接
    		                 //插入到this结点后面
         return link;
    };  
    
    class Polynomial {			//多项式类的定义
    public:
    	Polynomal() { first = new Term(0, -1); }	//构造函数
    	Polynomal ( Polynomal& R);            //复制构造函数
    	int maxOrder();	  			  //计算最大阶数
    private:
    	Term *first;
        friend ostream& operator << (ostream&, 
               const Polynomal& );
        friend istream& operator >> ( istream&, 
              Polynomal& );
        friend Polynomial operator + ( Polynomial& A, Polynomial& B );
        friend Polynomial operator * ( Polynomial& A, Polynomial& B);
    };
    
    

    多项式的相加:

    Polynomial operator + 
        (Polynomial & A, Polynomial & B) {
        Term *pa, *pb, *pc, *p;
         float  temp; 
         Polynomal C; 
         pc=C.getHead( );
         pa = A.getHead( )->link;   
         pb = B.getHead( )->link;   //检测指针pa和pb都指
                         //向自己所指多项式链表第一个结点
                         
    while ( pa!=NULL && pb!=Null ){①
    	if(pa->exp==pb->exp){②
    		temp=pa->coef+pb->coef;
    		if (fabs(temp)>0.001)
    			pc=pc->InsertAfter(temp,pa->exp);
    		pa=pa->link;  pb=pb->link; ②}
    	else if(pa->exp < pb->exp) {③
    		pc=pc->InsertAfter(pa->coef, pa->exp);
    		pa=pa->link ;
             		} ③
    		else {④
    			pc=pc->InsertAfter(pb->coef, pb->exp) ;
    			pb=pb->link;
    			} ④
    } ①
    
    //以下代码是把余下的A或B部分链到C的后面。
    if(pa!=NULL) p=pa; 
    else p=pb;
    while(p!=NULL)
    {
    	pc=pc->InsertAfter(p->coef, p->exp);
    	p=p->link;
    }
    return C;
    } 
    
    
    

    限于篇幅,有些知识点没有列出来,感觉以上这些是比较重要一点的东西。emmm,写博客也不需要面面俱到吧(努力为偷懒找借口)qwq。

  • 相关阅读:
    JavaScript 学习笔记 事件二
    auto_ptr
    POJ2299 UltraQuickSort(逆序对个数)
    2016年11月2日22:28:14
    将sql server中的数据倒入Excel(c#)
    线段树成段更新裸题POJ3468
    线段树成断更新裸题hdu1698 Just a Hook
    POJ2828 思维难度较好的一道线段树
    < 弱牛刷贪心给JerryDung&qiuwei大神Orz>最大乘积
    NOIP 2008 传纸条题解[双线程DP]
  • 原文地址:https://www.cnblogs.com/gylic/p/12809962.html
Copyright © 2011-2022 走看看