前言:我是为了考研来学习的数据结构,作为计算机专业基础课,它是当值无愧的一块难啃的硬骨头,对于我这个跨专业的考生学起来非常的难受;前面发的C学习的博客是为了学习这里的铺垫,毕竟不熟悉结构体、指针、还有malloc()等函数简直就不是到那些代码写的啥。下面的总结笔记主要来自于小甲鱼老师的视频教程,我边看边暂停记笔记写代码,在博客里做一个归拢,当作一个回顾和复习(其实和预习差不多)。
一、线性表定义及特点
1、定义
- 一个线性表是由零个或多个元素组成的有限序列;
2、特点
- 是一个非空的有限集
- 若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其余元素有且仅有一个前驱和后继;
3、记法
- Date 记作(a1,a2,a3,…ai,a(i+1),…an);
- n为线性表长度;
- n = 0为空表;
二、线性表作为抽象数据类型所定义的一些操作
1、什么是抽象数据类型
- 抽象数据类型(Abstract Date Type,ADT):是指一个数学模型以及定义在此数学模型上的一组操作;
- 描述方法:可用(D,S,P)三元组表示:
|D| 数据对象 |
|S|D上的关系集|
|P | 对D的基本操作集 |
2、线性表常用的基本操作总览
- InitList(*L):初始化操作,构建一个空的线性表;
- ListEmpty(L):判断线性表是否为空,若为空则返回TRUE,否则返回FALSE;
- ClearList(*L):清空线性表;
- GetElem(L,i,*e):将线性表L中的第i个位置元素返回给e;
- LocateElem(L,e):在线性表中查找与e相等的元素,成功返回i,无返回0;
- ListInsert(*L,i,e):在线性表L中第i个位置插入新元素e;
- ListDelete(*L,i,*e):删除线性表L中第i个元素,并用e返回其值;
- ListLength(L):返回线性表L的元素个数;
三、基本操作的结构代码及其算法思路
1、线性表顺序存储结构(顺序表)的结构代码
#define MAXSIZE 20
typedef int ElemType;
typedef struct{
ELemType date[MAXSIZE];
int length; //线性表当前长度
}SqList;
//实际上是对数组封装,增加了当前长度变量length
顺序存储结构封装需三个属性:
- 存储空间的起始位置,即数组date的存贮位置date[0];
- 最大存储容量,即数组长度MAXSIZE;
- 当前长度:length;
地址计算方法:
第i个元素ai的存储地址:LOC(ai) = LOC(a1) +(i-1)*c; //c = sizeof(SqList);
*2、InitList(L),构建一个新的线性表
Status InitList_Sq(SqList &L){
//构建一个空的线性表L;
L.elem =(ElemType *)malloc(MAXSIZE*sizeof(ElemType));
if( L == NULL)
printf("申请内存失败\n");
L.length = 0; //空表长度为零
L.listsize = MAXSIZE;//初始存储容量
return OK;
}
3、ListEmpty(L),判断线性表是否为空
Status ListEmpty_Sq(SqList L){
//判断线性表是否为空
if(L.length == 0)
return TRUE;
else return FALSE;
}
*4、ClearList(L),清空线性表
Status ClearList_Sq(Sqlist &L){
//清空线性表,需要找到表头位置,用到指针
int i;
for(i=0;i<L.length;i++)
{
L.elem[i]=0;
}
L.Length = 0;
return OK;
}
*5、GetElem(L,i,e),查找线性表L第i个位置的元素并返回给e;
Status GetElem_Sq(SqList L, int i, ElemType *e)
{
if(L.length==0 || i<1 ||i>L.length )
{
return ERROR;
}
*e = L.elem[i-1];
return OK;
}
6、LocateElem(L,e),在线性表中查找与给定e相等的元素
Status LocateELem_Sq(SqList L,ElemType e)
{
int i;
for(i=0;i<L.length;i++)
{
if(L.elem[i] == e)
return i;
}
return ERROR;
}
**7、ListInsert(L,I,e):删除线性表L中的第i个元素,并用e返回其值
Status ListInsert_Sq(Sqlist *L,int i,ElemType *e)
{
int k;
if(L->length == MAXSIZE ) //顺序表已经满了
{
return ERROR;
}
if( i<1 || i>L->length+1 ) //当i不在范围内时
{
return ERROR;
}
if( i<=L->length ) //若插入数据位置不在表尾
{
/*将要插入位置后数据向后移动一位*/
for( k=L->length-1; k>=i-1;k--)
{
L->elem[k+1] = L->elem[k];
}
}
L->elem[i-1] = e; //将新元素插入
L->length++;
return OK;
}
算法思路:
- 若插入位置不合理,抛出异常
- 若线性表长度大于数组长度,异常或扩容
- 从最后一个元素开始遍历到第i个位置,分别将它们后移一位;
- 将要插入元素填入i处
- 线性表+1
8、ListDelete(*L,i,*e):删除线性表L中第i个元素,并用e返回其值
Status ListDelete_Sq(Sqlist *L,int i,ElemType *e)
{
int k;
if(L->length == 0 ) //顺序表为空
{
return ERROR;
}
if( i<1 || i>L->length ) //当i不在范围内时
{
return ERROR;
}
*e = L->elem[i-1]; //取出要删除元素地址
if( i<L->length ) //若删除数据位置不在表尾
{
/*将要插入位置后数据向前移动一位*/
for( k=i; k < L->length; k++) //???如何与插入操作区分?
{
L->elem[k-1] = L->elem[k];
}
}
L->length--;
return OK;
}
算法思路:
- 删除位置判断:不合理就抛出异常,否则继续
- 取出删除元素
- 从最后一位元素遍历到第i个位置,分别将它们前移一位
- 返回要删除元素位置
- 线性表长度-1
四、顺序存储结构的优缺点
-
优点:可以快速的存取表中任意位置的元素,无需为表示元素之间的逻辑关系增加额外的存储空间(指针域);
-
缺点:插入和删除需要移动大量的元素,当线性表变化较大时无法确定存储空间容量,已造成存储空间浪费。
五、综上线性表的整体代码
/* 顺序表练习 */
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20
typedef int ElemType;
//顺序存储结构线性表结构代码
typedef struct
{
ElemType elem[MAXSIZE];
int length;
}SqList;
typedef int Status;
//Status 是函数的类型,其值是函数结果状态代码,如OK等
//初始条件:顺序线性表L已存在,i<=i<=ListLength(L)
//操作结果,用e返回L中第i个数据元素的值
Status InitList_Sq(SqList *L){
//构建一个空的线性表L;
L->length = 0; //空表长度为零
return OK;
}
Status ListEmpty_Sq(SqList L){
//判断线性表是否为空
if(L.length == 0)
return TRUE;
else return FALSE;
}
Status ListLength_Sq(SqList *L){ //求长度
return L->length;
}
Status ClearList_Sq(SqList *L){
//清空线性表,需要找到表头位置,用到指针
int i;
for(i=0;i<L->length;i++)
L->elem[i]=0;
L->length = 0;
return OK;
}
Status GetElem(SqList L, int i, ElemType *e)
//查找线性表L第i个位置的元素并返回给e
{
if(L.length==0 || i<1 ||L.length )
{
return ERROR;
}
*e = L.elem[i-1];
return OK;
}
Status LocateELem_Sq(SqList L,ElemType e)
//在线性表L中查找与给定e相等的元素
{
int i;
for(i=0;i<L.length;i++)
{
if(L.elem[i] == e)
return i;
}
return ERROR;
}
Status ListInsert_Sq(SqList *L,int i,ElemType *e)
//删除线性表L中的第i个元素,并用e返回其值
{
int k;
if(L->length == MAXSIZE ) //顺序表已经满了
{
return ERROR;
}
if( i<1 || i>L->length+1 ) //当i不在范围内时
{
return ERROR;
}
if( i<=L->length ) //若插入数据位置不在表尾
{
/*将要插入位置后数据向后移动一位*/
for( k=L->length-1; k>=i-1;k--)
{
L->elem[k+1] = L->elem[k];
}
}
L->elem[i-1] = *e; //将新元素插入
L->length++;
return OK;
}
Status ListDelete_Sq(SqList *L,int i,ElemType *e)
//删除线性表L中第i个元素,并用e返回其值
{
int k;
if(L->length == 0 ) //顺序表为空
{
return ERROR;
}
if( i<1 || i>L->length ) //当i不在范围内时
{
return ERROR;
}
*e = L->elem[i-1]; //取出要删除元素地址
if( i<L->length ) //若删除数据位置不在表尾
{
/*将要插入位置后数据向前移动一位*/
for( k=i; k < L->length; k++) //???如何与插入操作区分?
{
L->elem[k-1] = L->elem[k];
}
}
L->length--;
return OK;
}
Status DisplayList_Sq(SqList *L){ //输出表
if(L->length == 0)
{
return ERROR;
}
int i ;
for(i = 1; i <= L->length; i++){
printf("%2d ",L->elem[i]);
if(i % 10 == 0) printf("\n");
}
printf("\n");
return OK;
}
int main()
{
SqList list;
InitList_Sq(&list);
ElemType e;
printf("初始化顺序表:\n");
printf("表长:%d\n",ListLength_Sq(&list));
ListEmpty_Sq(list) == 1?printf("表空\n"):printf("表不空\n");
printf("插入数字 1-10:\n");
int i;
for(i = 1; i <= 10; i++){
e = i;
ListInsert_Sq(&list,i,&e);
}
printf("表长:%d\n",ListLength_Sq(&list));
ListEmpty_Sq(list) == 1?printf("表空\n"):printf("表不空\n");
printf("输出顺序表:\n");
DisplayList_Sq(&list);
printf("在第1位插入9\n");
e = 9;
ListInsert_Sq(&list, 1, &e);
printf("输出顺序表:\n");
printf("表长:%d\n",ListLength_Sq(&list));
DisplayList_Sq(&list);
printf("\n删除第10位\n");
ListDelete_Sq(&list, 10, &e);
printf("表长:%d\n",ListLength_Sq(&list));
printf("输出顺序表:\n");
DisplayList_Sq(&list);
return 0;
}
注意:InitList()在最后的整体代码中没有用malloc()函数去分配初始地址:
L->elem[0] =(ElemType *)malloc(MAXSIZE*sizeof(ElemType));
//错误的,数组元素不不能直接赋值的,这种创建方式一般用在链表里面
Status InitList_Sq(SqList &L)
//这里的函数定义是错的,c++才允许这样传指针
正确写法:
Status InitList_Sq(SqList *L)
自己实践一边感觉收益良多。