数据结构与算法学习(2)——线性表

目录

线性表

何为“线性”:

顺序存储结构——顺序表

顺序表操作实现

顺序表的优缺点

链式存储结构——链表

单链表

表示方法

带头结点的单链表

单链表操作实现

建立带头结点的单链表

顺序(单)链表

双向链表​编辑

顺序表和链表的比较

应用(算法思路)

参考资料:


线性表

L=\left ( \alpha _{1},\alpha_{2},...,\alpha_{n} \right )

        由n个数据特性相同的元素,例如书籍信息(结构体),构成的有限序列。

何为“线性”:

        除第一个和最后一个数据元素外,每个数据元素只有一个前驱和后驱数据元素。


顺序存储结构——顺序表

        通过数组的方式,将线性表的数据元素存储在一块连续地址空间的内存单元中,从而得到顺序表的特点——逻辑上相邻的数据元素,其物理次序也是相邻的。

        Loc\left ( a_{i} \right )=Loc(a_{0})+i\ast l

顺序存储
存储地址bb+l...b+i*l...b+n*j
存储内容a0a1...ai...an

定义结构体如下:

#define MaxSize 100

//顺序表的静态数组实现方法
typedef struct
{
    DataType list[MaxSize];    //数据存储在数组中
    int size;                  //数据元素个数
}SeqList;

//动态分配顺序存储结构(优先级高)
typedef struct
{
    ElemType *elem;            //存储空间基址
    int length;                //当前长度
}SqList;

下面以动态分配方法定义的结构体为基础来实现顺序表的操作。

顺序表操作实现

1.初始化操作

typedef int Status;
 
#define TRUE  1
 
#define FALSE 0
 
#define OK 1
 
#define ERROR 0
 
#define INFEASIBLE  -1
 
#define OVERFLOW  -2

Status InitList(SqList &L)
{//初始化
    L.elem = new ElemType[MaxSize];    //请看注解

    if(!L.elem)    return OVERFLOW;
    L.length = 0;

    return OK;
}

        注:通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。

2.取值操作

Status GetElem (SqList L, int i, ElemType &e)
{
    if ( i<1 || i>L.length )  // i 不在有效位置
        return  ERROR;

    e = L.elem[i-1];

    return OK;
}   // 时间复杂度  O(1)   取决于i

3.查找操作

int LocateElem( SqList L, ElemType e)
{//在顺序表L中查找值为 e 的数据元素,返回其序号
    for( i = 0; i < L.length; i++ )
        if ( L.elem[i] == e ) 
            return i+1;

    return 0;
}//时间复杂度  O(n)

4.插入操作

Status ListInsert (SqList &L, int i, ElemType e)
{
    if(i<1 || i >L.length+1)   
        return ERROR;// 位置超界

    if (L.length == MAXSIZE) 
        return ERROR; // 存储空间已满

    for(j=L.length-1; j>=i-1;j--)
        L.elem[j+1]=L.elem[j];

    L.elem[i-1] = e;
    L.length++;
	 
    return OK;
}//时间复杂度  O(n)

5.删除操作

和插入操作差不多,只需要关注超界的L.length+1->L.length即可,其他反向操作就不多水啦

顺序表的优缺点

顺序表的优点

  • 逻辑相邻,物理相邻
  • 可随机存取任一元素
  • 存储空间使用紧凑

顺序表的缺点

  • 插入、删除操作需要移动大量的元素
  • 预先分配空间需按最大空间分配,利用不充分
  • 表容量难以扩充

链式存储结构——链表

链式存储结构存储线性表数据元素的方式是,把存储有数据元素的结点用指针域构造成链。

单链表

结点:数据域|指针域  
数据域:元素本身信息
指针域:指示直接后继的存储位置

特点:

  • 不需预先分配空间
  • 指针占用额外存储空间
  • 不能随机存取,查找速度慢

表示方法

