数据结构与算法教程(C++版)第二章(线性表)知识点整理

2.1 线性表的逻辑结构

2.1.1 线性表的概念

线性表是由类型相同的数据元素组成的有限序列,记作:
(a1, a2, …,ai,ai+1,…, an
该序列中的所有结点具有相同的数据类型

注:
ai 是线性表中数据元素
ai称为ai+1的直接前驱,简称为前驱
ai+1称为ai的直接后继,简称为后继
a1称为线性表的第一个(首)结点
an称为线性表的最后一个(尾)结点。

n 指线性表长度
当n=0时,称为空表。

2.1.2 线性表的特征

除第一个元素外,其他每一个元素有一个且仅有一个 直接前驱
除最后一个元素外,其他每一个元素有一个且仅有一个 直接后继
线性表中的结点可以是单值元素(每个元素只有一个数据项)
26个英文字母组成的字母表: (A,B,C、…、Z)

线性表中的结点可以是记录型元素,每个元素含有多个数据项 ,每个项称为结点的一个域 。每个元素有一个可以唯一标识每个结点的数据项组,称为关键字

2.1.3 线性表基本操作

(1)int Length() const
初始条件:线性表已存在。
操作结果:返回线性表元素个数。
(2)bool Empty() const
初始条件:线性表已存在。
操作结果:如线性表为空,则返回true,否则返回false。
(3)void Clear()
初始条件:线性表已存在。
操作结果:清空线性表。
(4)void Traverse(void (*visit)(const ElemType &)) const
初始条件:线性表已存在。
操作结果:依次对线性表的每个元素调用函数(*visit)。
(5)StatusCode GetElem(int position, ElemType &e) const
初始条件:线性表已存在,1≤position≤Length()。
操作结果:用e返回第position个元素的值。
(6)StatusCode SetElem(int position,const ElemType &e)
初始条件:线性表已存在,1≤position≤Length()。
操作结果:将线性表的第position个位置的元素赋值为e。
(7)StatusCode Delete(int position, ElemType &e)
初始条件:线性表已存在,1≤position≤Length()。
操作结果:删除线性表的第position个位置的元素, 并用e返回其值,长度减1。
(8)StatusCode Insert(int position, const ElemType &e)
初始条件:线性表已存在,1≤position≤Length()+1。
操作结果:在线性表的第position个位置前插入元素e,长度加1。

2.2 线性表的存储结构

2.2.1 线性表的顺序存储结构

顺序表的定义:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表
顺序表的特点:

1.所有元素的逻辑先后顺序与其物理存放顺序一致  
2.数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现
3.可利用一维数组描述存储结构,采用线性表的顺序存储方式
4.插入或删除操作需移动大量的数据元素

在这里插入图片描述
在具体的机器环境下:设线性表的每个元素需占用l个存储单元,以所占的第一个单元的存储地址作为数据元素的存储位置。

线性表中第i+1个数据元素的存储位置LOC(ai+1)和第i个数据元素的存储位置LOC(ai)之间满足下列关系:
          LOC(ai+1)=LOC(ai)+l
线性表的第i个数据元素ai的存储位置为:
          LOC(ai)=LOC(a1)+(i-1)*l  

顺序表(SeqList)类模板的定义:

// 顺序表类模板

template <class ElemType>
class SqList 
{
      protected:
     // 顺序表实现的数据成员:
      int count;			// 元素个数
      int maxSize;		// 顺序表最大元素个数
      ElemType *elems;	// 元素存储空间
     // 辅助函数模板
      bool Full() const;	// 判断线性表是否已满
      void Init(int size);	// 初始化线性表

      public:
    // 抽象数据类型方法声明及重载编译系统默认方法声明:
      SqList(int size = DEFAULT_SIZE);// 构造函数模板
      virtual ~SqList();	// 析构函数模板
      int Length() const;	// 求线性表长度			 
      bool Empty() const;	// 判断线性表是否为空
      void Clear();		// 将线性表清空

      void Traverse(void (*visit)(const ElemType &)) const;			// 遍历线性表
      StatusCode GetElem(int position, ElemType &e) 	const;			// 求指定位置的元素	
      StatusCode SetElem(int position, const ElemType &e); 	// 设置指定位置的元素值
      StatusCode Delete(int position, ElemType &e);		// 删除元素		
      StatusCode Insert(int position, const ElemType &e);
	// 插入元素
      SqList(const SqList<elemType> &copy); 
	// 复制构造函数模板
      SqList<elemType> &operator =(const
 	  SqList<elemType> &copy); 	// 重载赋值运算符
};

判定线性表是否已满

template <class ElemType>
bool SqList<ElemType>::Full() const
{
       return count == maxSize;
}

初始化最大元素个数为size的空线性表

template <class ElemType>
void SqList<ElemType>::Init(int size)
{
      maxSize = size;			      // 最大元素个数
      if (elems != NULL) delete []elems;// 释放存储空间
      elems = new ElemType[maxSize];// 分配存储空间
      count = 0;			// 空线性表元素个数为0
}

构造一个最大元素个数为size的空顺序表

template <class ElemType>
SqList<ElemType>::SqList(int size)
{
      elems = NULL;		// 未分配存储空间前,elems为空
      Init(size);			// 初始化线性表
}

销毁线性表

template <class ElemType>
SqList<ElemType>::~SqList()
{
     delete []elems;		// 释放存储空间
}

顺序表的插入
在线性表 L= (a1,…a i-1,ai, ai+1,…,an) 中的第i(1≦i≦n)个位置前插入一个新结点e,使其成为线性表:
L=(a1,…a i-1,e,ai,ai+1,…,an)

实现步骤:

1.线性表是否满?
2.插入位置是否全法?
3.将线性表L中的第i个至第n个结点后移一个位置。
4.将结点e插入到结点ai-1之后。
5.线性表长度加1。

操作代码:

template <class ElemType>
StatusCode SqList<ElemType>::Insert(int position,const ElemType &e)
{
      int len = Length();
      ElemType tmp;
      if (Full())
       {	
        	return OVER_FLOW;	
       }
      else if (position < 1 || position > len + 1)
       {	
	        return RANGE_ERROR;
       }
       else
       {	
	        count++;	
	        for (int curPosition = len; curPosition >= position;  curPosition--)
	           {
		          GetElem(curPosition, tmp);
		          SetElem(curPosition + 1, tmp); 
	           }
	        SetElem(position, e);//将e赋值到position位置处
	        return SUCCESS;    // 插入成功
     	}
}

插入操作时间复杂度分析
用结点的移动来估计算法的时间复杂度。
在表中第 i 个位置插入,从data[i-1] 到data [n-1] 成块后移,移动(n-1)-(i-1)+1 = n-i+1项。
设在线性表L中的第i个元素之前插入结点的概率为Pi,不失一般性,设各个位置插入是等概率,则Pi=1/(n+1)
总的平均移动次数:
Einsert=∑pi*(n-i+1) (1≦i≦n+1) =n/2

算法的平均时间复杂度为O(n)

顺序表的删除
在线性表 L= (a1,…a i-1,ai, ai+1,…,an) 中删除结点ai(1≦i≦n),使其成为线性表:
L= (a1,…ai-1,ai+1,…,an)

实现步骤:

1.删除位置是否合法?
2.将线性表L中的第i+1个至第n个结点依此向前移动一个位置。
3.线性表长度减1

操作代码:

template <class ElemType>
StatusCode SqList<ElemType>::Delete(int position, ElemType &e)
{
    int len = Length();
    ElemType tmp;
    if (position < 1 || position > len)
       {	
	       return RANGE_ERROR;
       }
       else
       {	
	       GetElem(position, e);
	       for (int curPosition = position + 1; curPosition <= len; curPosition++)
           	{	 
		       GetElem(curPosition, tmp);
		    SetElem(curPosition - 1, tmp); 
	        }
	       count--;	// 删除后元素个数将自减1
	       return SUCCESS;
       }
}

删除操作时间复杂度分析
用结点的移动来估计算法的时间复杂度。
设在线性表L中删除第i个元素的概率为Pi,不失一般性,设删除各个位置是等概率,则Pi=1/n
删除第 i 个表项,需将第 i+1 项到第 n 项全部前移,需前移的项数为 n-(i+1)+1 = n-i
则总的平均移动次数: Edelete=∑pi*(n-i) (1≦i≦n)
Edelete=(n-1)/2
算法的平均时间复杂度为O(n)

2.2.2 线性表的链式存储结构

链表定义:用一组任意的存储单元存储线性表中的数据元素。用这种方法存储的线性表简称线性链表。存储链表中结点的一组任意的存储单元可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。

2.2.2.1 单链表

在存储每个结点的同时,还必须存储指示其直接后继结点的地址(或位置),称为指针(pointer)或链(link),这两部分组成了链表中的结点结构
链表是通过每个结点的指针域将线性表的n个结点按其逻辑次序链接在一起的。在这里插入图片描述
每一个结只包含一个指针域的链表,称为单链表

                  data(元素的值) + next(后继)=  结点(表示数据元素)

用一组地址任意的存储单元存放线性表中的数据元素,以“结点的序列”表示线性表称作单链表在这里插入图片描述
例:
在这里插入图片描述
常见的指针操作:
在这里插入图片描述
在这里插入图片描述
单链表的类定义:
使用面向对象方法,要把数据与操作一起定义和封装,用多个类表达一个单链表。
链表结点(ListNode)类
链表(List)类

单链表的相关类模板:

// 结点类模板

template <class ElemType>
struct Node 
{
     // 数据成员:
     ElemType data;			// 数据域
     Node<ElemType> *next;	// 指针域

     // 构造函数模板:
     Node();			// 无参数的构造函数模板
     Node(ElemType item, Node<ElemType> *link = NULL);// 已知数据域和指针域建立结构
};

// 简单线性链表类模板

template <class ElemType>
class SimpleLinkList 
{
      protected:
     // 链表实现的数据成员:
      Node<ElemType> *head;	// 头结点指针

     // 辅助函数模板:
      Node<ElemType> *GetElemPtr(int position) const;		// 返回指向第position个结点的指针
      void Init();				// 初始化线性表
      public:
     //  抽象数据类型方法声明及重载编译系统默认方法声明:
      SimpleLinkList();		// 无参数构造函数模板
      virtual ~SimpleLinkList();	// 析构函数模板
      int Length() const;		// 求线性表长度	 
      bool Empty() const;	// 判断线性表是否为空
      void Clear();			// 将线性表清空
      void Traverse(void (*visit)(const ElemType &)) const;				// 遍历线性表
      StatusCode GetElem(int position, ElemType &e) const;				// 求指定位置的元素
      StatusCode SetElem(int position, const ElemType &e);			// 设置指定位置的元素值
      StatusCode Delete(int position, ElemType &e);
	  // 删除元素		
      StatusCode Insert(int position, const ElemType &e);
	  // 插入元素
      SimpleLinkList(const SimpleLinkList<ElemType>  &copy); 			// 复制构造函数模板
      SimpleLinkList<ElemType> &operator =(const SimpleLinkList<ElemType> &copy); 
	  // 重载赋值运算符
};

获得第i个结点指针

template<class ElemType>
Node<ElemType> *SimpleLinkList<ElemType>::GetElemPtr(int position) const
{
    Node<ElemType> *tmpPtr = head;		// 用tmpPtr遍历线性表以查找第position个结点
    int curPosition = 0;	// tmpPtr所指结点的位置
    while (tmpPtr != NULL && curPosition < position)
// 顺指针向后查找,直到tmpPtr指向第position个结点
      {
	      tmpPtr = tmpPtr->next;
	      curPosition++;
      }
 	if (tmpPtr != NULL && curPosition == position)
      {	// 查找成功
        	return tmpPtr;	
      }
    else
      {	// 查找失败
	        return NULL;	
      }
}

注:时间复杂度: O(n)

单链表的插入
插入运算是将值为e的新结点插入到表的第i个结点的位置上,即插入到ai-1与ai之间。
在这里插入图片描述
实现步骤:

1.找到ai-1所在的结点tmpPtr
2.生成一个数据域为e的新结点newPtr
newPtr = new Node<ElemType>(e, tmpPtr->next); 
3.newPtr结点作为tmpPtr的直接后继结点
tmpPtr->next = newPtr;

操作代码:

template <class ElemType>StatusCode SimpleLinkList<ElemType>::Insert( int position, const ElemType &e)
{
      if (position < 1 || position > Length() + 1)
         {	
	          return RANGE_ERROR;
         }
      else
         {
	          Node<ElemType> *tmpPtr;
	          tmpPtr = GetElemPtr(position - 1);		
	          Node<ElemType> *newPtr;
	          newPtr = new Node<ElemType>(e,tmpPtr->next);	
	          tmpPtr->next = newPtr;			
	          return SUCCESS;
          }
}

插入操作时间复杂度分析
设链表的长度为n,合法的插入位置是1≦i≦n+1,算法的时间主要耗费移动指针p上,时间复杂度为O(n)

单链表的删除
在这里插入图片描述

实现步骤:

1.找到ai-1的存储位置p
2.令p–>next指向ai的直接后继结点,即把ai从链上摘下
3.释放结点ai的空间

操作代码:

template <class ElemType>
StatusCode SimpleLinkList<ElemType>::Delete(int position, ElemType &e)
{
    	if (position < 1 || position > Length())
        	{	
	             return RANGE_ERROR;
            }
        else
            {	
	             Node<ElemType> *tmpPtr;
	             tmpPtr = GetElemPtr(position - 1);
	             Node<ElemType> *nextPtr = tmpPtr->next;
	             tmpPtr->next = nextPtr->next;
	             e = nextPtr->data;
	             return SUCCESS;
            }
}

删除操作时间复杂度分析
设单链表长度为n,则删去第i个结点仅当1≦i≦n时是合法的,算法的时间主要耗费移动指针p上,时间复杂度为O(n)

2.2.2.2 循环链表

循环链表的概念
循环链表是一种头尾相接的链表,最后一个结点的指针域指向链表的头结点,整个链表的指针域链接成一个环
特点:从循环链表的任意一个结点出发都可以找到链表中的其它结点,使得表处理更加方便灵活。
在这里插入图片描述
循环链表的相关类模板

// 简单循环链表类模板

template <class ElemType>
class SimpleCircLinkList 
{
        protected:
      //  循环链表实现的数据成员:
        Node<ElemType> *head;		// 头结点指针
     // 辅助函数模板:
        Node<ElemType> *GetElemPtr(int position) const;
	 // 返回指向第position个结点的指针
        void Init();					// 初始化线性表
        public:
     //  抽象数据类型方法声明及重载编译系统默认方法声明:
        SimpleCircLinkList();	// 无参数的构造函数模板
        virtual ~SimpleCircLinkList();	// 析构函数模板
        int Length() const;			// 求线性表长度
        bool Empty() const;	// 判断线性表是否为空
        void Clear();				// 将线性表清空
        void Traverse(void (*visit)(const ElemType &))  const;					// 遍历线性表
        StatusCode GetElem(int position, ElemType &e)  const;				// 求指定位置的元素
        StatusCode SetElem(int position, const ElemType  &e);			// 设置指定位置的元素值
        StatusCode Delete(int position, ElemType &e);
     	// 删除元素		
        StatusCode Insert(int position, const ElemType &e);
	   // 插入元素
        SimpleCircLinkList(const SimpleCircLinkList<ElemType> &copy); 
	   // 复制构造函数模板
        SimpleCircLinkList<ElemType> &operator =(const
	    SimpleCircLinkList<ElemType> &copy);
	  // 重载赋值运算符
};

2.2.2.3 双向链表

双向链表(Double Linked List) :指的是构成链表的每个结点中设立两个指针域:一个指向其直接前趋的指针域prior,一个指向其直接后继的指针域next。这样形成的链表中有两个方向不同的链,故称为双向链表
在这里插入图片描述
注:双向链表通常采用带表头结点的循环链表形式
双向链表相关类模板

// 双向链表结点类模板

template <class ElemType>
struct DblNode 
{
    // 数据成员:
     ElemType data;				// 数据域
     DblNode<ElemType> *back;	// 指向前驱的指针域
     DblNode<ElemType> *next;	// 指向后继的指针域
     // 构造函数模板:
     DblNode();						// 无数据的构造函数模板
     DblNode(ElemType item, 
     DblNode<ElemType> *linkBack = NULL,
     DblNode<ElemType> *linkNext = NULL);// 已知数据域和指针域建立结构
};

// 双向链表类模板
template <class ElemType>
class SimpleDblLinkList 
{
     protected:
       //  循环链表实现的数据成员:
     DblNode<ElemType> *head; // 头结点指针

       // 辅助函数模板:
     DblNode<ElemType> *GetElemPtr(int position) const;	// 返回指向第position个结点的指针
     void Init();	// 初始化线性表
     public:
       //  抽象数据类型方法声明及重载编译系统默认方法声明:
     DblLinkList();		// 无参数的构造函数模板
     virtual ~DblLinkList();		// 析构函数模板
     int Length() const;			// 求线性表长度	 
     bool Empty() const;	// 判断线性表是否为空
     void Clear();				// 将线性表清空
     void Traverse(void (*visit)(const ElemType &)) const;	// 遍历线性表
     int GetCurPosition() const;	// 返回当前位置
     StatusCode GetElem(int position, ElemType &e) const;	// 求指定位置的元素	
     StatusCode SetElem(int position, const ElemType &e);	// 设置指定位置的元素值
     StatusCode Delete(int position, ElemType &e);			// 删除元素		
     StatusCode Insert(int position, const ElemType &e);		// 插入元素
     DblLinkList(const DblLinkList<ElemType> &copy);			// 复制构造函数模板
     DblLinkList<ElemType> &operator =(const     DblLinkList<ElemType> &copy); // 重载赋值运算符
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值