IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构简记--2 -> 正文阅读

[数据结构与算法]数据结构简记--2

第 2?章 线性表

2.1 线性表的定义

? ? ? ? 线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为L=(a1,a2,···,ai,ai+1,···,an),式中a1是唯一的第一个数据元素,又称表头元素;an是唯一的最后一个元素,又称表尾元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后继。

? ? ? ? ?线性表的特点:

  • 表中元素的个数有限
  • 表中元素具有逻辑上的顺序性,表中元素有先后次序
  • 表中元素都是数据元素,每个元素都是单个元素。
  • 表中元素的数据类型都相同,即每个元素占有相同大小的存储空间。
  • 表中元素具有抽象性,即仅讨论元素的逻辑关系,而不考虑元素的内容。

?注意:线性表是一种逻辑结构,表示元素之间一对一的相邻关系,顺序表和链表是指存储结构,两者属于不同层面的概念。

2.2?线性表的基本操作

  • 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所占用的内存空间。

?2.3 顺序表

?2.3.1 顺序表的定义

? ? ? ? 线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。第1个元素存储在线性表的起始位置,第 i 个元素的存储位置后面紧接着存储的是第 i+1 个元素,称 i 为元素ai在线性表中的位序。因此,顺序表的特点就是表中元素的逻辑顺序与其物理顺序相同。

? ? ? ? 假设线性表的元素类型为ElemType,则线性表的顺序存储类型描述为

#define MaxSize 50             //定义线性表的最大长度
typedef struct{
    ElemType data[MaxSize];    //顺序表的元素
    int length;                 //顺序表的当前长度
}SqList;                       //顺序表的类型定义         

?注意:上机运行时需要将ElemType更换为具体的数据类型。

?动态分配时,线性表的顺序存储类型描述为

#define InitSize 100             //表长度的初始定义
typedef struct{
    ElemType *data;            //指示动态分配数组的指针
    int MaxSize,length;        //数组的最大容量和当前个数
}SqList;                       //动态分配数组顺序表

?C的初始动态分配语句为

L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);

??C++的初始动态分配语句为

L.data = new ElemType[InitSize];

?2.3.2 顺序表上的基本操作

初始化操作

//方式一
void initlist(SqList *L)
{
    L->data = (ElemType*)malloc(sizeof(ElemType)*InitSize);
    L->length=0;
    L->MaxSize=InitSize;
}
//方式二
void initlist(SqList L)
{
    L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);
    L.length=0;
    L.MaxSize=InitSize;
}

?注意:使用*L时,用(L->)来取元素,使用L时,用(L.)来取元素。

?求表长操作

int getlen(SqList *L)
{
    return L->length;
}

?取元素操作

int getelemz(SqList *L,int i,ElemType *e)
{
    if(i<1 || i>L->length)
        return 0;
    *e=L->data[i-1];
    return 1;
}

?元素定位操作

int LocateElem(SqList *L,ElemType e)
{
    int i;
    for(i=0;i<L->length;i++)
        if(L->data[i]==e)
            return i+1;
    return 0;
}

?插入操作

