数据结构 复习笔记 2 线性表

基本概念

定义:n(n\geq0)个具有相同类型的数据元素的有限序列

长度:线性表中数据元素的个数({a_{i}}^{}称为数据元素,下标i表示该元素在线性表中的位置或序号)

空表:长度等于0的线性表,L=()

特性:有限性、相同性、顺序性(前驱和后继的关系,{a_{1}}无前驱,{a_{n }}^{}无后继)

顺序存储结构---顺序表(静态存储分配,事先确定容量)

特点:指的是用一组地址连续的存储单元依次存储线性表中的各个元素

        (通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系)

顺序存储的实现:一维数组存储顺序表中的数据

随机存取O(1)时间内存取数据元素

顺序表存储如下图:

const int MaxSize=100;  
class SeqList
{
 public:
     SeqList( ) ;                    //构造函数
     SeqList(int a[ ], int n);       
     ~SeqList( ) ;                  //析构函数
     int Length( );                 //长度
     int Get(int i); 
     int Locate(int x ); 
     void Insert(int i, int x);      //插入
     int Delete(int i);              //删除
 private:
     int data[MaxSize]; 
     int length;
};
SeqList::SeqList( )            //无参构造函数
{  
    length=0; 
}
SeqList::SeqList(int a[ ], int n)     //有参构造函数
{  
       if (n>MaxSize) throw "参数非法";
       for (i=0; i<n; i+ +)  
             data[i]=a[i];
       length=n; 
 }
int SeqList::Locate(int x)            //按值查找
{
     for (i=0; i<length; i++)
         if (data[i]==x) return i+1; 
     return 0;          
}
void SeqList::Insert(int i, int x)      //插入(注意边界条件)
{
      if (length>=MaxSize) throw  "上溢";     //长度大于最大容量不能插入
      if (i<1 | | i>length+1) throw  "位置不合理 ";    //第一个元素之前最后一之后不能插入
      for (j=length; j>=i; j--)
           data[j]=data[j-1]; 
      data[i-1]=x;
      length++;
}
int SeqList::Delete(int i)              //删除
{
      int x;
      if (length==0) throw  "下溢";   
      if (i<1 | | i>length+1) throw  "删除未知错误 ";   
      x = data[i-1];               //取出i的位置
      for (int j=i; j<length; j++)
           data[j-1]=data[j]; 
      length--;
      return x;
}
void SeqList::PrintList()    //遍历操作
{
      for(int i=0;i<length;i++)
         cout<<data[i]<<"\t";
      cout<<endl;
}
int main()
{
    int a[100],j;
    for(int i=0; i<10; i++)
    {
        cin>>a[i];
    }                         //输入数据
    SeqList L(a,10);
    cout<<"请输入要插入的元素位置:";
    cin>>j;
    L.Insert(j,6);            //在第j个位置插入6
    L.PrintList();             //显示插入后的数据
    cout<<"请输入要删除的元素位置:";
    cin>>j;
    L.Delete(j);           //删除第j个位置的元素
    L.PrintList();         //显示删除后的数据
}

时间复杂度分析:

最好情况(i = n+1):基本语句执行0次,时间复杂度为O(1)

最坏情况(i = 1):基本语句执行n次,时间复杂度为O(n)

平均情况(1\leqslant i\leqslant n+1):\sum_{i=1}^{n+1}{p_{i}}^{}(n-i+1)=\frac{1}{n+1}\sum_{i=1}^{n+1}(n-i+1)=\frac{n}{2}=O(n)

顺序表优点:①无需为表示表中元素之间的逻辑关系而增加额外的存储空间

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

顺序表缺点: ① 插入和删除操作需要移动大量元素;

                       ②表的容量难以确定,表的容量难以扩充

                       ③ 造成存储空间的碎片

链式存储结构

    ---单链表、双向链表、循环链表(动态存储分配,运行时分配空间)

单链表

线性表的链式存储结构(用一组任意(连续、不连续、零散分布)的存储单元存放线性表的元素)

头指针:指向第一个结点的地址              尾标志:终端结点的指针域为空

空表:first = NULL

非空表

 将空表与非空表统一:变为带头结点的单链表

(头结点:在单链表的第一个元素结点之前附设一个类型相同的结点,

                  目的是让空表和非空表处理统一)

struct Node
{
     int data;
     Node *next;
}; 
class LinkList
{   
public:
     LinkList( );
     LinkList(int a[ ], int n); 
     ~LinkList( );                    
     void Insert(int i, int x);   
     int Delete(int i);        
     void PrintList( );           
private:
     Node *first; 
}
LinkList::LinkList(int a[],int n)
{
    first = new Node;
    Node *r = first,*s = nullptr;
    for(int i=0;i<n;i++)
    {
        s = new Node;
        s->data = a[i];
        r->next = s;
        r = s;
    }
    r->next = nullptr;
}
void LinkList::PrintList()
{
    Node*p = first->next;
    while(p!=nullptr)
    {
        cout<<p->data<<"\t";
        p = p->next;
    }
    cout<<endl;
}
void LinkList::Insert(int i,int x)
{
    Node*p = first,*s = nullptr;
    int count = 0;
    while(p!=nullptr&&count<i-1)
    {

        p = p->next;
        count++;
    }
    if(p==nullptr)
        throw "插入位置错误";
    else
    {
        s = new Node;
        s->data = x;
        s->next = p->next;
        p->next = s;
    }
}
int LinkList::Delete(int i)
{
    int x;
    Node *p = first,*q = nullptr;
    int count = 0;
    while(p!=nullptr&&count<i-1)
    {
        p = p->next;
        count++;
    }
    if(p==nullptr||p->next==nullptr)
        throw "删除位置错误";
    else
    {
        q = p->next;
        x = q->data;
        p->next = q->next;
        delete q;
        return x;
    }
}
int main()
{
    int a[10],x,m,n;
    for(int i=0; i<10; i++)
    {
        cin>>a[i];
    }
    LinkList LinkList(a,10);
    cout<<"插入位置:";
    cin>>m;
    cout<<"插入元素:";
    cin>>n;
    LinkList.Insert(m,n);
    LinkList.PrintList();
    cout<<"要删除元素位置:";
    cin>>x;
    LinkList.Delete(x);
    LinkList.PrintList();
}

顺序表与单链表的比较

顺序表:数据元素之间的逻辑关系利用存储关系实现;按位查找时间为O(1),是随机存取;插入删除时,需移动表长一半的元素,时间为(n);顺序表的存储空间利用率为100%,存储密度为1,没有浪费空间。

单链表:数据元素之间的逻辑关系利用指针实现;按位查找时间为O(n),是顺序存取;插入删除时,不需要移动元素在给出某个合适位置的指针后所需的时间仅为(1);单链表的存储空间利用率为50%,存储密度<1,有指针的结构性开销,但对元素的个数没有限制。

结论:

使用顺序表:①线性表频繁查找却很少进行插入和删除操作

                      ②其操作和元素在表中的位置密切相关

                      ③如果事先知道线性表的大致长度

使用单链表:①线性表需频繁插入和删除

                      ②线性表中元素个数变化较大或者未知

循环链表

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

 插入

 

s=new Node<T> ;

s->data=x; 

s->next=p->next;      

p->next=s;

删除

q = p->next;
x = q->data;
p->next = q->next;
delete q;

注意:两个循环单链表的合并

算法思想:先找到两个链表的尾,并分别由指针pq指向它们,然后将第一个链表的尾与第二个表的第一个结点链接起来,并修改第二个表的尾q,使它的链域指向第一个表的头结点。

双链表

单链表的每个结点中再设置一个指向其前驱结点的指针域

data: 数据域,存储数据元素

prior:指针域,存储该结点的前趋结点地址

next:  指针域,存储该结点的后继结点地址

插入 

插入

 

 

 

s->prior=p;

s->next=p->next; 

p->next->prior=s;

p->next=s;

删除

(p->prior)->next=p->next;

(p->next)->prior=p->prior;

delete p;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值