第二章序言
上一章的两节介绍了常用的算法,这些算法用来处理零散的数据。实际上我们有时候处理的数据之间是存在一种或者多种特定的关系时,我们称这些关系为结构。通常数据之间有三种基本的机构。
(1)线性结构:数据元素之间为一对一的关系。
(2)树形结构:数据元素之间为一对多的关系。
(3)网状结构:数据元素之间为多对多的关系。
什么是线性表?
线性表示最基本、最简单、也是最常用的一种数据结构。它是一个含有n个节点的有限序列,不同线性表的数据元素可以不痛,但是对于同一个线性表,各个数据元素必须有相同的数据类型,即同一线性表中各个数据元素具有相同的数据类型,每个数据元素的长度相同。
线性表的数据结构具有以下特征:
a、有且只有一个首元素
b、有且只有一个末元素
c、除末元素之外,其余元素均有唯一的后继元素
d、除首元素之外,其余元素均有唯一的前驱元素
顺序表是用一组地址连续的存储单元依次保存线性表中的数据元素,由于它使用这种存储结构,使得程序中可以很容易的访问顺序表中的任何元素,也可以方便的将数据元素添加到顺序表的末尾。
顺序表有什么结构特征?
1、采用一组地址连续的存储单元来存储数据
2、表的存储容量长度不易改变
3、可以随机查找
4、可频繁访问元素
5、不适用于元素的频繁增删操作
项目代码案例,项目结构如下:
文件1:Utils.h
#ifndef _YELER_082_02
#define _YELER_082_02
typedef int ElemType;
typedef int Status;
typedef void MyVoid;
typedef void MyVoid;
typedef int ElemType;
typedef int Status;
typedef struct
{
ElemType * elem; //存储空间基址
int length; //当前实际使用长度
int listsize; //当前分配存储容量
}SqList;
//比较函数的声明
Status compare(ElemType e1, ElemType e2);
MyVoid visit(ElemType e, ElemType i);
#endif
文件二:method.h
#ifndef _YELER_082_01
#define _YELER_082_01
#include "Utils.h"
Status Init(SqList &L);//构造一个空的线性表
Status Destory(SqList &L);//销毁线性表L
Status ClearList(SqList &L);//将L表重置为空表
Status ListEmpty(SqList L);//判断L表是否为空
Status ListLength(SqList L);//返回L表中数据元素的长度
Status ListInsert(SqList &L, ElemType i, ElemType e);//向表中的第i个位置插入元素e
Status GetElem(SqList L,ElemType i,ElemType &e);//取出L表中指定位置的元素
Status LocateElem(SqList L,ElemType e, Status(*compare)(ElemType, ElemType));//找出在L表中第一个与e匹配的元素
Status PriorElem(SqList L,ElemType cur_e,ElemType &pre_e);//找出当前元素cur_e的前驱pre_e
Status NextElem(SqList L,ElemType cur_e,ElemType &next_e);//找出当前元素cur_e的后继next_e
Status ListDelete(SqList &L,ElemType i,ElemType &e);//删除表中第i个位置的元素e
Status ListTraverse(SqList L, MyVoid(*visit)(ElemType,ElemType));//遍历表中的每个元素在visit函数中执行一遍
Status ListSort(SqList &L);//对链表中的数据排序,从小到大
MyVoid MergeList_Sq(SqList La, SqList Lb, SqList &Lc); //已知线性表La和Lb,归并La和Lb的到线性表Lc,Lc的元素按照非递减排列
#endif
文件3:Utils.cpp
#include<stdio.h>
typedef int ElemType;
typedef int Status;
typedef void MyVoid;
//比较的工具函数
Status compare(ElemType e1, ElemType e2)
{
if (e1 == e2)
return true;//在C++中有bool类型的定义,C语言中是没有的
return false;
}
//输出的工具函数
MyVoid visit(ElemType e,ElemType i)
{
printf("第%d个元素是%d\n", i, e);
}
文件4:method.cpp
#include<stdio.h>
#include<stdlib.h>
#include "Utils.h"
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
#define OK 1
#define TRUE 1
#define FALSE 0
#define NOTEXIST 0
#define OVERFLOW -1
#define ERROR -2
//构造一个空的线性表
Status Init(SqList &L)
{
L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));//给线性表分配初始空间400个字节
if (!L.elem)
{
exit(OVERFLOW);
}
L.length = 0;//指定实际长度为0,存的数据个数
L.listsize = LIST_INIT_SIZE;//指定分配空间长度
return OK;
}
//销毁线性表L
Status Destory(SqList &L)
{
if (!L.elem)//如果线性表为空,返回错误码
{
return ERROR;
}
free(L.elem);
return OK;
}
//将L表重置为空表
Status ClearList(SqList &L)
{
if (!L.elem)
{
return ERROR;
}
L.length = 0;
return OK;
}
//判断L表是否为空
Status ListEmpty(SqList L)
{
if (!L.elem)
{
return ERROR;
}
if (L.length!=0)
{
return TRUE;
}
return FALSE;
}
//返回L表中数据元素的长度
Status ListLength(SqList L)
{
if (!L.elem)
{
return ERROR;
}
return L.length;
}
//取出L表中指定位置的元素
Status GetElem(SqList L, ElemType i, ElemType &e)
{
if (!L.elem)
{
return ERROR;
}
if (i<1 || i>L.length + 1)
{
return ERROR;
}
e= L.elem[i-1];
return OK;
}
//找出在L表中第一个与e匹配的元素,第三个参数是指向比较函数的指针
Status LocateElem(SqList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
//在顺序线性表L中查找第1个值与e满足compare()的元素的为序
//若找到,则返回其在L中的位序,否则返回0
int i = 1;//i的初值为第1个元素的位序
ElemType *p = L.elem;//定义一个指针指向线性表的起始位置
while (i<=L.length&&!(*compare)(*p++,e))//线性表的数据在长度范围内没有找到相同的数值,位置以此后移
{
i++;
}
if (i<=L.length)
{
return i;//返回的是在第几个位置的元素
}
else
{
return NOTEXIST;
}
}
//找出当前元素cur_e的前驱pre_e
Status PriorElem(SqList L, ElemType cur_e, ElemType &pre_e)
{
ElemType *p = NULL,i=1;
p = L.elem;//定义一个指针指向头结点
if (!L.elem)
{
return ERROR;
}
if (*p==cur_e)//如果当前被找的参照物为第一个节点,那么返回错误
{
return ERROR;
}
//遍历找到当前的节点所在的位置
while (i<=L.length)
{
if (*p==cur_e)
{
pre_e = *(p - 1); break;
}
i++;
p++;
}
if (i <= L.length)
{
return OK;
}
else
{
return NOTEXIST;
}
}
//找出当前元素cur_e的后继next_e
Status NextElem(SqList L, ElemType cur_e, ElemType &next_e)
{
ElemType *p = NULL,i=0;
p = L.elem;
//当传递过来的线性表为空,或者ElemType cur_e为最后的一个节点的时候,返回错误值
if (!p||cur_e==L.elem[L.length-1])//数组表示是从零开始,L.elem[L.length-1]代表线性表中的最后一个元素
{
return ERROR;
}
while (i<=L.length)
{
if (cur_e==*p)
{
next_e = *(p + 1); break;
}
i++;
p++;
}
if (i <= L.length)
{
return OK;
}
else
{
return NOTEXIST;
}
}
//向表中的第i个位置插入元素e
Status ListInsert(SqList &L, ElemType i, ElemType e)
{
ElemType *newbase = NULL, *q = NULL, *p = NULL;
if (!L.elem)
{
return ERROR;
}
if (i<1 || i>L.length + 1)
{
return ERROR;
}
if (L.length >= L.listsize)
{
newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREMENT)*sizeof(ElemType));
if (!newbase)
{
exit(OVERFLOW);
}
L.elem = newbase;//新基址
L.listsize += LISTINCREMENT;
}
q = &(L.elem[i - 1]);//得到要插入元素的地址
//插入之后的元素位置后移
for (p = &(L.elem[L.length - 1]); p >= q; --p)
{
*(p + 1) = *p;
}
*q = e;
++L.length;
return OK;
}
//删除表中第i个位置的元素e
Status ListDelete(SqList &L, ElemType i, ElemType &e)
{
ElemType *p = NULL,*q= NULL,t=1;
p = L.elem;
if (!L.elem)
{
return ERROR;
}
if (i<1||i>L.length+1)
{
return ERROR;
}
//删除涉及到移位操作
p = &L.elem[i];//获取需要删除的元素的地址
e = *p;//将被删除的数据放置到变量中,不能放到下方,因为指针的位置变了
q = L.elem + L.length - 1;//q是指向表尾节点的指针
for (++p; p <=q ; p++)
{
*(p - 1) = *(p);
}
L.length--;
return OK;
}
//遍历表中的每个元素在visit函数中执行一遍typedef void MyVoid;
Status ListTraverse(SqList L, MyVoid(*visit)(ElemType, ElemType))
{
ElemType *p = NULL,i;
p = L.elem;
for (i = 1; i <=L.length; i++,p++)
{
(*visit)(*p,i);
}
return OK;
}
//对链表中的数据排序,从小到大,采用冒泡排序法
Status ListSort(SqList &L)
{
ElemType *p = NULL, i, j;
ElemType temp;
for ( i = 0; i < L.length; i++)
{
for ( j = 0; j < L.length-i-1; j++)
{
if (L.elem[j]>L.elem[j+1])
{
temp = L.elem[j];
L.elem[j ] = L.elem[j+1];
L.elem[j+1] = temp;
}
}
}
return OK;
}
//已知线性表La和Lb,归并La和Lb的到线性表Lc,Lc的元素按照非递减排列
MyVoid MergeList_Sq(SqList La, SqList Lb, SqList &Lc)
{
ElemType *pa = NULL, *pb = NULL, *pc = NULL;
ElemType *pa_last = NULL, *pb_last = NULL;
pa = La.elem, pb = Lb.elem;
Lc.listsize = Lc.length = La.length + Lb.length;
pc = Lc.elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));//为pc申请空间
if (!Lc.elem) exit(OVERFLOW);//存储分配失败
pa_last = La.elem + La.length - 1;//获取线性表La的最后一个元素的指针
pb_last = Lb.elem + Lb.length - 1;//获取线性表Lb的最后一个元素的指针
while (pa <= pa_last&&pb <= pb_last)
{
if (*pa <= *pb)
{
*pc++ = *pa++;//*pc = *pa; pc = pc+1; pa = pa+1;
}
else
{
*pc++ = *pb++;
}
}
while (pa<=pa_last)
{
*pc++ = *pa++;//插入La中的剩余元素
}
while (pb<=pb_last)
{
*pc++ = *pb++;//插入Lb中的剩余元素
}
}
文件5:main.cpp
#include<stdio.h>
#include<stdlib.h>
#include"method.h"
#include"Utils.h"
int main()
{
SqList L,La,Lb,Lc;
ElemType i = 0,e=0,*p=NULL,flag;
printf("*************线性表的顺序操作**********\n");
printf("初始化顺序表:");
i=Init(L);
printf("%d\n",i);
/*printf("————————————————————\n");
printf("销毁顺序表:");
i = Destory(L);
printf("%d\n", i);*/
/*printf("————————————————————\n");
printf("将顺序表置空:");
i=ClearList(L);
printf("%d\n",i);*/
/*printf("————————————————————\n");
printf("判断顺序表是否为空:");
i = ListEmpty(L);
printf("%d\n", i);*/
/*printf("————————————————————\n");
printf("输出顺序表的长度:");
i=ListLength(L);
printf("%d\n", i);*/
printf("————————————————————\n");
printf("向表中插入元素\n");
ListInsert(L, 1, 5);
ListInsert(L, 2, 3);
ListInsert(L, 1, 1);
ListInsert(L, 2, 7);
ListInsert(L, 2, 10);
printf("插入元素为:");
p = L.elem;//这里另外找一个指针专门用来遍历,不要轻易的移动L.elem,否则会对后面的操作造成麻烦
for ( i = 0; i < ListLength(L); i++)
{
printf("%d\t",*p);
p++;
}
printf("\n表中元素的长度为%d\n", ListLength(L));
printf("————————————————————\n");
printf("取出L表中第三个位置的元素:");
i=GetElem(L, 3, e);
printf("%d\n",e);
printf("————————————————————\n");
printf("判断线性表中是否存在某个元素,本例以10为查找的目的数据:\n");
//int(*f) (int x); /* 声明一个函数指针 */
Status(*my_compare)(ElemType e1, ElemType e2);//定义了一个指向函数类型为Status compare(ElemType e1, ElemType e2)的指针
my_compare = compare; //将函数compare的入口地址赋给函数指针变量my_compare
i = LocateElem(L,10, my_compare);//调用定位函数,判断10这个元素在顺序线性表中是否存在
if (i==0)
{
printf("该数据在线性表中不存在!\n");
}
else
{
printf("线性表中存在该元素,处于第%d个位置\n",i);
}
printf("————————————————————\n");
printf("取出L表中7的前驱元素:");
flag=PriorElem(L, 7, i);
if (flag)
{
printf("L表中7的前驱元素是-->%d\n",i);
}
else
{
printf("取出L表中7的前驱元素不存在!\n");
}
printf("————————————————————\n");
printf("取出L表中7的后继元素:");
flag = NextElem(L, 7, i);
if (flag)
{
printf("L表中7的后继元素是-->%d\n", i);
}
else
{
printf("取出L表中7的后继元素不存在!\n");
}
printf("————————————————————\n");
printf("删除L表中数值为7元素:\n");
ListDelete(L, 3, i);
printf("L表中被删除的元素是%d\n", i);
printf("————————————————————\n");
printf("遍历L表中的元素并打印:\n");
ListSort(L);//调用排序函数
MyVoid(*Visit)(ElemType, ElemType);//定义一个指向返回空值,含有两个参数,名称为Visit的函数指针
Visit = visit;//将函数指针指向一个函数
ListTraverse(L, Visit);
printf("————————————————————\n");
Init(La), Init(Lb);//初始化两个线性表
//向线性表中插入数据
ListInsert(La, 1, 5);
ListInsert(La, 2, 9);
ListInsert(La, 3, 4);
ListInsert(Lb, 1, 2);
ListInsert(Lb, 1, 7);
ListInsert(Lb, 1, 1);
ListInsert(Lb, 2, 8);
ListSort(La), ListSort(Lb);//对线性表进行排序
MergeList_Sq(La, Lb, Lc);
ListTraverse(Lc, Visit);
return 0;
}
运行截图: