数据结构第二章--线性表

博文为考研专业课408–数据结构学习笔记记录!如有错误敬请海涵!!!

线性表的定义和基本操作

线性表的定义

线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为:
L=(a1,a2,a3…ai,…an)
注意
相同的数据类型的意思是每个数据元素所占空间一样大,并且数据元素的位序是从1开始,而数组从0开始。

线性表的基本操作

InitList(&L):初始化表。构造一个空的线性表。
Length(L): 求表长。返回线性表L的长度,即L中数据元素的个数。
LocateElem(L,e):按值查找操作。在表L查找具有给定关键字值的元素。
GetElem(L,i): 按位查找操作。获取表L中的第i个位置的元素的值。
ListInsert(&L,i,e):插入操作。在表 L中第i个位置插入指定元素e。
ListDelete(&L,i,&e):删除操作。删除表 L中第i个位置的元素,并用e返回删除元素的值。
PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。
Empty(L):判空操作。若L为空表,则返回true,否则返回false。
DestroyList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。
注意
什么时候要传入参数的引用“&”?
当对参数的修改结果需要带回来的时候就需要用&。请看下面的俩个例子:

#include<stdio.h>
void test(int x){
   x=1024;
   printf("test函数内部x=%d\n",x);
}

int main(){
   int x=1;
   printf("调用test前x=%d\n",x);
   test(x);
   printf("调用test后x=%d\n",x);
}

其函数的输出结果为:
调用test前x=1
test函数内部x=1024
调用test后x=1

这里为什么X没有改变呢?我们可以理解为test函数里面的X其实是main函数里X的复制品,俩个不是同一个东西,并未将操作过后的值带回去所以该操作并未改变原main函数里面X 的值!
在这里插入图片描述
那如果我们将test函数里面的X前加上&,结果又会是什么呢?

#include<stdio.h>
void test(int  &x){
   x=1024;
   printf("test函数内部x=%d\n",x);
}

int main(){
   int x=1;
   printf("调用test前x=%d\n",x);
   test(x);
   printf("调用test后x=%d\n",x);
}

此时的输出结果为:
调用test前x=1
test函数内部x=1024
调用test后x=1024

这时结果已经发生了变化,原因是&代表的是对操作过后的结果带回去,意思就是这里的X 不是谁的复制品,它改变的就是main函数里X的值。
如果你还没有明白“&”带回来的意思,就去C++在线工具敲一敲上面的代码,仔细体会一下&的含义吧!!

线性表的顺序表示

顺序表的定义

顺序表就是具有顺序存储结构的线性表。顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。

顺序表的实现–静态分配

 # include<stdio.h>
 #define MaxSize 10  //定义最大长度
 typedef struct {
 int  data [MaxSize];  //用静态的“数组”存放数据元素
 int length;               //顺序表的当前长度
}SqList;                //顺序表的类型定义(静态分配方式)

//基本操作--初始化一个顺序表
void InitList(SqList &L){
for(int i=0;i<MaxSize;i++)
	L.dada[i]=0;//将所有数据元素设置为默认初始值
L.ength=0;    //顺序表初识长度为0
}
int main(){
 SqList L; //声明一个顺序表
 InitList(L);  //初始化顺序表
 //.......后续操作
 return 0;
}

在静态分配时,由于数组的大小和空间事先已经固定好,一旦空间占满,再加入心得数据将会产生溢出,进而导致程序崩溃。
而在动态分配时,存储数组的空间时在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性地划分所有空间。

顺序表的实现–动态分配

#include <stdio.h>
# define InitSize 10 //默认的最大长度
typedef struct {
int *data;   //指示动态分配数组的指针
int MaxSize;  //顺序表的最大容量
int length; //顺序表的当前长度
}SeqList;

int main(){
SeqList L;   //声明一个顺序表
InitList(L); //初始化顺序表
//.......其他操作函数
IncreaseSize(L,5);
return 0;
}
void InitList(SeqList &L){
//用malloc 函数申请一片连续的存储空间
L.data=(int *)malloc(InitSize*sizeof(int));
L.length=0;
L.MaxSize=InitSize;
}

//增加动态数组的长度
void IncreaseSize(SeqList &L,int len){
int *p=L.data;
L.data=(int *)malloc((L.MaxSize+len)*sizeof(int));
for(int i=0;i<L.length;i++){
L.data[i]=p[i];   //将数据复制到新区域
}
L.MaxSize=L.MaxSize+len; //顺序表最大长度增加len
free(p);  //释放原来的内存空间
}

在这里插入图片描述
malloc函数动态申请一片连续的存储空间
free函数动态的释放内存的空间
C的初识动态分配语句为:
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
其中(ElemType *)是因为malloc函数返回一个指针,需要强制转换型为你定义数据元素的类型指针
sizeof(ElemType)*InitSize意思是:数据元素所占存储内存空间大小 * 顺序表的初始长度=存放的存储空间大小
注意
1、malloc、free函数的头文件需要引用# include<stdlib.h>
2、动态分配并不是链式存储,它同样属于顺序存储结构。

