第二章 线性表

线性表

一、线性表的定义及特点

定义:数据元素具有属于同一数据对象,相邻数据元素之间存在序偶关系。

特点:

  • 存在唯一的一个被称为“第一个”的数据元素。
  • 存在唯一的一个被称为“最后一个”的数据元素。
  • 除第一个之外,结构中的每个数据元素均只有一个前驱。
  • 除最后一个之外,结构中的每个数据元素均只有一个后继。

二、线性表引入

在此之前,我们了解了数据结构一个基本结构——三元组,但三元组储存的数据是有限的,而线性表是一种灵活的数据结构,其长度可以根据需要增长或缩短,及对线性表的数据元素进行访问(对数据元素的访问包括对数据元素的增添、删除、改变等操作)。

eg:稀疏多项式的运算

一般情况下,一元n次多项式可以写成:

P n ( x ) = p 1 x e 1 + p 2 x e 2 + … + p m x e m P_{n}(x)=p_{1}x^{e_{1}}+p_{2}x^{e_{2}}+…+p_{m}x^{e_{m}} Pn(x)=p1xe1+p2xe2++pmxem

我们可以创建一个链表来存储其系数和指数便于运算。

三、线性表的抽象数据类型定义

ADT{

数据对象:D={ a i a_{i} ai | a i a_{i} ai 属于ElemSet, i = 1, 2, …, n, n>=0}

数据关系:R={< a i − 1 , a i > a_{i-1}, a_{i}> ai1,ai> | a i − 1 , a i a_{i-1}, a_{i} ai1,ai 属于D,i=1, 2, …, n}

基本操作:

InitList(&L)

操作结果:构造一个空的线性表。

CreateList(&L, n)

操作结果:创建一个长度为n的线性表。

DestroyList(&L)

初始条件:线性表L已存在。

操作结果:销毁线性表L。

ClearList(&L)

初始条件:线性表L已存在。

操作结果:将L重置为空表。

ListEmept(L)

初始条件:线性表L已存在。

操作结果:若L为空表,则返回True,否则返回False。

ListLength(L)

初始条件:线性表L已存在。

操作结果:返回L中数据元素个数。

GetElem(L, i, &e)

初始条件:线性表L已存在,且i>=1&&i<ListLength(L)。

操作结果:用e返回L中第i个数据元素的值。

LocateElem(L, e)

初始条件:线性表L已存在。

操作结果:返回L中第i个值与e相同的元素在L中的位置,若该元素不存在,则返回值为0。

ListInsert(&L, i, e)

初始条件:线性表L已存在,且i>=1&&i<ListLength(L)。

操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。

ListDelete(&L, i)

初始条件:线性表L已存在,且i>=1&&i<ListLength(L)。

操作结果:删除L的第i个数据元素,L的长度减1。

}ADT List

四、线性表的两种表示和实现方式

1.线性表的顺序存储表示

  • 线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。
  • 数据元素的存储结构在逻辑上相邻,在物理上也相邻的线性表为顺序表

2.线性表的链式存储表示

  • 线性表的链式存储结构中,存储数据元素的存储单元可以是连续的,也可以是不连续的。
  • 这种存储结构的线性表称为链表

3.顺序表与链表的特性

顺序表的优点:

由于顺序表的存储单元在物理上为连续的,因此只要知道顺序表的第一个数据元素 a 1 a_{1} a1(基址)的存储位置,就可以存取任一数据元素。所以顺序表的存储结构是一种随机存取的存储结构。

顺序表的缺点:

在对顺序表做插入或删除操作时,需要移动大量元素,操作相对复杂,必然导致存储空间的浪费。

链表的优点:

链表对数据元素存取的实现方式是通过指针实现的,因此在对链表进行插入或删除操作时,只需要将指针指向的数据元素进行改变即可。

链表的缺点:

由于链表需要通过不断的指针获取数据,因此获取第i个数据元素时,只能从表头开始依次向后遍历,效率较低。

4.顺序表中基本操作的实现

创建顺序表:
typedef struct{
    int *elem;       //存储空间的基地址
    int length;      //当前长度
}SqList;
void CreateList_Sq(SqList &A,int n){
    //构造顺序表
    A.elem=new int[MAXSIZE];    //为顺序表分配一个大小为MAXSIZE的数组空间
    A.length=n; 
	for(int i=0;i<n;i++)
    {
		printf("请输入第%d个元素:",i+1);
		scanf("%d",&A.elem[i]);
	}           //空表长度为0
}
取值:
int GetElem(SqList A, int e,int i)
{
	if(i<1||i>A.length)
	{
		return ERROR;
	}
	else
	{
		e = A.elem[i-1];
	}
	return 0;
}
插入:
int ListInsert(SqList &A, int e, int i)
{
	if((i<1)||(i>A.length+1)||(A.length==MAXSIZE))
	{
		return ERROR;
	}
	else
	{
		for(int j = A.length-1; j>=i-1; j--)
		{
			A.elem[j+1]=A.elem[j];
		}
		A.elem[i-1]=e;
		++A.length;
		return OK;
	}
}
显示顺序表:
void PrintA(SqList A)
{
	//输出数组元素
	printf("顺序表元素为:");
    for(int i=0;i<A.length-1;i++)
        printf("%d ",A.elem[i]);
    printf("%d",A.elem[A.length-1]);
}
删除:
void DeleteItem(SqList &A,int item){
    //删除顺序表A中所有值为item的元素
    for(int i=0;i<A.length;i++){
        if(A.elem[i]==item){
            for(int j=i;j<A.length;j++)
            A.elem[j] = A.elem[j + 1];
		    A.length--;
            i--;
        }
    }
}
完整代码实现:
#include <stdio.h>

