二、线性表
线性结构:线性表、栈、队列、串和数组
顺序表
特点:逻辑上相邻的数据元素,其物理次序也是相邻的。可以做到随机存取,按照下标取元素
线性表的应用:多项式的运算、稀疏多项式的运算,图书管理系统
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define MAXSIZE 100
typedef int Status; //定义函数类型名
typedef int ElemType;//定义ElemType类型
typedef struct {
ElemType* elem; //线性表数据存储空间基址
int length;
}SqList;// LIST为用户定义的线性表类型
//初始化空线性表
Status InitList(SqList& L) {
L.elem = (ElemType*)malloc(MAXSIZE * sizeof(ElemType));
if (L.elem == NULL) {
return ERROR;
}
L.length = 0;
return OK;
}
Status ClearList(SqList& L) {
L.length = 0;
return OK;
}
//求出线性表长度
int ListLength(SqList L) {
return L.length;
}
//向线性表指定位置插入一个新元素
Status ListInsert(SqList &L, int pos, ElemType e) {
//pos为插入的(逻辑)位置,e为待插入的数据元素。插入成功则返回1;否则函数返回值为0。
if ((pos < 1) || (pos>L.length+1)) return 0;
if (L.length == MAXSIZE) return 0;
for (int j = L.length - 1; j >= pos-1; j--) {
L.elem[j + 1] = L.elem[j];
}
L.elem[pos - 1] = e;
L.length++;
return OK;
}
//从线性表中删除指定位序的元素
Status ListDelete(SqList& L, int pos)
{//pos为删除的(逻辑)位置,用e返回被删元素。删除成功则返回1;否则函数返回值为0。
int n;
int j;
n = ListLength(L);
if (pos<1 || pos>L.length) {
return 0;
}
for (size_t j =n-1; j > pos - 1; j--) {
L.elem[j] = L.elem[j + 1];
}
n--;
return OK;
}
//获取顺序表中指定位置上的数据元素
Status GetElem(SqList L, int pos, ElemType& e) {
//pos为指定位置,e用于返回找到的数据元素,如果表空,则返回ERROR;
int j;
if ((pos < 1) || (pos > L.length)) {
return 0;
}
e= L.elem[pos-1];
return e;
}
//从线性表中查找元素,返回第一个与指定值匹配元素位置
int LocateElem(SqList L, ElemType e) {
//e为待查找的数据元素
for (size_t i=0;i<L.length;i++){
if (L.elem[i] == e) {
return i+1;
}
}
return 0;
}
//遍历输出线性表
Status TraverseList(SqList L) {
for (size_t i = 0; i < L.length; i++)
{
printf("%5d", L.elem[i]);
}
return 1;
}
// 集合并运算LA=LA∪LB
Status Union(SqList& La, SqList Lb) {
int m, n;
ElemType e;
m = ListLength(La);
n = ListLength(Lb);
for (int i = 1; i <= n; i++) {
GetElem(Lb, i,e);
if (!LocateElem(La, e)) {
ListInsert(La, ++m, e);
}
}
return 0;
}
// 集合交运算LA=LA ∩LB
Status Intersection(SqList& La, SqList Lb) {
SqList Lc;
InitList(Lc);
int m;
int n;
int k;
ElemType e;
m = ListLength(La);
n = ListLength(Lb);
k = ListLength(Lc);
for (size_t i = 1; i <= n; i++) {
GetElem(Lb, i, e);
if (LocateElem(La, e)) {
//Lc.length++;
ListInsert(Lc, ++k, e);
}
}
ClearList(La);
for (size_t i = 0; i < Lc.length; i++) {
ListInsert(La, i + 1, Lc.elem[i]);
}
return 0;
}
// 集合差运算LA=LA-LB
Status Difference(SqList& La, SqList Lb) {
int m, n;
int k;
ElemType e;
SqList Lc;
InitList(Lc);
k = ListLength(Lc);
m = ListLength(La);
n = ListLength(Lb);
for (size_t i = 1; i <= m; i++) {
GetElem(La, i, e);
if (!LocateElem(Lb, e)) {
ListInsert(Lc, ++k, e);
}
}
ClearList(La);
for (size_t i = 0; i < Lc.length; i++) {
ListInsert(La, i + 1, Lc.elem[i]);
}
return 0;
}
// 有序表合并LC=LA+LB
Status MergeList(SqList La, SqList Lb, SqList& Lc) {
ElemType *pc;
ElemType *pa;
ElemType* pb;
ElemType* pc_last;
ElemType* pa_last;
ElemType* pb_last;
Lc.length = La.length + Lb.length;
Lc.elem = new ElemType[Lc.length];
pc = Lc.elem;
pa = La.elem;
pb = Lb.elem;
pb_last = Lb.elem + Lb.length - 1;
pa_last = La.elem + La.length - 1;
while (pa <= pa_last && pb <= pb_last) {
if (*pa <= *pb) { *pc = *pa; pc++; pa++; }
else *pc++ = *pb++;
}
while (pa <= pa_last) *pc++ = *pa++;//LA到达表尾,依次将LA剩余元素插入LC
while (pb <= pb_last)*pc++ = *pb++;//LB到达表尾,依次将LB剩余元素插入表尾
return 0;
}
Status Purge(SqList& Lc) {
int m;
int k;
ElemType e;
SqList l;
InitList(l);
k = ListLength(l);
m = ListLength(Lc);
for (int i = 0; i < m; i++) {
if (Lc.elem[i] == Lc.elem[i + 1] ) {
ListInsert(l, ++k, Lc.elem[i]);
i = i + 2;
}
if (Lc.elem[i] != Lc.elem[i + 1]) {
ListInsert(l, ++k, Lc.elem[i]);
}
}
ClearList(Lc);
for (size_t i = 0; i < l.length; i++) {
ListInsert(Lc, i + 1, l.elem[i]);
}
return 0;
}
//主函数
int main() {
SqList LA, LB, LC; //定义顺序表变量
ElemType Array1[] = { 2,8,27,39,66,77,89 }; //顺序表LA的元素
ElemType Array2[] = { 6,18,27,59,65,77,89,120,140 };//顺序表LB的元素
ElemType item;
int i;
//0.数据准备
//0-1初始化线性表LA,LB
InitList(LA);
InitList(LB);
InitList(LC);
for (size_t i = 0; i < 7; i++) {
ListInsert(LA, i+1, Array1[i]);
}
for (size_t i = 0; i < 9; i++) {
ListInsert(LB, i+1, Array2[i]);
}
//0-2生成顺序表LA,LB
//0-3输出顺序表表元素
printf("\n");
printf("LA=");
TraverseList(LA);
printf("\n");
printf("LB=");
TraverseList(LB);
//1. 计算LA=LA∪LB,并输出结果
Union(LA, LB);
printf("\n");
printf("LA=");
TraverseList(LA);
//2.计算LA=LA ∩LB,并输出结果
ClearList(LA);
for (size_t i = 0; i < 7; i++) {
ListInsert(LA, i+1, Array1[i]);
}
Intersection(LA, LB);
printf("\n");
printf("LA=");
TraverseList(LA);
//3.计算LA=LA-LB,并输出结果
ClearList(LA);
for (size_t i = 0; i < sizeof(Array1) / sizeof(Array1[0]); i++) {
ListInsert(LA, i+1, Array1[i]);
}
Difference(LA, LB);
printf("\n");
printf("LA=");
TraverseList(LA);
//4.有序表合并LC=LA+LB,并输出结果
ClearList(LA);
for (size_t i = 0; i < sizeof(Array1) / sizeof(Array1[0]); i++) {
ListInsert(LA, i, Array1[i]);
}
MergeList(LA, LB, LC);
printf("\n");
printf("LC=");
TraverseList(LC);
//5.去掉LC重复多余的元素 ,并输出结果
Purge(LC);
printf("\n");
printf("LC=");
TraverseList(LC);
return 1;
}#include<stdio.h>`
`#include<stdlib.h>`
`#define TRUE 1`
`#define FALSE 0`
`#define OK 1`
`#define ERROR -1`
`#define initsize 4`
`#define addsize 2`
`typedef int status;`
`typedef int ElemType;`
`typedef struct //结构体构建`
`{`
`ElemType *elem; //储存空间基址`
`int len; //表的当前长度`
`int size; //当前分配的存储容量`
`} Line;`
`status LineInit(Line *L) //构造一个空的线性表
{
(*L).elem = (ElemType *)malloc(initsize*sizeof(ElemType)); //根据存储空间基址类型开辟空间`
`if((*L).elem==NULL)
exit (FALSE); //分配失败
(*L).len=0; //空表的长度为0`
`(*L).size=initsize; //初始储存容量`
`return OK;`
`}`
`status LineInsert(Line *L,int i,ElemType e) //线性表末插入数据`
`{`
`ElemType *newbase; //开辟新的空间`
`ElemType *p; //定义指针
int j;
if(i<1 || i>(*L).len+1)`
`return FALSE;`
`if((*L).len == (*L).size)//线性表满`
`{`
`newbase=(ElemType *)realloc((*L).elem,((*L).size+addsize)*sizeof(ElemType)); //开辟新空间`
`if(!newbase)`
`exit (FALSE); //开辟失败,退出`
`(*L).elem=newbase; //指针指向新开辟的空间
(*L).size=(*L).size+addsize;
}
//插入数据
p=(*L).elem;`
`for(j=0; j<(*L).len-i+1; j++)`
`*(p+(*L).len-j)=*(p+(*L).len-j`
return OK;}`
`Status ListInsert(Sqlist &L ,int i,ElemType e){`//表中插入
`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;`
`}`
`}`
`status LineDelete(Line *L,int i,ElemType *e)//删除数据
{
int j;
if(i<1 || i>(*L).len)`
`return FALSE;`
`*e=*((*L).elem+i-1);
for(j=0; j<(*L).len-i; j++)`
`*((*L).elem+i-1+j)=*((*L).elem+i+j);`
`(*L).len--;`
`return OK;`
`}`
`status LineDisplay(Line L) //展示表中原有的数据`
`{`
`int i;`
`for(i=0; i<L.len; i++)`
`{`
`printf("%5d",*(L.elem+i));`
`}`
`printf("\n");`
`return OK;`
`}`
`status LineLenth(Line L) //返回当前表长`
`{`
`return L.len;`
`}`
`status LineEmpty(Line *L) //清空列表
{
(*L).len=0;`
`return OK;`
`}`
`status LineDestory(Line *L) //释放内存
{
free((*L).elem);`
`(*L).elem=NULL;
(*L).len=0;`
`(*L).size=0;`
`return OK;`
`}`
`status ElemGet(Line L,int i,ElemType *e) //获取新的元素`
`{`
`if(i<1 || i>L.len)`
`return FALSE;`
`*e=*(L.elem+i-1);`
`return OK;`
`}`
`int main()`
`{`
`Line L;`
`ElemType i,e;`
`LineInit(&L); //初始化`
`for(i=0; i<initsize+5; i++) //插入一些测试数据`
`{`
`LineInsert(&L,1,i);`
`}`
`printf("current number is\n");`
`LineDisplay(L);//显示线性表的内容`
`LineDelete(&L,2,&e);//删除其中一个数据`
`printf("after delete No.2\n");`
`LineDisplay(L);//显示线性表的内容`
`ElemGet(L,3,&e);//获取其中一个数据`
`printf("ElemGet No.3 is %d\n",e);`
`LineInsert(&L,5,34);//再次插入测试数据`
`printf("after add No.5\n");`
`LineDisplay(L);`
`LineEmpty(&L);//清空线性表的内容`
`LineDestory(&L);//销毁这个线性表`
`return 0;`
`}`
#### 链表
线性表链式存储结构:用一组任意的存储单元存储线性表的数据元素。在存储了本身的信息之外,还需要存储一个指示其直接后继的信息。这两部分信息组成数据元素组成数据元素Ai的存储映像,称为节点。存储元素信息的域被称为数据域,存储直接后继存储地址位置的域被称为指针域。指针域中存储的信息被称为指针或链。
链表:单链表,循环链表,双向链表,二叉链表,十字链表,邻接表,邻接多重表。
链表中逻辑上相邻的两个数据元素其物理存储位置不要求相邻。(非顺序映像或链式映像,单链表可以由头指针唯一确定)
```C++
#include<iostream>
using namespace std;
class list {
public:
int data;
list* next;
list* last;
list* creatlist(list &L);
list* getelem(list &L,int e);
list* insert(list& L, int j,int e);
list* destory(list &L,int i);
}L,*p1,*p2;
list* list::creatlist(list &L) {
L.next = NULL;
L.data = -1;
L.last = NULL;
}
list* list::getelem(list& L,int e) {
p1 = L.next;
int j = 1;
while (p1) {
if (p1->data == e) {
return p1;
}
else p1++;
}
}
list* list::insert(list& L, int j, int e) {
list* n;
n = new list;
n->data = e;
p1 = L.next;
int i = 0;
while (p1 && i < j) {
p1 = p1->next;
i++;
}
n->next = p1->next;
p1->next = n;
delete n;
return p1;
}
list* list::destory(list &L ,int n) {
p1 = L.next;
int j = 0;
while (p1 && j < n - 1) {
p1++;
j++;
}
list* q;
q = new list;
q = p1->next;
p1->next = p1->next->next;
delete q;
return p1;
}
int main() {
}
循环链表:
表中最后一个节点的指针域指向头指针,让整个链表形成一个环,从表中的任何一个节点出发都可以遍历到所有节点。
双向链表:指针域指向前驱节点和直接后继节点
顺序表和链表的比较
存储空间:顺序表的存储空间必须提前分配,元素个数扩充收到一定的影响,易造成存储空间浪费和空间溢出现象;
链表不需要预先分配空间,只要内存空间允许,链表中的元素个数不受到限制。
存储密度:链表的每个节点除了设置数据域用来存储数据元素外,还要设置指针域,用来存储指示元素关系的指针,存储密度较小(数据元素本身所占用的存储量和整个节点结构所占据的存储量之比)
存储元素的效率:顺序表由数组实现按,是一种随机存储结构,取值的操作效率高
链表是一种顺序存储结构,按位置访问列表中的元素,只能从表头开始依次向后遍历链表,取值操作效率较低。
插入和删除操作:
对于链表,在确定插入和删除的位置后,插入和删除操作无需移动数据,只需要修改指针(时间复杂度为O(1))
对于顺序表,进行插入和删除时,平均需要移动表中近一半的节点,时间复杂度为O(n);
应用实例:
线性表的合并:
void MergeList(list &LA,list LB){//将所有在线性表LB中但不在LA中的数据元素插入到LA中
m=listlength(LA);
n=listlength(LB);//求线性表的长度
for (i=0;i<=n;i++){
Getelem(LB,i,e);//取LB中第i个数据元素赋值给e
if (!Located(La,e))//LA中不存在和e相同的元素
listinsert(LA,++m,e);//e插在LA的最后
}
}
有序表的合并:
void MergeList(Sqlist LA,Sqlist LB,Sqlist &LC){
//已知顺序有序表LA和LB的元素按值非递减排列
//归并LA和LB得到新的顺序有序表LC,LC的元素也按非递减排列
LC.length=LA.length+LB.length;//新表长度为待合并两表长度之和
LC.elem=new ElemType[LC.length];//未合并后的新表分配数组空间
pc=LC.elem;//指向新表的第一个元素
pa=LA.elem;
pb=Lb.elem;//pa,pb指向LA,LB的第一个元素
pa_last=LA.elem+LA.length-1;//
pb_last=LB.elem+LB.length-1;//指向LA,LB的最后一个元素
while(pa<=pa_last && pb<=pb_last){//LA,LB都未达到表尾
if (*pa<=*pb)*pc++=*pa++;//取两个表中较小的节点放入LC中
else *pc++=*pb++
}
while(pa<=pa_last) *pc++=*pa++;//LA到达表尾,依次将LA剩余元素插入LC
while(pb<=pb_last)*pc++=*pb++;//LB到达表尾,依次将LB剩余元素插入表尾
}