线性表
Posted silentsharer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表相关的知识,希望对你有一定的参考价值。
1. 线性表综述:
线性表是具有相同特性数据元素的一个有限序列
该序列中所包含元素的个数叫做线性表的长度
当一个线性表有序时,成为有序表,属于逻辑结构
线性表的逻辑特征:
对于至少含有一个元素的线性表来说
除起始元素没有前驱元素外,其他元素都有一个唯一前驱
除终端元素没有后继元素外,其他元素都有一个唯一后继
在线性表中,每个元素最多只有一个前驱和后继元素
线性表存储结构:
线性表有顺序和链式两类存储结构,前者成为顺序表,后者成为链表
顺序表特点:
无需为元素间的逻辑关系儿增加额外的存储空间
可以随机存取表中的任何一个元素,但是要占用连续的空间
而且插入和删除元素时需要移动大量的元素,平均需要移动n/2个元素
链表特点:
不仅存储每个节点的信息,而且也存储节点之间的关系(通过指针)
链表的地址可以是不连续的,不支持随机访问,插入和删除比较快
链表中的第一个节点的存储位置通常被成为头指针
如果链表有头结点,头指针为头结点的地址
如果没有头结点,头指针为开始节点的地址
通常用头指针来标识一个链表
1、顺序表程序:
//线性表存储空间的初始分配量
#define LIST_INIT_SIZE 10
//顺序存储结构的分配增量
#define LIST_INCREMENT 5
typedef int ElemType;
//顺序存储结构体
struct SqList
{
ElemType* elem; //存储空间基址
int length; //数据元素当前长度
int listsize; //当前分配的总容量(元素个数)
};
/*******基础操作********/
//初始化顺序表
void InitList(SqList* L)
{
L->elem = (ElemType*)malloc(sizeof(ElemType)*LIST_INIT_SIZE);
if (L->elem == NULL)
{
printf("空间申请失败!\n");
exit(-1);
}
L->length = 0;
L->listsize = LIST_INIT_SIZE;
}
//销毁顺序表
void DestroyList(SqList* L)
{
free(L->elem);
L->elem = NULL;
L->length = 0;
L->listsize = 0;
}
//清空顺序表
void ClearList(SqList* L)
{
L->length = 0;
}
//判空
int EmptyList(SqList* L)
{
if (L->length == 0)
return 1;
return 0;
}
//顺序表长
int ListLength(SqList* L)
{
return L->length;
}
//得到顺序表第index元素
int GetElem(SqList* L, int index, ElemType* e)
{
//要找的元素不在顺序表范围内
if (index<1 || index>L->length)
return -1;
*e = L->elem[index-1];
return 1;
}
//返回元素的位置
int LocateElem(SqList* L, ElemType* e)
{
for (int i=0; i<L->length; ++i)
{
//元素相等
if (L->elem[i] == *e)
return i;
}
return -1;
}
//返回元素的前驱
int PriorElem(SqList* L, ElemType* e)
{
int index = LocateElem(L, e);
//第一个元素没有前驱
if (index == -1 || index == 0)
return -1;
return index-1;
}
//返回元素的后继
int NextElem(SqList* L, ElemType* e)
{
int index = LocateElem(L, e);
//最后一个元素没有后继
if (index == -1 || index == L->length-1)
return -1;
return index+1;
}
//顺序表插入
int ListInsert(SqList* L, int index, ElemType* e)
{
//判断插入位置是否合法
if (index<1 || index>L->listsize)
return -1;
//判断空间是否满
if (L->length == L->listsize)
{
L->elem = (ElemType*)realloc(L->elem, (L->listsize+LIST_INCREMENT)*sizeof(ElemType));
if (L->elem == NULL)
exit(-1);
L->listsize += LIST_INCREMENT;
}
//开始插入
for (int i=L->length-1; i>=index-1; --i)
{
//循环后移
L->elem[i+1] = L->elem[i];
}
//插入
L->elem[index-1] = *e;
//长度++
L->length++;
return 1;
}
//顺序表删除
int ListDelete(SqList* L, int index)
{
//判断删除位置是否合法
if (index<1 || index>L->length)
return -1;
//开始删除
for (int i=index-1; i<L->length; ++i)
{
L->elem[i] = L->elem[i+1];
}
--L->length;
return 1;
}
//顺序表遍历
void ListTraverse(SqList* L)
{
for (int i=0; i<L->length; ++i)
{
cout << L->elem[i] << ‘ ‘;
}
cout << endl;
}
/*******进阶操作********/
//合并两个顺序表
//将所有在顺序表Lb中,但不在La中的元素插入到La中
void Union(SqList* La, SqList* Lb)
{
int lena = ListLength(La);
int lenb = ListLength(Lb);
for (int i=0; i<Lb->length; ++i)
{
//在La中没有出现就插入
if (LocateElem(La, &(Lb->elem[i])) == -1)
ListInsert(La, La->length+1, &(Lb->elem[i]));
}
}
//归并两个有序顺序表
void MergeList(SqList* La, SqList* Lb, SqList* Lc)
{
int i=0, j=0;
int lena = La->length;
int lenb = Lb->length;
//初始化顺序表Lc
InitList(Lc);
//两个都没有完的时候
while (i<lena & j<lenb)
{
//La<Lb
if (La->elem[i] < Lb->elem[j])
{
//尾插La里面的元素
ListInsert(Lc, Lc->length+1, &(La->elem[i]));
++i;
}
else //Lb<La
{
//尾插Lb里面的元素
ListInsert(Lc, Lc->length+1, &(Lb->elem[j]));
++j;
}
}
//其中有一个插完了
while (i < lena)
{
//尾插La里面的元素
ListInsert(Lc, Lc->length+1, &(La->elem[i]));
++i;
}
while (j < lenb)
{
//尾插Lb里面的元素
ListInsert(Lc, Lc->length+1, &(Lb->elem[j]));
++j;
}
}
int main()
{
/*基础操作测试
SqList L;
//初始化
InitList(&L);
cout << "初始化后: 首地址:" << L.elem << "元素个数:" << L.length << "总容量:" << L.listsize << endl;
//头插
for (int i=1; i<=5; ++i)
ListInsert(&L, 1, &i);
cout << "头插5次:" << endl;
ListTraverse(&L);
//是否为空
cout << "顺序表是否为空:" << EmptyList(&L) << endl;
//大小
cout << "顺序表大小:" << ListLength(&L) << endl;
//清空顺序表
cout << "清空顺序表:" << endl;
ClearList(&L);
cout << "清空后大小:" << ListLength(&L) << endl;
//尾插
for (int i=6; i<=10; ++i)
ListInsert(&L, L.length+1, &i);
cout << "尾插5次:" << endl;
ListTraverse(&L);
//删除第3个元素
cout << "删除第3个元素:" << endl;
ListDelete(&L, 3);
cout << "删除后:" << endl;
ListTraverse(&L);
//销毁顺序表
DestroyList(&L);
cout << "首地址:" << L.elem << "元素个数:" << L.length << "总容量:" << L.listsize << endl;
cout << "顺序表已成功销毁" << endl;
*/
SqList La, Lb, Lc;
//初始化
InitList(&La);
InitList(&Lb);
InitList(&Lc);
//插入La
for (int i=1; i<=5; ++i)
ListInsert(&La, La.length+1, &i);
//插入Lb
for (int i=2; i<=10; i+=2)
ListInsert(&Lb, Lb.length+1, &i);
//合并两个顺序表
//Union(&La, &Lb);
//遍历顺序表La
//cout << "合并后为:" << endl;
//归并La和Lb
MergeList(&La, &Lb, &Lc);
cout << "La和Lb归并后:" << endl;
ListTraverse(&Lc);
return 0;
}
2.链表:
和顺序表相比,链表在实现插入、删除操作时,不需要移动大量的元素
但是不容易实现随机存取,适用于经常进行插入和删除的线性表
一般用一个头指针来唯一的标识一个链表
链表的插入和删除:
插入和删除要先把后边的链表链起来,然后再链接上前面的链表,都要找到当前节点和前驱
链表的插入和删除主要考虑:第一个元素、中间元素、最后一个元素
其中,中间元素和最后一个元素的操作都相同,只剩下第一个元素和其余元素的区别
会因为带头结点和不带头结点而有所区别:比较重要:
不带头结点:
因为如果不带头结点插入表头或者删除表头的话,得修改链表的唯一标识指针即 L
带头结点的话,令头结点为第0个元素,表头结点为第1个元素
就可以不用修改L来插入或者删除表头了,这时修改的是头结点的指针域
链表指针始终指向的是头结点。所以就不用考虑插入是在表头还是在其他地方了
带头结点:
带头结点的链表的插入和删除,都是定义两个结点指针变量 p=L, q=NULL
p指向要插入或删除的结点,q指向p的前驱结点
因为不管是插入和删除,都要找到待操作结点和该结点的前驱
插入和删除:
插入:位置判断条件是:pos<1 || NULL==q 就退出
因为插入时,可以插在1–length+1之间,即 p==NULL是允许的
删除:位置判断条件是:pos<1 || NULL==p 就退出
因为在删除时,可以删除1–length之间,要删除的结点必须不能为空,即 p!=NULL
typedef int ElemType;
//链表结构体定义
typedef struct Node
{
ElemType data; //数据域
struct Node* next; //指针域
}Node, *LinkList;
//初始化
//也可以用Node** L来代替LinkList* L
void InitList(LinkList* L)
{
//产生头结点,并使L指向该头结点
*L = (Node*)malloc(sizeof(Node));
if (*L == NULL)
{
cout << "分配失败!" << endl;
exit(-1);
}
(*L)->next = NULL;
}
//销毁链表
//得传入一个链表的指针
void DestroyList(LinkList* L)
{
Node* q = NULL;
while (NULL != *L)
{
q = (*L)->next;
free(*L);
*L = q;
}
cout << "销毁成功!" << endl;
}
//清空一个链表,不改变头结点
void ClearList(LinkList L)
{
Node* p = NULL;
Node* q = NULL;
p = L->next; //p指向第一个结点
while (NULL != p) //循环删除链表结点
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;
}
//链表判空
int ListEmpty(LinkList L)
{
if (L->next == NULL)
return 1;
return 0;
}
//统计链表长度
int ListLength(LinkList L)
{
Node* q = L->next;
int length = 0;
while (NULL != q)
{
++length;
q = q->next;
}
return length;
}
//取得链表第i个元素
int GetElem(LinkList L, int pos, ElemType* e)
{
Node* p = L->next;
int count = 1;
while (NULL != p && count < pos)
{
++count;
p = p->next;
}
//没有找到
if (NULL==p || count>pos)
return 0;
//找到
*e = p->data;
return 1;
}
//获取元素在链表中的位置
int LocateElem(LinkList L, ElemType* e)
{
Node* p = L->next; //使p指向第一个结点
int pos = 1;
while (NULL!=p && p->data!=(*e))
{
++pos;
p = p->next;
}
if (NULL == p)
return -1;
return pos;
}
//找元素的前驱
int PriorElem(LinkList L, ElemType* cur_e, ElemType* pre_e)
{
Node* p = L->next; //指向当前结点
Node* q = NULL; //指向当前结点的前驱
while (NULL!= p && p->data!=(*cur_e))
{
q = p;
p = p->next;
}
if (NULL == p)
return -1;
*pre_e = q->data;
return 1;
}
//找到元素的后继
int NextElem(LinkList L, ElemType* cur_e, ElemType* next_e)
{
Node* p = L->next;
while (p->next != NULL && p->data!=(*next_e))
{
p = p->next;
}
if (NULL == p->next) //没有找到
return -1;
*next_e = p->next->data;
return 1;
}
//在指定位置插入
//可以在1和length+1位置之间进行插入
int ListInsert(LinkList L, int pos, ElemType* e)
{
Node* p = L; //指向要插入的位置
Node* q = NULL; //指向要插入的前驱
int count = 0;
//寻找要插入结点的前驱q
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
//插入位置不对 大于表长NULL==p或者小于1
if (pos<1 || NULL==q)
{
cout << "插入位置不正确!" << endl;
return -1;
}
p = (Node*)malloc(sizeof(Node));
p->data = *e;
p->next = q->next;
q->next = p;
return 1;
}
//在指定位置删除元素
//在1和length之间可以删除
int ListDelete(LinkList L, int pos, ElemType* e)
{
Node* p = L; //指向要删除的位置
Node* q = NULL; //指向要删除位置的前驱
int count = 0;
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
if (pos<1 || NULL==p) //位置不在范围内
{
cout << "删除位置不正确!" << endl;
return -1;
}
//删除结点
q->next = p->next;
*e = p->data;
free(p);
p = NULL;
return 1;
}
/***********不带头结点的链表的插入和删除***********/
int ListInsert(LinkList* L, int pos, ElemType* e)
{
int count = 1;
Node* p = NULL;
Node* q = NULL;
p = (Node*)malloc(sizeof(Node));
p->data = *e;
p->next = NULL;
//表头
if (pos == 1)
{
p->next = (*L);
*L = p;
}
else //其他位置
{
while (NULL!=q && count<pos-1)
{
q = q->next;
++count;
}
//插入位置大于表长
if (pos<1 || NULL == q)
{
cout << "插入位置不对!" << endl;
return -1;
}
p->next = q->next;
q->next = p;
}
return 1;
}
int ListDelete(LinkList* L, int pos, ElemType* e)
{
Node* p = *L;
Node* q = NULL;
int count = 1;
//头删
if (pos == 1)
{
p = (*L)->next;
*L = p->next;
free(p);
p = NULL;
}
else
{
while (NULL!=p && count<pos)
{
q = p;
p = p->next;
++count;
}
if (pos<1 || NULL==p)
{
cout << "插入位置不合法!" << endl;
return -1;
}
q->next = p->next;
*e = p->data;
free(p);
p = NULL;
}
}
//链表遍历
void ListTraverse(LinkList L)
{
Node* p = L->next;
while (NULL != p)
{
cout << p->data << endl;
p = p->next;
}
}
//头插建立链表
void HeadCreateList(LinkList* L, int n)
{
//建立头结点
*L = (Node*)malloc(sizeof(Node));
(*L)->next = NULL;
Node* p = NULL;
int num = 0;
for (int i=0; i<n; ++i)
{
p = (Node*)malloc(sizeof(Node));
cin >> num;
p->data = num;
p->next = (*L)->next;
(*L)->next = p;
}
}
//尾插法建立链表
void TailCreateList(LinkList* L, int n)
{
//建立头结点
(*L) = (Node*)malloc(sizeof(Node));
(*L)->next = NULL;
Node* p = NULL;
Node* cur = *L;
int num = 0;
for (int i=0; i<n; ++i)
{
//找到尾结点
while (NULL != cur->next)
{
cur = cur->next;
}
//开辟新结点
p = (Node*)malloc(sizeof(Node));
cin >> num;
p->data = num;
p->next = NULL;
//插入尾部
cur->next = p;
}
}
//合并两个链表(合并不同元素)
void Union(LinkList La, LinkList Lb)
{
ElemType e;
int len_a = ListLength(La);
int len_b = ListLength(Lb);
for (int i=1; i<=len_b; ++i)
{
GetElem(Lb, i, &e);
if (LocateElem(La, &e) == -1)
ListInsert(La, ++len_a, &e);
}
}
//归并两个有序链表
void MergeList(LinkList La, LinkList Lb, LinkList Lc)
{
int len_a = ListLength(La);
int len_b = ListLength(Lb);
int i=1, j=1;
Node* pa = La->next;
Node* pb = Lb->next;
Node* pc = NULL;
while (i<=len_a && j<=len_b)
{
//La < Lb
if (pa->data < pb->data)
{
//开辟新结点
pc = (Node*)malloc(sizeof(Node));
pc->data = pa->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//La后移一个
pa = pa->next;
++i;
}
else
{
//开辟新结点
pc = (Node*)malloc(sizeof(Node));
pc->data = pb->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//Lb后移一个
pb = pb->next;
++j;
}
}
while (i <= len_a)
{
//开辟新结点
pc = (Node*)malloc(sizeof(Node));
pc->data = pa->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//后移一个
pa = pa->next;
++i;
}
while (j <= len_b)
{
//开辟新结点
pc = (Node*)malloc(sizeof(Node));
pc->data = pb->data;
pc->next = NULL;
//接入Lc
Lc->next = pc;
Lc = pc;
//后移一个
pb = pb->next;
++j;
}
}
int main()
{
/****基础功能测试****/
/*
LinkList L;
//初始化链表
InitList(&L);
//头插
for (int i=0; i<5; ++i)
ListInsert(L, 1, &i);
//在第最后一个位置插入
int num = 10;
ListInsert(L, 6, &num);
//遍历
cout << "插入后的链表:" << endl;
ListTraverse(L);
//链表长度
cout << "链表长度为:" << ListLength(L) << endl;
//在第1个位置删除
cout << "删除后的链表:" << endl;
ListDelete(L, 1, &num);
//遍历
ListTraverse(L);
//清空链表
cout << "清空链表:" << endl;
ClearList(L);
//遍历
ListTraverse(L);
//链表长度
cout << "链表长度为:" << ListLength(L) << endl;
//销毁链表
cout << "销毁链表:" << endl;
DestroyList(&L);
*/
LinkList L;
//头插创建链表
//HeadCreateList(&L, 3);
//尾插创建链表
TailCreateList(&L, 3);
//遍历
ListTraverse(L);
/****进阶功能测试****/
LinkList La, Lb, Lc;
//初始化链表
InitList(&La);
InitList(&Lb);
InitList(&Lc);
//头插
for (int i=1; i<=5; ++i)
ListInsert(La, i, &i);
//尾插
int count = 1;
for (int i=2; i<=10; i+=2)
ListInsert(Lb, count++, &i);
//遍历
ListTraverse(La);
ListTraverse(Lb);
//Union(La, Lb);
cout << "归并后的链表为:" << endl;
MergeList(La, Lb, Lc);
ListTraverse(Lc);
return 0;
}
以上是关于线性表的主要内容,如果未能解决你的问题,请参考以下文章