顺序表的特点

(1)随机访问,通过首地址和元素号即可在世家O(1)内找到指定的元素,
(2)存储密度高,每个结点只存储数据元素
(3)扩展容量不方便(扩展长度的时间复杂度也比较高)
(4)插入、删除操作不方便,需要移动大量元素

顺序表的基本操作—插入

用存储位置的相邻来体现数据元素之间得到逻辑关系
ListInsert(&L,i,e): 插入操作。在表中的第i个位置插入指定元素e。

在这里插入图片描述
在这里插入图片描述

# define MaxSize 10   //定义最大长度
typedef struct{
   int data[MaxSize]; //用静态的“数组”存放数据元素
   int length;        //顺序表的当前长度
}SqList;            //顺序表的类型定义

bool ListInsert(SqList &L,int i,int e){
   if(i<1||i>L.length+1)   //判断i的范围是否有效
   		return false;
   if(L.length>=MaxSize)   //当前存储空间已满,不能插入
   		return false;
   for(int j=L.length;j>=i;j--)  //将第i个元素及之后的元素后移
   		L.data[j]=L.data[j-1];
   L.data[i-1]=e;            //在位置i处放入e
   L.length++;               //长度加1
   return ture;
}
int main(){
    SqList L;    //声明一个顺序表
    InitList(L);    //初始化顺序表
    //     顺序表的其他函数的其他操作
    ListInsert(L,3,3);
}

插入操作的 时间复杂度

在这里插入图片描述
最好情况:
新元素插入到表尾,不需要移动元素。
i=n+1,循环0次;最好时间复杂度=O(1)
最坏情况:
新元素插入到表头,需要将原有的n个元素全部向后移动
i=1,循环n次;最坏时间复杂度=O(n)
平均情况:
假设新元素插入到任何一个位置的概率相同,即i=1,2,3,…,length+1的概率都是p=1/(n+1)
i=1,循环n次;i=2时,循环n-1次;i=3,循环n-2次…i=n+1时,循环0次
平均循环次数=np+(n-1)p+(n-2)p+…+1p=n/2
平均时间复杂度=O(n)

顺序表的基本操作–删除

删除上面顺序表中的第三个元素e,并输出删除的元素,这里需要用到“&”,体会前面的“带回来

# define MaxSize 10   //定义最大长度
typedef struct{
   int data[MaxSize]; //用静态的“数组”存放数据元素
   int length;        //顺序表的当前长度
}SqList;            //顺序表的类型定义

bool ListDelete(SqList &L,int i,int &e){
   if(i<1||i>L.length+1)   //判断i的范围是否有效
   		return false;
   	e=L.data[i-1];   //将删除的元素赋值给e
   for(int j=i;j<L.length;j++)  //将第i个元素及之后的元素前移
   		L.data[j-1]=L.data[j];
   L.length--;               //线性表长度-1
   return ture;
}
int main(){
    SqList L;    //声明一个顺序表
    InitList(L);    //初始化顺序表
    //     顺序表的其他函数的其他操作
    int e=-1;//用变量e把删除的元素“带回来”
    if(ListDelete(L,3,e))
    	printf("已删除第3个元素,删除元素值为=%d\n",e);
    else
    	printf("位序i不合法,删除失败\n");
    reuturn 0;
}

在这里插入图片描述

删除操作时间复杂度

在这里插入图片描述
最好情况:
删除表尾元素,不需要移动其他元素。
i=n,循环0次;最好时间复杂度=O(1)
最坏情况:
删除表头元素,需要将后续的n-1个元素全部向前移动
i=1,循环n-1次;最坏时间复杂度=O(n)
平均情况:
假设删除任何一个元素的概率相同,即i=1,2,3,…,length+1的概率都是p=1/n
i=1,循环n-1次;i=2时,循环n-2次;i=3,循环n-3次…i=n时,循环0次
平均循环次数=(n-1)p+(n-2)p+(n-3)p+…+1p=(n-1)/2
平均时间复杂度=O(n)

在这里插入图片描述
双链表的遍历

//后向遍历
while(p!=null){
    //对结点p做相应处理
    p=p->next;
}
//前向遍历
while(p->prior!=null){
    p=p->prior;
}

循环双链表

typedef struct LNode{
    elemtyoe data;
    struct lnode *next;
}lnode,*linklist;
//初始化一个循环单链表
bool initlist (linklist ,&l){
    l=(lnode *malloc (sizeof(lnode)));//分配一个头结点
    if(l==null)
    	return false;
    	l->next=l;
}return true;
  • 74
    点赞
  • 251
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱吃香菜的斌斌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值