数据结构与算法笔记第2章 线性表 顺序表示

2.1 线性表的定义和特点
  1. 定义:线性表是具有相同特性的数据元素的一个有限序列
  2. ai-1,ai,ai+1 中ai-1为直接前趋ai+1为直接后继;
  3. 线性表是典型的线性结构;
  4. 线性表的优点:随机存取 ;
2.2 案例引入
  1. 一元多项式表示,记作 Pn(x)=p0+p1x+p2x2+…+pnxn,其中设定指数为 i 系数为 pi例如:P(x)=10+5x-4x2+3x3+2x4 表示为
  2. 一元多项式的运算:实现两个多项式加,减,乘运算;运算原理是分别从头遍历比较存放一元多项式的两个数组a和b的每一项,指数相同则相加存放在新数组中,指数不同则将较小的复制在新数组中。使用带头结点的链式存储实现;
2.3 线性表的类型定义

基本操作:

  • InitList(&L) 构造一个空的线性表;
  • DettroyList(&L) 销毁线性表;
  • CLearList(&L) 线性表的重置;
  • ListEmpty(L) 判断线性表是否为空,若为空则返回TURE,否则返回FALSE;
  • ListLength(L) 返回线性表L中的数据元素个数;
  • GetElem(L,i,&e) 用e返回线性表L中的第 i个数据元素的值;
  • LocateElem(L,e,compare()) 返回L中第1个与e满足compare()的数据元素的位序,若为空则返回为0;
  • PriorElem(L,cur_e,&pre_e) 若cur_e是L中的数据元素而且不是第一个元素,则用pre_e返回它的前趋,否则无效;
  • NextElemr_e,&next_e) 若cur_e是L中的数据元素而且不是最后一个元素,则用next_e返回它的后继,否则无效;
  • ListInsert(&L,i,e) 在L的第i个位置之前插入新的数据元素e,L的长度加一;
  • ListDelete(&L,i,&e) 删除L的第i个数据元素,并且用e返回其值,L的长度减一;
  • ListTraverse(&L,visited()) 依次对线性表中的每个元素调用visited();
2.4 线性表的顺序表示
静态数组和动态数组的区别
  • 静态数组在内存中位于栈区,是在定义时就已经在栈上分配了固定大小,在运行时这个大小不能改变,如:int a[10],在函数执行完以后,系统自动销毁;

  • 动态数组是malloc或者new出来的,位于内存的堆区,它的大小是在运行时给定,并且可以改变其,如:
    int *a;
    a = new int[10];
    动态数组,其创建麻烦,使用完必须由程序员自己通过free或者delete释放,否则严重会引起内存泄露;

  • new[]/delete[],malloc/free new/delete 区别:他们都是对堆区的操作,程序员申请内存,用完要记得释放,否则会内存泄漏,多次申请释放,会造成堆区碎片;
    new/delete是操作符,malloc/free是标准库函数,前者可以重载,可以有构造和析构函数,可以返回某种类型对象的指针,后者返回void指针。

线性表的顺序表示
  1. 顺序表中元素存储位置的计算:LOC(ai) = LOC(a1) + (i-1) * l,其中l是每个元素所占的存储单元;
  2. 顺序表特点:地址连续,依次存放,随机存取,类型相同;
  3. 顺序表示定义模板:
#define MAXSIZE 100//顺序表所能达到的最大长度
typedef struct
{
	/*数据类型定义*/
	float real;
	double imag;
}ElemType;
typedef struct
{
	ElemType *elem;//数组动态分配
	int Length;//当前长度
}SqList;//顺序存储结构类型为SqList
补充 类C语言规定

顺序表类型定义:

[1]
typedef struct
{
	ElemType data[MAXSIZE];
	int length;
}SqList;//顺序表类型

[2]
typedef struct
{
	ElemType *elem;
	int length;
}SqList;//顺序表类型

1.ElemType 所表示的是所定义的数据类型,可以为char,int,…也可以自己定义复杂类型。
简单数据类型定义例如:

typedef char ElemType;//定义ElemType为字符型数据类型

复杂数据类型定义例如:

typedef struct
{
	int a;
	char b;
	double c;
}ElemType;

2.数组定义方式的不同
数组静态分配:

typedef struct
{
	ElemType data[MAXSIZE];
	int length;
}SqList;

数组动态分配:

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

例如:数组动态分配完整示例

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
typedef struct
{
	int num;
	char name[20];
}ElemType;
typedef struct
{
	ElemType* elem;//数组基地址
	int length;
}SqList;
int main()
{
	SqList L;
	L.elem = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);//分配空间
	/*L.elem=new ElemType[MAXSIZE]*/
	……
	return 0;
}
补充 C++中的参数传递

函数调用时传递给形参表的实参必须与形参 三个一致:类型,个数,顺序;
参数传递的两种方式:传值方式,传地址;
例:两个数的交换程序
传值方式:

//输出:3 5
#include<stdio.h>
void swap(int a, int b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a=3int b=5;
	swap(a, b);
	printf("%d %d\n",a,b);
	return 0;
}

由此可见,传值方式并没有改变a,b的值;
传地址:

