2.1 线性表的定义和特点
- 定义:线性表是具有相同特性的数据元素的一个有限序列;
- ai-1,ai,ai+1 中ai-1为直接前趋ai+1为直接后继;
- 线性表是典型的线性结构;
- 线性表的优点:随机存取 ;
2.2 案例引入
- 一元多项式表示,记作 Pn(x)=p0+p1x+p2x2+…+pnxn,其中设定指数为 i 系数为 pi例如:P(x)=10+5x-4x2+3x3+2x4 表示为
- 一元多项式的运算:实现两个多项式加,减,乘运算;运算原理是分别从头遍历比较存放一元多项式的两个数组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指针。
线性表的顺序表示
- 顺序表中元素存储位置的计算:LOC(ai) = LOC(a1) + (i-1) * l,其中l是每个元素所占的存储单元;
- 顺序表特点:地址连续,依次存放,随机存取,类型相同;
- 顺序表示定义模板:
#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=3;
int 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)字节的内存,因此在释放上就会存在内存溢出的错误。