#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100

typedef struct{
    int *elem;
    int length;
}SqList;

void CreateList_Sq(SqList &A,int n){
    A.elem=new int[MAXSIZE];
    A.length=n; 
	for(int i=0;i<n;i++)
    {
		printf("请输入第%d个元素:",i+1);
		scanf("%d",&A.elem[i]);
	}
}

int GetElem(SqList A, int e,int i)
{
	if(i<1||i>A.length)
	{
		return ERROR;
	}
	else
	{
		e = A.elem[i-1];
	}
	return 0;
}

int ListInsert(SqList &A, int e, int i)
{
	if((i<1)||(i>A.length+1)||(A.length==MAXSIZE))
	{
		return ERROR;
	}
	else
	{
		for(int j = A.length-1; j>=i-1; j--)
		{
			A.elem[j+1]=A.elem[j];
		}
		A.elem[i-1]=e;
		++A.length;
		return OK;
	}
}

void DeleteItem(SqList &A,int item){
    for(int i=0;i<A.length;i++){
        if(A.elem[i]==item){
            for(int j=i;j<A.length;j++)
            A.elem[j] = A.elem[j + 1];
		    A.length--;
            i--;
        }
    }
}

void PrintA(SqList A)
{
	printf("顺序表元素为:");
    for(int i=0;i<A.length-1;i++)
        printf("%d ",A.elem[i]);
    printf("%d",A.elem[A.length-1]);
}

int main()
{
	int n,m,i,j,e;
    while(1)
    {
    	printf("请输入你想完成的功能;\n");
		printf("0.退出程序;\n1.创建顺序表;\n2.取值;\n3.插入数据;\n4.删除数据;\n5.显示顺序表;\n");
    	scanf("%d",&m);
        if(m==0) 
		{
			break;
		}
		else
		{
			switch (m) {
				case 1:
					SqList A;
					printf("请输入顺序表的长度:");
					scanf("%d", &n);
					CreateList_Sq(A,n);
					printf("顺序表创建成功!\n");
					break;
				case 2:
					printf("请输入取值的位置:");
					scanf("%d",&i);
					GetElem(A, e, i);
					printf("该位置的数据为:%d\n",e);
					break;
				case 3:
					printf("请输入需要插入的值:");
					scanf("%d",&e);
					printf("请输入插入的位置:");
					scanf("%d",&i);
					ListInsert(A, e, i);
					break;
				case 4:
					int item;
					printf("请输入要删除的值:");
					scanf("%d",&item);
					DeleteItem(A,item);
					printf("链表已删除!");
					break;
				case 5:
					PrintA(A);
					break;
				default:
					printf("没有你想完成的功能\n");
					break;
				}
		}
    }
    return 0;
}

5.链表中基本操作实现

初始化:
int InitList(linkList &L)
{//构造一个空的单链表L
    L = new LNode;			//生成新节点作为头结点,用头指针L指向头结点
    L -> next = NULL;		//头结点的指针域置空
    return OK;
}
创建链表:
//前插法:
int CreateList_H(LinkList &L, int n) {
	int i;
	LinkList p;
    //逆位序输入n个元素的值,建立带表头节点的单链表L
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;			//先建立一个带头节点的空链表
	for (i = 0; i < n; i++) {
		p = (LinkList)malloc(sizeof(LNode));		//生成新的节点*p
		printf("请输入第%d个数据:",i+1);
		scanf("%f",&p->data);					//输入元素值赋给新节点*p的数据域
		p->next = L->next;
		L->next = p;					//将新节点*p插入到头结点后
	}
	return 0;
}