typedef struct LNode
{ 
    ElemType      data;		// 数据域
    struct LNode *next; 	// 指针域
}LNode,*LinkList;

带头结点的单链表

不带头结点的单链表

空表条件: L == NULL                   表尾条件: P->next == NULL

带头结点的单链表

空表条件: L->next == NULL        表尾条件: P->next == NULL

单链表操作实现

1.初始化操作

Status InitList(LinkList &L)
{
    L = new LNode;    //生成头结点
    L->next = NULL;   //头结点的指针域置空

    return OK;
}

2.取第i个数据元素的操作

Status GetElem (LinkList L, int i, ElemType &e)
{// L指向头结点
    int j=1; 
    LinkList p=L->next;  // p指向首元结点
    while(j<i && P)
    {
        p=p->next; 
		++j;
    }

    if(!p || j>i) 
        return ERROR; 

// i>n 或 i<=0  所指位置超界
    e=p->data;
    return OK;
} // 时间复杂度O(n) 

3.查找操作

LNode  *LocateElem (LinkList L, ElemType e)
{// L指向头结点
    p=L->next;  // p指向首元结点
   	while(p && p->data != e )
    	p=p->next; 

	return p; 
    // 查找成功返回值为e的结点地址,失败p为NULL
} // 时间复杂度O(n) 

4.插入操作

Status ListInsert (LinkList L, int i, ElemType e)
{// L指向头结点
    p=L;j=0;
    while(p && j<i-1) //p是要插入结点的前驱结点
    {
        p=p->next; 
        ++j;
    }
    if (!p||j>i-1) return ERROR;  // i>n+1 or i<1

    s=new LNode;   //(LinkList)malloc(sizeof(LNode));
    s->data=e;    
    s->next=p->next;
    p->next=s;
    return OK;
}//时间复杂度O(n)

5.删除操作

Status ListDelete (LinkList &L, int i)
{
    p=L;  j=0;
    while( p->next && j<i-1 )
    {
    //查找第i-1个结点,p->next是要删除的结点
        p=p->next;  
        ++j;
    }
    if( !p->next || j>i-1) 
    //当 i>n or i<1时 删除位置不合理
	    return ERROR;

    q=p->next;  
    p->next=q->next;
    delete  q;  // 释放删除结点的空间
    return OK;
}

建立带头结点的单链表

void CreateList_H(LinkList &L, int n)//前插法
{ 
    L= new LNode;
    L->next=NULL; // 先建立一个带头节点的空链表
    for(i=0; i<n; i++)
    {
         p=new LNode; // 生成新结点
         cin >> p->data;  // 给数据域赋值
         p->next=L->next;
  	     L->next=p; // 新结点插在头结点后
     }
} //时间复杂度O(n) 

void CreateList_R(LinkList &L, int n)//后插法
{ 
    L= new LNode;
    L->next=NULL; // 先建立一个带头节点的空链表
    r = L;
    for(i=0; i<n; i++)
    {
        p=new LNode; // 生成新结点
        cin >> p->data;  // 给数据域赋值
        p->next=NULL; 
	    r->next=p; // 新结点插在r结点后
	    r=p; // r=r->next;
     }
} //时间复杂度O(n) 

顺序(单)链表

循环链表:
        表尾条件:p->next==H
        有头结点的空表条件:H->next == H

对循环链表,有时不给出头指针,而给出尾指针可以更方便的找到第一个和最后一个结点。

(借用一下老师的ppt)

双向链表

typedef struct Node
{
    DataType data;
    struct Node *next;
    struct Node *prior;
}DLNode;

实现操作的代码省略(主要是p->prior->next之类的运用,虽然我习惯用(--p)->next

顺序表和链表的比较

应用(算法思路)

放在社区学习活动(找不到就是我还没放)

参考资料:

1. 数据结构——使用C语言(第5版)

2. 数据结构——C++实现

3. 数据结构与算法 ppt(线性表)        张合生

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zedkyx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值