int ListInsert(SqList *L,int i,ElemType e)
{
    int j;
    if(i<1 || i>L->length+1)    //判断i的范围是否有效
        return 0;
    if(L->length==L->MaxSize)   //存储空间不够,增加一个存储空间
    {
        //重置存储空间长度
        L->data = (ElemType*)realloc(L->data,(L->length+1) * sizeof(ElemType));
        L->MaxSize++;
    }
    for(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 1;
}

?删除操作

int ListDelete(SqList *L,int i,ElemType *e)
{
	int j;
    if(i<1 || i>L->length)        //判断i的范围是否有效
        return 0;
    *e=L->data[i-1];              //将删除的元素赋值给e
    for(j=i;j<L->length;j++)      //将第i个位置后的元素前移
        L->data[j-1]=L->data[j]; 
    L->length--;                  //顺序表长度减1
    return 1;
}

输出操作

void list(SqList *L)
{
	int i;
	for(i=0;i<L->length;i++)
		printf("%5d",L->data[i]);
	printf("\n");
}

在主函数中调用以上函数

    SqList L;
	int E;
	//初始化一个空的顺序表 
    initlist(&L);
    //调用 ListInsert(SqList *L,int i,ElemType e)向空表中加入数据元素 
    ListInsert(&L,1,1);
    ListInsert(&L,2,2);
    ListInsert(&L,3,3);
    ListInsert(&L,4,4);
    //显示当前顺序表中的所有数据元素 
    printf("当前顺序表的元素\n");
    list(&L);
    //获取当前的顺序表的长度 
    printf("调用getlen(&L)的结果:%d\n",getlen(&L));
    //取顺序表中的第1个元素并输出该元素的值 
    printf("调用getelem(&L,1,&E)的结果:%d\n",getelem(&L,1,&E));
    printf("E=%d\n",E);
    //输出指定元素第一次出现在顺序表中的位置 
    printf("调用LocateElem(&L,2)的结果:%d\n",LocateElem(&L,2));
    //调用 ListInsert(SqList *L,int i,ElemType e)在顺序表指定位置中加入数据元素
    printf("调用ListInsert(&L,2,3)的结果:%d\n",ListInsert(&L,2,3));
    //显示当前顺序表中的所有数据元素 
    printf("当前顺序表的元素\n");
    list(&L);
    //删除顺序表中指定位置的元素并将输出该位置的数据元素 
    printf("调用ListDelete(&L,2,&E)的结果:%d\n",ListDelete(&L,2,&E));
    printf("E=%d\n",E);
    //显示当前顺序表中的所有数据元素 
    printf("当前顺序表的元素\n");
    list(&L);

?2.4 链表

?2.4.1 单链表

? ? ? ? ?线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对每个链表节点,除存放元素本身的信息外,还需要存放一个指向其后续的指针。

? ? ? ? 单链表中结点类型的描述如下:

typedef struct LNode{       //定义单链表结点类型
    ElemType data;          //数据域
    struct LNode *next;     //指针域
}slink;

?2.4.2 单链表上的基本操作

?头插法建立单链表-方法一

void List_HeadInsert(slink *L)
{
    slink *s;
    int n;
    L->next=NULL;                       //初始为空表
    scanf("%d",&n);                     //输入结点值
    while(n!=9999)                     //输入9999表示结束
    {
      s=(slink *)malloc(sizeof(slink));//创建新结点
      s->data=n;
      s->next=L->next;
      L->next=s;
      scanf("%d",&n);
    }
}
//调用该函数创建链表需在主函数中先初始化指针L,如下
int main()
{
    slink *L;
    L=(slink *)malloc(sizeof(slink));   //创建头结点
    List_HeadInsert(L);
    return 0;
}

?头插法建立单链表-方法二?

slink * List_HeadInsert()
{
    slink *s,*L;
    int n;
    L=(slink *)malloc(sizeof(slink));   //创建头结点
    L->next=NULL;                       //初始为空表
    scanf("%d",&n);                     //输入结点值
    while(n!=9999)                     //输入9999表示结束
    {
      s=(slink *)malloc(sizeof(slink));//创建新结点
      s->data=n;
      s->next=L->next;
      L->next=s;
      scanf("%d",&n);
    }
    return L;
}
//在主函数中使用如下方式调用
int main()
{
    slink *L;
    L=List_HeadInsert();
    return 0;
}

?尾插法建立单链表

slink * List_TailInsert(slink * L)
{
    slink *s,*r;						//r为表尾指针 
    int n;
    L=(slink *)malloc(sizeof(slink));   //创建头结点
    r=L;
    scanf("%d",&n);                     //输入结点值
    while(n!=9999)                     //输入9999表示结束
    {
      s=(slink *)malloc(sizeof(slink));//创建新结点
      s->data=n;
      r->next=s;
      r=s;
      scanf("%d",&n);
    }
    r->next=NULL;
    return L;
}
//在主函数中调用该函数的方法如下
int main()
{
    slink *L;
    L=List_TailInsert(L);
    return 0;
}

?建立单链表

slink * CresList(int n)
{
    slink *L,*p,*s;		//p用于指向新链入结点,s用于指向新开辟结点 
    int i;
    p=L=(slink *)malloc(sizeof(slink));   //创建头结点
    for(i=1;i<=n;i++)
    {
      s=(slink *)malloc(sizeof(slink));//创建新结点
      scanf("%d",&s->data); 			//输入结点值
      p->next=s;		//将新结点连接到p所指结点的后面 
      p=s;				//p指向新链入结点 
    }
    p->next=NULL;		//尾结点指针域置为空 
    return L;
}
//在主函数中调用该函数的方法如下
int main()
{
    slink *L;
    L=CresList(5);
    return 0;
}

?求表长操作

int getlen(slink *L)
{
	slink *p;
	int n;
	p=L->next;
	n=0;
	while(p!=NULL)
	{
		n++;
		p=p->next;
	}
	return n;
}

?取元素操作

int getlem(slink *L,int i,ElemType *e)
{
	slink *p;
	int j;
	if(i<1)	return 0;	//参数i不合理,取元素失败,返回0 
	p=L->next;
	j=1;
	while(p!=NULL&&j<i)	//从第1个结点开始查找第i个结点 
	{
		p=p->next;
		j++;	
	}
	if(p==NULL) return 0; //i值超过链表长度,元素失败,返回0 
	*e=p->data;
	return 1;			//取元素成功,返回1
}

?按值查找结点

slink * GetElem(slink *L,ElemType e)
{
	slink *p=L->next;
	while(p!=NULL&&p->data!=e)
		p=p->next;
	return p;
}

插入操作

int insert(slink *L,int i,ElemType e)
{
	slink *p,*q;
	int j;
	if(i<1)	return 0;	//参数i不合理,取元素失败,返回0 
	p=L;
	j=0;
	while(p!=NULL&&j<i-1)	//从第1个结点开始查找第i个结点 
	{
		p=p->next;
		j++;	
	}
	if(p==NULL) return 0; //i值超过链表长度,元素失败,返回0 
	q=(slink *)malloc(sizeof(slink));
	q->data=e;
	q->next=p->next;
	p->next=q; 
	return 1;
}

?删除操作

int delete(slink *L,int i,ElemType *e)
{
	slink *p,*q;
	int j;
	if(i<1)	return 0;	//参数i不合理,取元素失败,返回0 
	p=L;
	j=0;
	while(p!=NULL&&j<i-1)	//从第1个结点开始查找第i个结点 
	{
		p=p->next;
		j++;	
	}
	if(p==NULL) return 0; //i值超过链表长度,元素失败,返回0 
	q=p->next;           //指向第i个结点
	p->next=q->next;     //指向被删结点的下一个结点
	*e=q->data;         //保存被删结点的数据值
	free(q);            //释放被删除结点占用的空间
	return 1;
}

?输出操作

void list(slink *L)
{
    slink *p;
    p=L->next;
    while(p!=NULL)
    {
    	
        printf("%4d",p->data);
        p=p->next;
    }
    printf("\n");
}

?2.4.3 双链表

? ? ? ? ?为了能快速方便的找到任意一个结点的前驱和后继,在结点中再增设一个指针域,指向该结点的前驱结点,这样形成的链表就有两条不同方向的链,称为双向链表,简称双链表。

? ? ? ? 双链表的结点类型定义如下:

typedef struct DNode{
    ElemType data;
    struct DNode *prior;
    struct DNode *next;
}dlink;

?2.4.4 双链表上的基本操作

建立双链表

dlink * credlink(int n)
{
    dlink *L,*p,*s; //p用于指向新链入结点,s用于指向新开辟结点
    int i;
    p=L=(dlink *)malloc(sizeof(dlink)); //创建头结点
    for(i=1;i<=n;i++)
    {
        s=(dlink *)malloc(sizeof(dlink)); //s指向新开辟结点
        scanf("%d",&s->data);         //新结点数据域赋值
        s->prior=p;
        p->next=s;            //将新结点连接到p指向结点的后面
        p=s;                  //p指向新链入结点
    }
    p->next=L->prior=NULL;    //头结点的前驱域和尾结点的后继域置为空
    return L;                 //返回头指针
}

?求表长、取元素、定位操作与单链表一样,只需要将slink替换为的dlink即可。

插入操作

int insert(dlink *L,int i,ElemType e)
{
	dlink *p,*q;
	int j;
	if(i<1)	return 0;	//参数i不合理,取元素失败,返回0
	p=L;
	j=0;
	while(p!=NULL&&j<i-1)	//从第1个结点开始查找第i个结点
	{
		p=p->next;
		j++;
	}
	if(p==NULL) return 0; //i值超过链表长度,元素失败,返回0
	q=(dlink *)malloc(sizeof(dlink));
	q->data=e;
	q->next=p->next;
	q->prior=p;
	if(p->next!=NULL)
    p->next->prior=q; // 若恰好在表尾插入,因为没有后继结点,则不需要这一步
	p->next=q;
	return 1;
}

?删除操作

int delete(dlink *L,int i,ElemType *e)
{
	dlink *p,*q;
	int j;
	if(i<1)	return 0;	//参数i不合理,取元素失败,返回0
	p=L;
	j=0;
	while(p!=NULL&&j<i-1)	//从第1个结点开始查找第i个结点
	{
		p=p->next;
		j++;
	}
	if(p==NULL) return 0; //i值超过链表长度,元素失败,返回0
	q=p->next;           //指向第i个结点
	p->next=q->next;    //指向被删结点的下一个结点
	if(p->next!=NULL)
	q->next->prior=p;
	*e=q->data;         //保存被删结点的数据值
	free(q);            //释放被删除结点占用的空间
	return 1;
}

?输出操作

void list(dlink *L)
{
    dlink *p;
    p=L;
    while(p->next!=NULL)         //正向输出链表元素值
    {
        p=p->next;
        printf("%4d",p->data);
    }
    printf("\n");
    while(p!=L)                //反向输出链表元素值
    {
        printf("%4d",p->data);
        p=p->prior;
    }
    printf("\n");
}

?2.4.5 循环链表

?循环单链表

? ? ? ? 循环单链表与单链表的区别在于表中最后一个结点的指针不是NULL,而是指向头结点。循环单链表的判空条件不是头结点的指针为空,而是它是否等于头指针。

? ? ? ? 循环单链表的结点类型定义和单链表是一样的,此外,在循环单链表上的基本操作和单链表是基本一致,只需要将单链表中的判定条件p->next=!NULL、p->next=NULL分别改为p->next=!L、p->next=L即可。

循环双链表

? ? ? ? 将双链表的最后一个结点的后继指针域的值由空改为指向头结点,头结点中的前驱指针域的值由空改为指向尾结点,这样的双向链表称为双向循环链表。

????????循环双链表的结点类型定义和双链表是一样的,此外,在循环双链表上的基本操作和双链表是基本一致,只需要将双链表中的判定条件p->next=!NULL、p->next=NULL、p->prior=NULL分别改为p->next=!L、p->next=L、L->prior=p即可。

2.4.6 静态链表

?????????用数组描述的链表,即称为静态链表。静态链表的表现形式即为结构体数组,结构体变量包括数据域data和游标 CUR。这种存储结构,仍需要预先分配一个较大的空间,但在作为线性表的插入和删除操作时不需移动元素,仅需修改指针,故仍具有链式存储结构的主要优点。

? ? ? ? 注意:静态链表在数组存储数据是从数组的第3个位置开始的,即数组下标为2存储的是链表的第一个元素的值。

? ? ? ? 静态链表结构类型的描述如下:

typedef int ElemType;  //在实际中,根据需要定义所需的数据类型
#define MaxSize 50     //静态链表中的最大长度
typedef struct{
    ElemType data;     //存储数据元素
    int next;          //下一个元素的数组下标
}stalink[MaxSize];

?初始化操作

void initlist(stalink space)
{
    int i;
    for(i=0;i<MaxSize-1;i++)
        space[i].next=i+1;
    space[MaxSize-1].next=0;
}

?获取结点操作

int allocnode(stalink space)
{
    int i;
    i=space[0].next;
    if(i==0) return 0; //链表空间已空,分配空间失败
    space[0].next=space[i].next;
    return i;
}

?回收结点操作

void freenode(stalink space,int i)
{
    space[i].next=space[0].next;
    space[0].next=i;
}

?建立静态链表

int crestalink(stalink space,int n)
{
    int L,k,s,i;
    k=L=allocnode(space);
    for(i=1;i<=n;i++)
    {
        s=allocnode(space);
        scanf("%d",&space[s].data);
        space[k].next=s;
        k=s;
    }
    space[k].next=0;
    return L;
}

?求表长操作

int getlen(stalink space,int head) //head为链表头结点的下标
{
    int i,k;
    k=space[head].next;
    i=0;
    while(k!=0)
    {
        i++;
        k=space[k].next;
    }
    return i;
}

?取元素操作

int getlem(stalink space,int head,int i,ElemType *e)
{
    int j,k;
    if(i<1) return 0;
    j=0;
    k=head;
    while(k!=0&&j<i)
    {
        j++;
        k=space[k].next;
    }
    if(k==0) return 0;
    *e=space[k].data;
    return 1;
}

?定位操作

int locate(stalink space,int head,ElemType e)
{
    int k;
    k=space[head].next;
    while(k!=0&&space[k].data!=e)
        k=space[k].next;
    return k; //k表示的是在数组的下标,若想返回的是在链表中的第几个则用k-1;
}

?插入操作

int insert(stalink space,int head,int i,ElemType e)
{
    int j,k,m;
    if(i<1) return 0;
    k=head;
    j=0;
    while(k!=0&&j<i-1)
    {
        j++;
        k=space[k].next;
    }
    if(k==0)
        return 0;
    m=allocnode(space);
    if(m!=0)
    {
        space[m].data=e;
        space[m].next=space[k].next;
        space[k].next=m;
        return 1;
    }
    else return 0;
}

?删除操作

int delete(stalink space,int head,int i,ElemType *e)
{
    int j,k,m;
    if(i<1) return 0;
    k=head;
    j=0;
    while(k!=0&&j<i-1)
    {
        j++;
        k=space[k].next;
    }
    if(k==0) return 0;
    m=space[k].next;
    space[k].next=space[m].next;
    *e=space[m].data;
    freenode(space,m);
    return 1;
}

?输出操作

void list(stalink space,int head)
{
    int i;
    i=space[head].next;
    while(i!=0)
    {
        printf("%4d",space[i].data);
        i=space[i].next;
    }
    printf("\n");
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-25 23:20:35  更:2022-09-25 23:22:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/25 4:13:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码