//输出:5 3
#include<stdio.h>
void swap(int *a, int *b)
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
}
int main()
{
	int a = 3;
	int b = 5;
	int* p1, * p2;
	p1 = &a;
	p2 = &b;
	swap(p1, p2);
	printf("%d %d\n",a,b);
	return 0;
}

传地址:参数为指针变量,参数为引用类型,参数数组名;
数组名作参数
例:输出数组的最大值

//输出:5
#include<stdio.h>
#define N 5
int max(int b[])
{
	int i;
	int m;
	m = b[0];
	for (i = 1; i < N; i++)
	{
		if (b[i] > m)
		{
			m = b[i];
		}
	}
	return m;
}
int main()
{
	int a[N] = { 1,2,3,4,5 };
	int n;
	n = max(a);
	printf("%d\n",n);
	return 0;
}

引用类型作参数
例:两个数的交换程序

//输出:5 3
#include<stdio.h>
void swap(int &a, int &b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 3;
	int b = 5;
	swap(a, b);
	printf("%d %d\n", a, b);
	return 0;
}
2.4 线性表的顺序表实现

[ 算法2.4.1 ] 线性表L的初始化

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2
int n;//定义全局变量 n
typedef int Status;
typedef char ElemType;
typedef struct
{
	ElemType* elem;
	int length;
}SqList;
Status InitList(SqList& L)
{
	L.elem = new ElemType[n];//这个 n 是可变的 
	/*L.elem = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);*/
	if (!L.elem)
	{
		exit(OVERFLOW);
	}
	L.length = 0;
	return OK;
}
int main()
{
	cin >> n;
	SqList L;
	InitList(L);
	……
	return 0;
}

[ 算法2.4.2 ] 销毁线性表

void DestroyList(SqList& L)
{
	if (L.elem)
	{
		free(L.elem);//释放存储空间
		//delete L.elem;
	}
}

[ 算法2.4.3 ] 清空线性表

void ClearList(SqList& L)
{
	L.length = 0;//将线性表的长度置为 0
}

[ 算法2.4.4 ] 求线性表的长度

int GetLength(SqList L)
{
	return (L.length);
}

[ 算法2.4.5 ] 判断线性表是否为空