//后插法:
int CreateList_R(LinkList &L, int n) {
	int i;
	LinkList p;
    //正位序输入n个元素的值,建立带表头节点的单链表L
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;			//先建立一个带头节点的空链表
    LinkList r = L;			//尾指针r指向头结点
	for (i = 0; i < n; i++) {
		p = (LinkList)malloc(sizeof(LNode));		//生成新的节点
		printf("请输入第%d个数据:",i+1);
		scanf("%f",&p->data);					//输入元素值赋给新节点*p的数据域
		p->next = NULL;
		r->next = p;			//将新节点*p插入尾结点*r后
		r = p					//r指向新的尾结点*p
	}
	return 0;
}
取值:
Status GetElem(LinkList L, ElemType &e, int i)
{//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
    LinkList p;
	p = L->next;		//初始化,p指向首元结点
    int j = 1;			//计数器j初值为1
    while(p != NULL && j<i)			//顺链域向后扫描,指定p为空或指向第i个元素
    {
        p=p->next;				//p指向下一个节点
        ++j;					//计数器j响应加1
    }
    if(p == NULL || j>i)		//判断i值是否合理
    {
    	return ERROR;
	}
    e=p->data;				//取第i个节点的数据域
    return OK;
}
插入:
Status ListInsert(LinkList &L, ElemType &e, int i)
{//在带头结点的单链表L中第i个位置插入值为e的新节点
	int j = 0;
	LinkList  p, s;
	p = L;
	while ((p != NULL) && (j < i - 1)) {
		p = p->next;			//查找第i-1个节点,p指向该节点
		j++;
	}
	if ((p == NULL) || (j > i - 1))
	{
		return ERROR;
	}
	s = (LinkList)malloc(sizeof(LNode));			//生成新节点*s
	s->data = e;				//将节点*s的数据域置为e
	s->next = p->next;			//将节点*s的指针域指向第i个节点
	p->next = s;			//将节点*p的指针域指向节点*s
	return 0;
}
显示单链表:
Status Print_List(LinkList &L){
	LinkList p;
	p=L->next;
	while(p != NULL)
    {
		printf("%f ",p -> data);
		p = p->next;
	}
	printf("\n");
	return OK;
}
删除单链表:
Status ListDelete(LinkList &L, int i)
{//在带头结点的单链表L中,删除第i个元素
	LinkList p, q;
	p = L;
	int j = 0;
	while ((p->next) || (j < i - 1))		//查找第i-1个节点,p指向该节点
    {
		p=p->next;
		++j;
	}
	if(!(p->next) || (j > i - 1)){
		return ERROR;
	}
	q = p->next;		//临时保存被删节点的地址以备释放
	p->next = q->next;		//改变删除节点前驱节点的指针域
	free(q);			//释放删除节点的空间
	return OK;

}
完整代码实现:
#include <stdio.h>
#include <stdlib.h>

#define OK 1;
#define ERROR 0;
#define OVERFLOW -2;

typedef int Status;
typedef float ElemType;

typedef struct LNode {
	ElemType data;
	struct LNode *next;
} LNode, *LinkList;

int CreateList(LinkList &L, int n) {
	int i;
	LinkList p;
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;
	for (i = 0; i < n; i++) {
		p = (LinkList)malloc(sizeof(LNode));
		printf("请输入第%d个数据:",i+1);
		scanf("%f",&p->data);
		p->next = L->next;
		L->next = p;
	}
	return 0;
}

Status GetElem(LinkList L, ElemType &e, int i)
{
    LinkList p;
	p = L->next;
    int j = 1;
    while(p != NULL && j<i)
    {
        p=p->next;
        ++j;
    }
    if(p == NULL || j>i){
    	return ERROR;
	}
    e=p->data;
    return OK;
}

Status Print_List(LinkList &L){
	LinkList p;
	p=L->next;
	while(p != NULL){
		printf("%f ",p -> data);
		p = p->next;
	}
	printf("\n");
	return OK;
}

Status ListInsert(LinkList &L, ElemType &e, int i) {
	int j = 0;
	LinkList  p, s;
	p = L;
	while ((p != NULL) && (j < i - 1)) {
		p = p->next;
		j++;
	}
	if ((p == NULL) || (j > i - 1))
	{
		return ERROR;
	}
	s = (LinkList)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return 0;
}

Status ListDelete(LinkList &L, int i) {
	LinkList p, q;
	p = L;
	int j = 0;
	while ((p->next) || (j < i - 1)) {
		p=p->next;
		++j;
	}
	if(!(p->next) || (j > i - 1)){
		return ERROR;
	}
	q = p->next;
	p->next = q->next;
	free(q);
	return OK;

}

int main() {
	LinkList L, p;
	int i, j, n;
	float e;
	int m;
	while (1) {
		printf("请输入你想完成的功能;\n");
		printf("0.退出程序;\n1.创建单链表;\n2.取值;\n3.插入数据;\n4.删除数据;\n5.显示单链表;\n");
		scanf("%d", &m);
		if (m == 0) {
			break;
		} else {
			switch (m) {
				case 1:
					printf("请输入结点数:");
					scanf("%d", &n);
					CreateList(L, n);
					printf("单链表创建成功!\n");
					break;
				case 2:
					printf("请输入取值的位置:");
					scanf("%d",&i);
					GetElem(L,e,i);
					printf("该位置的数据为:%f\n",e);
					break;
				case 3:
					printf("请输入需要插入的值:");
					scanf("%f",&e);
					printf("请输入插入的位置:");
					scanf("%d",&i);
					ListInsert(L, e, i);
					break;
				case 4:
					ListDelete(L, i);
					printf("链表已删除!");
					break;
				case 5:
					Print_List(L);
					break;
				default:
					printf("没有你想完成的功能\n");
					break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值