int IsEmpty(SqList L)
{
	if (L.length == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

[ 算法2.4.6 ] 顺序表的取值

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

[ 算法2.4.7 ] 顺序表的查找

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

[ 算法2.4.8 ] 顺序表的插入

情况分析: 插入位置在最后,插入位置在中间,插入位置在最前面;
算法思想: 判断插入位置是否合法,判断是否溢出;

Status ListInsert(SqList& L, int i, ElemType e)
{
	int j;
	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;
}

[ 算法2.4.9 ] 顺序表的删除

情况分析: 删除位置在最后,删除位置在中间,删除位置在最前面;
算法思想: 判断删除位置是否合法;

Status ListDelete(SqList& L, int i)
{
	int j;
	if (i<1 || i>L.length)
	{
		return ERROR;
	}
	for (j = i - 1; j < L.length; j++)
	{
		L.elem[j] = L.elem[j + 1];
	}
	L.length--;
	return OK;
}
顺序表的优缺点

优点:

  • 存储密度大;
  • 随机存取;

缺点:

  • 时间效率低;
  • 浪费存储空间;
  • 静态存储形式,数据元素的个数不能自由扩充;
线性表的顺序表示和实现完整参考代码
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int ElemType;
typedef int Status;
typedef struct
{
	/*
	数组动态的时候使用
	ElemType *elem;
	*/
	ElemType elem[MAXSIZE];//静态数组
	int length;
}SqList;
Status InitList(SqList& L)//线性表L的初始化
{
	/*
	数组动态的时候使用
	L.elem = (ElemType*)malloc(sizeof(ElemType) (n + 1));
	//L.elem = new ElemType[n + 1];//n + 1 的原因是因为最后 free 的时候不会认为溢出判错
	if (!L.elem)
	{
		exit(OVERFLOW);
	}
	*/
	L.length = 0;
	return OK;
}
void print(SqList L)//输出线性表
{
	int i;
	for (i = 0; i < L.length; i++)
	{
		printf("%d ",L.elem[i]);
	}
	printf("\n");
}
int GetLength(SqList L)//获取线性表的长度
{
	return L.length;
}
int IsEmpty(SqList L)//判断线性表是否为空
{
	if (L.length == 0)
	{
		return 1;//线性表为空
	}
	else
	{
		return 0;
	}
}
Status ClearList(SqList &L)//清空线性表
{
	L.length = 0;
	return OK;
}
Status GetElem(SqList L, int i, ElemType& e)//获取线性表元素
{
	if (i<1 || i>L.length)
	{
		printf("输入的取值位置错误!\n");
		return ERROR;
	}
	e = L.elem[i - 1];
	return OK;
}
int LocateElem(SqList L, ElemType e)//查找线性表元素
{
	int i;
	for (i = 0; i < L.length; i++)
	{
		if (L.elem[i] == e)
		{
			return i + 1;
		}
	}
	return 0;
}
Status ListInsert(SqList& L, int i, ElemType e)//线性表元素插入
{
	if (i<1 || i>L.length)
	{
		return ERROR;
	}
	if (L.length == MAXSIZE)
	{
		return ERROR;
	}
	for (int j = L.length - 1; j >= i - 1; j--)
	{
		L.elem[j + 1] = L.elem[j];
	}
	L.elem[i - 1] = e;
	L.length++;
	return OK;
}
Status ListDelete(SqList& L, int i)//线性表元素删除
{
	if (i<1 || i>L.length)
	{
		return ERROR;
	}
	for (int j = i - 1; j < L.length; j++)
	{
		L.elem[j] = L.elem[j + 1];
	}
	L.length--;
	return OK;
}
int main()
{
	SqList L;
	ElemType e;
	int i;
	int m;//元素的个数
	InitList(L);/*线性表L的初始化*/
	printf("输入线性表的个数:");
	scanf("%d", &m);
	printf("请输入线性表元素:");
	for (i = 0; i < m; i++)
	{
		scanf("%d", &L.elem[i]);
		L.length++;
	}
	printf("线性表的长度为:%d\n", GetLength(L));/*求线性表的长度*/
	printf("输出线性表:");
	print(L);
	printf("判断线性表是否为空:");
	if (IsEmpty(L) == 1)
	{
		printf("线性表为空\n");
	}
	else
	{
		printf("线性表不为空\n");
	}
	printf("输入要取出的位置:");
	scanf("%d",&i);
	printf("取出的值为:");
	GetElem(L, i, e);/*线性表的取值*/
	printf("%d\n",e);
	printf("输入要查找的元素:");
	scanf("%d",&e);
	printf("查找到的线性表的位置:%d\n", LocateElem(L, e));/*线性表的查找*/
	printf("输入要插入的位置:");
	scanf("%d",&i);
	printf("输入要插入的元素:");
	scanf("%d",&e);
	ListInsert(L, i, e);/*线性表的插入*/
	printf("输出线性表:");
	print(L);
	printf("输入要删除元素的位置:");
	scanf("%d", &i);
	ListDelete(L, i);/*线性表的删除*/
	printf("输出线性表:");
	print(L);
	printf("清空线性表后判断线性表是否为空:");
	ClearList(L);/*线性表的清空*/
	if (IsEmpty(L) == 1)
	{
		printf("线性表为空\n");
	}
	else
	{
		printf("线性表不为空\n");
	}
	return 0;
}
free(L.elem);
释放空间出现的错误

先看代码和错误提示:

样例输入:
3
1 1 1
样例输出:
1 1 1

#include <iostream>
using namespace std;
const int MAXSIZE = 100;
int n;
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
typedef struct
{
	ElemType* elem;
	int length;
}SqList;
Status InitList(SqList &L)
{
	//L.elem = (ElemType*)malloc(sizeof(ElemType) * n);
	L.elem = new ElemType[n];
	L.length = 0;
	return OK;
}
Status print(SqList L)
{
	if (L.length == 0) return ERROR;
	else
	{
		for (int i = 0; i < L.length; i++) cout << L.elem[i] << " ";
	}
	cout << endl;
	return OK;
}
void DestroyList(SqList L)
{
	if (L.elem) free(L.elem);
}
int main()
{
	SqList L;
	InitList(L);
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> L.elem[i];
		L.length++;
	}
	print(L);
	DestroyList(L);
	return 0;
}

在这里插入图片描述
发现错误的原因是代码 L.elem = new ElemType[n]; 这一行,因为输入了 n 个数,所以我申请开辟的空间乘以了 n 个,但是在最后的释放的时候会显示错误弹窗。当把代码改成 L.elem = new ElemType[n + 1]; 的时候就会正确。

正确代码如下:

#include <iostream>
using namespace std;
const int MAXSIZE = 100;
int n;
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
typedef struct
{
	ElemType* elem;
	int length;
}SqList;
Status InitList(SqList &L)
{
	//L.elem = (ElemType*)malloc(sizeof(ElemType) * (n + 1));
	L.elem = new ElemType[n + 1];
	L.length = 0;
	return OK;
}
Status print(SqList L)
{
	if (L.length == 0) return ERROR;
	else
	{
		for (int i = 0; i < L.length; i++) cout << L.elem[i] << " ";
	}
	cout << endl;
	return OK;
}
void DestroyList(SqList L)
{
	if (L.elem) free(L.elem);
}
int main()
{
	SqList L;
	InitList(L);
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> L.elem[i];
		L.length++;
	}
	print(L);
	DestroyList(L);
	return 0;
}

free()的时候程序宕机,很多时候都是和内存溢出或者说内存分配问题有关,一句话就是你想释放的内存和你释放的内存不一致,很多时候是大小不一致。 因为在分配的时候,代码:L.elem = new ElemType[n]; 上面只开辟了sizeof(n)个内存,但是实际上节点被绑到其它数据块上了,它远远不止sizeof(n)字节的内存,因此在释放上就会存在内存溢出的错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清纯献给了作业

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

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

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

打赏作者

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

抵扣说明:

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

余额充值