线性表讲解

第二章 线性表
线性结构特点 : 在数据元素非空的有限集中
1 存在唯一一个第一和最后一个元素
2 除第一个元素之外,集合中的每个元素都只有一个前驱
3 除最好一个元素之外,集合中每个元素都只有一个后继

线性表:n 个数据元素的有限序列
抽象数据线性表定义
ADT list{
	数据对象 D : = {......}
	数据关系
	基本操作{
	InitList(&L);   //构造一个空的线性表
    DestroyList(&L);  //销毁线性表
    ClearList(&L);    //清空线性表
    ListEmpty(L);     //判断线性表是否为空
    ListLength(L);    //求线性表长度
    GetElem(L, i, &e);   //用 e 返回线性表第 i 个元素
    LocateElem(L, e, compare());  //返回L中第 1 个与 e 满足 compare()关系的元素
    PriorElem(L, cur_e, &pre_e);   //求 cur_e 的前驱元素
    NextElem(L, cur_e, &next_e);   //求 cur_e 的后继元素
    ListInsert(&L, i, e);   //在第 i 个位置之前插入元素 e
    ListDelete(&L, i, &e);  //删除第 i 个位置的元素,用 e 返回其值
    ListTraverse(L, visit());  //依次对L的每个元素调用函数 visit()
}
}
稍复杂操作举例
合并线性表A,B
void unionList(&La, List Lb)
{
	//将所有在线性表Lb中但不再La的元素插入La中
	La.len = ListLength(La);  //求线性表长
	Lb.len = ListLength(Lb);
	for (int i = 1; i < Lb.len; i++)
	{
		GetElem(Lb, i, e);    //取表 Lb 第 i 个元素赋给 e
		if (!LocatElem(La, e, equal))       //La 中不存在和 e 相同的元素则插入
			ListInsert(La, ++La_len, e);
	}
}


归并线性表
//将表A,B按照非递减顺序归并成一新表C
void MergeList(List La, List Lb, List &Lc)
{
	//已知线性表La和Lb中的数据元素按值非递减排列
	//归并La和Lb
	InitList(Lc);
	i = j = 1;
	k = 0;
	La.len = ListLength(La);
	Lb.len = ListLength(Lb);
	while (i <= La.len&&j <= Lb.len)
	{
		GetElem(La, i, ai);
		GetElem(Lb, j, bj);
		if (ai <= bj)
		{
			ListInsert(Lc, ++k, ai);
			++i;
		}
		else
		{
			ListInsert(Lc, ++k, bj);
			++j;
		}
	}
	while (i <= La.len)
	{
		GetElem(La, i++, ai);
		ListInsert(Lc, ++k, ai);
	}
	while (j <= Lb, len)
	{
		GetElem(Lb, ++j, bj);
		ListInsert(Lc, ++k, bj);
	}
}


线性表的顺序结构--使用数组
#define LIST_INIT_SIZE 100   //线性表存储空间的初始分配量
#define LISTINCREMENT 10     //线性表存储空间的分配增量
typedef struct {
	ElemType *elem;     //存储空间基址
	int length;         //当前长度
	//int listsize;       //当前分配的存储容量(以sizeof(ElemType)为单位)下述测试代码不用
}SqList;
上述基本操作的实现:以顺序结构为例
/*测试结构
typedef struct{
int *data;
int length;
}SqList;
*/
SqList *InitList(void)      //创建空表,分配相应内存 
{
	SqList *L = (SqList *)malloc(sizeof(SqList));
	L->data = (int *)malloc(sizeof(int)*LIST_INIT_SIZE);
	if (!L)  //分配内存失败
	{
		printf("failure\n");
		exit(0);
	}

	L->length = 0;    //初始化表长

	return L;
}

void DestroyList(SqList *L)
{
	free(L->data);
	free(L);
}

顺序结构下的线性表插入和删除操作比较复杂,在每插入或删除一个元素之后,该元素后面的元素要做相应的移动以保持存储结构上的相邻
int ListInsert(SqList *L, int i, int e)   //插入操作 在第 i 个元素的位置上插入新元素
{
	
	for (int p = L->length; p >= i - 1; p--)
	{
		L->data[p + 1] = p;
	}
	L->data[i - 1] = e;
	L->length++;  //增加当前表长

	return OK;
}

int ListDelete(SqList *L, int i)  //删除操作,删除第 i 个元素,返回删除的元素值
{
	if (L == NULL)
	{
		printf("the list is null\n");
		exit(0);
	}
	int temp = L->data[i - 1];
	for (int p = i; p < L->length; p++)
	{
		L->data[p - 1] = L->data[p];
	}
	L->length--;  //减少当前表长

	return temp;
}


//上述操作的范例代码
Status ListInsert(SqList &L, int i, ElemType e)
{
	//在顺序表L中第 i 个位置之前插入新的元素 e
	// i 的合法值为 1 <= i <= ListLength(L)+1

	if (i<1 || i>L.length + 1)      // i 值不合法
		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]);  //q 为插入位置
	for (p = &(L.elem[L.length = 1]); p >= q; --p)
		*(p + 1) = *p;    //后移元素

	*q = e;  //插入元素
	++L.length;  //增加表长

	return OK;
}


Status ListDelete(SqList &L, int i, ElemType &e)
{
	//在顺序表 L 中删除第 i 个元素,用 e 返回其值
	// i 的合法值为 1 <= i <= ListLength(L)
	if (i<1 || i>L.length)
		return ERROR;

	p = &(L.elem[i - 1]);  //p为被删除元素位置
	e = *p; //被删除元素值赋给 e ,注意这些位置都是以地址表示的,得到值要用取地址运算符
	q = L.elem + L.length - 1;
	for (++p; p <= q; ++p)
		*(p - 1) = *p;
	--L.length;
	return OK;
}
//NOTE 在对顺序结构的线性表进行插入删除操作时较为复杂,移动元素的次数过多
//其解决方法可以采用链式结构的线性表,或者对顺序结构做处理,增加标记元素,但是这样会造成逻辑结构与存储结构的不一致
//扩展如静态链表

//在上例中用到的LocateElem()操作
int LocateElem(SqList *L, ElemType e, Status(*compare)(ElemType, ElemType))
{
	//在顺序表 L 中查找第 1 个值与 e 满足 compare() 元素的位序
	//若找到返回其在 L 中的位序,否则返回 0
	i = 1;   // i 的初值位第一个元素的位序
	p = L.elem;  //p 的初值为第一个元素的存储位置
	while (i <= L.length && !(*compare)(*p++, e))
		++i;

	if (i <= L.length)
		return i;
	else
		return 0;
}


//MergeList的第二种写法
void MergeList(SqList La, SqList Lb, SqList *Lc)
{
	//已知线性表La和Lb中的数据元素按值非递减排列
	//归并La和Lb
	pa = La.elem;
	pb = Lb.elem;
	Lc.listsize = Lc.length = La.lengt + Lb.length;
	pc = Lc.elem = (Elemtype*)malloc(Lc.listsize * sizeof(ElemType));
	if (!Lc.elem)
		exit(OVERFLOW);

	pa.last = La.elem + La.length - 1;
	pb.last = Lb.elem + Lb.length - 1;
	while (pa <= pa.last&&pb <= pb.last)  //归并
	{
		if (*pa <= *pb)
			*pc++ = *pa++;
		else
			*pc++ = *pb++;
	}
	//插入剩下元素
	while (pa <= pa.last)
		*pc++ = *pa++;
	while (pb <= pb.last)
		*pc++ = *pb++;
}
NOTE 以线性表合并进行集合的各种运算最好先进行排序,这样可以减少部分算法的时间复杂度



顺序表的链式结构--使用指针索引
typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;
//GetElem 在链表中的实现
//由于链表的每个结点数据在存储位置上没有必然联系,只能通过索引指针来进行元素索引
int GetElem(LinkList L, int i)
{
	//L为带头结点的单链表头指针
	//存在第 i 个元素时,返回其值,否则返回 ERROR
	LinkList p = L->next;
	int count = 0;
	if (i > L.length)
	{
		printf("ERROR");
		return ERROR;
	}
	
	while (count < i)
	{
		p = p->next;
		count++;
	}

	return p->data;
}
//范例代码
Status GetElem(LinkList L, int i, ElemType &e)
{
	p = L->next;
	j = 1;
	while (p&&j < i)
	{
		p = p->next;
		j++;
	}
	if (!p || j > i)
		return ERROR;
	e = p->data;
	return OK;
}

//链表的插入与删除
//由于链表是由指针索引,插入与删除操作只需断开结点后再续接即可,不需要对大量元素进行移动,时间复杂度较低
int listInsert(LinkList L, int i, int e)
{
	LinkList P = L->next;
	LinkList q = L;
	if (i > L.length)
		return ERROR;

	int count = 1;
	while (count < i)
	{
		p = p->next;  
		q = q->next;
		count++;
	}
	//结束循环后 p 指向第 i 个位置 ,q 指向 i - 1 的位置,初始 count = 0 时该条件会使使指针溢出

	/*在第 i 个位置之前插入元素 e -- 需要对 q 指针做操作
	LinkList temp = (LinkList)malloc(sizeof(LNode));  //申请新结点
	if (!temp)
		exit(0);
	temp->data = e;
	temp->next = NULL;

	q->next = temp;  //续接指针
	temp->next = p;
	*/

	/*在第 i 个位置之后插入元素 e -- 需要对 p 指针操作
	LinkList temp = (LinkList)malloc(sizeof(LNode));  //申请新结点
	if (!temp)
		exit(0);
	temp->data = e;
	temp->next = NULL;

	temp->next = p->next;
	p->next = temp;
	*/

	return OK;
}

int listDelete(LinkList L, int i)   //删除第 i 个结点
{
	LinkList p = L->next;
	LinkList q = L;

	//删除位置不合理
	if (p == NULL)
		return ERROR;
	else if (i > L.length)
		return ERROR;

	int count = 1;
	while (count < i)
	{
		p = p->next;
		q = q->next;
		count++;
	}
	q->next = p->next;
	free(p);

	return OK;
}
//esp 上述代码中的L.length表示 L 的长度,在该链表结构中并没有存储其值,在此处仅做形象表示,
//可以通过函数计算或则在创建的时候存储在头节点的数据域中
//范例代码
Status ListInsert(LinkList &L, int i, ElemType e)
{
	//在带头结点的单链表L中第 i 个位置之前插入元素 e
	ListLink p = L;
	int j = 0;
	while (p&&j < i - 1)
	{
		p = p->next;
		j++;
	}

	if (!p || j > i)
		return ERROR;

	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (!s)
		exit(0);
	s->data = e;
	s->next = p->next;
	p->next = s;

	return OK;
}

Status ListDelete(LinkList &L, int i, ElemType &e)
{
	//在带头结点的单链表L中删除第 i 个元素并用 e 返回其值
	LinkList p = L;
	int j = 0;
	while (p->next&&j < i - 1)  //寻找第 i 个结点,并令 p 指向其前驱
	{
		p = p->next;
		j++;
	}

	if (!p->next || j > i - 1)
		return ERROR;

	LinkList q = p->next;
	p->next = q->next;
	e = q->data;
	free(q);

	return OK;
}

//链表是一种动态存储结构,使用时要分配相应内存,销毁时要对相应内存进行释放
//由于是动态结构,相较于顺序结构的链表,在存储上具有优势,可以任意的添加或删除元素
//下面提供几种创建链表的方法
void creatListFIFO(LinkList *L)  //先进先出
{
	*L = (LinkList)malloc(sizeof(LNode));
	if (!(*L))
		exit(0);
	(*L)->next = NULL;

	LinkList current = NULL, last = NULL;
	int e;
	while (scanf("%d", &e) == 1)
	{
		current = (LinkList)malloc(sizeof(LNode));
		if (!current)
			exit(0);
		else
		{
			current->next = NULL;
			current->data = e;
			if ((*L)->next == NULL)
			{
				(*L)->next = current;
				last = current;
			}
			else
			{
				last->next = current;
				last = current;
			}
		}
	}
	if (last)
		last->next = NULL;
}

LinkList creatListFIFO(void)  //先进先出,无参数
{
	LinkList L = (LinkList)malloc(sizeof(LNode));
	if (!L)
		exit(0);
	L->next = NULL;

	LinkList current = NULL, last = NULL;
	int e;
	while (scanf("%d", &e) == 1)
	{
		current = (LinkList)malloc(sizeof(LNode));
		if (current != NULL)
		{
			current->data = e;
			if (L->next == NULL)
			{
				L->next = current;
				last = current;
			}
			else
			{
				last->next = current;
				last = current;
			}
		}
	}
	if (last)
		last->next = NULL;

	return L;
}

void creatListFILO(LinkList *L)  //先进后出,有参数
{
	*L = (LinkList)malloc(sizeof(LNode));
	if (!(*L))
		exit(0);
	(*L)->next = NULL;

	LinkList current = NULL;
	int e;
	while (scanf("%d", &e) == 1)
	{
		current = (LinkList)malloc(sizeof(LNode));
		if (current != NULL)
		{
			current->data = e;
			current->next = (*L)->next;
			(*L)->next = current;
		}
	}
}

LinkList creatListFILO(void)  //先进后出,无参数
{
	LinkList L = (LinkList)malloc(sizeof(LNode));
	if (!L)
		exit(0);
	L->next = NULL;

	LinkList current = NULL;
	int e;
	while (scanf("%d", &e) == 1)
	{
		current = (LinkList)malloc(sizeof(LNode));
		if (current != NULL)
		{
			current->data = e;
			current->next = L->next;
			L->next = current;
		}
	}

	return L;
}
//示例代码
void CreatList(LinkList &L, int n)
{
	//逆位序输入n个元素值,建立带头结点的单链表
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;
	for (int i = n; i > 0; i--)
	{
		LinkList p = (LinkList)malloc(sizeof(LNode));
		scanf(&p->data);
		p->next = L->next;
		L->next = p;
	}
}


//归并有序链表
void MergeList(LinkList &La, LinkList &Lb, LinkList &Lc)
{
	LinkList pa = La->next;
	LinkList pb = Lb->next;
	LinkList pc = La;
	Lc = pc;
	while (pa&&pb)
	{
		if (pa->data <= p->data)
		{
			pc->next = pa;
			pc = pa;
			pa = pa->next;
		}
		else
		{
			pc->next = pb;
			pc = pb;
			pb = pb->next;
		}
	}
	pc->next = pa ? pa : pb;
	free(Lb);
}



循环链表--将最后一个结点接入头结点上形成一个环

双向链表
//双向链表的存储结构--在每个结点里增添了一个指向前驱的指针
typedef struct DuLNode {
	ElemType data;
	struct DuLNode *prior;
	struct DuLNode *next;
}DuLNode,*DuLinkList;
NOTE 在双向链表的计算表长,取元素和检索元素之类的操作与单链表基本一致,但是插入删除等操作设计了两个指针,就有很大不同
//示例代码
Status ListInsert(DuLinkList &L, int i, ElemType e)
{
	//在带头结点的双循环链表中第 i 个位置之前插入元素 e
	// i 的合法值为 1 <= i <= 表长 + 1
	if (!(p = GetElem(L, i)))   // 在 L 中确定插入位置
		return ERROR;           //p = NULL,插入位置不合法

	if (!(s = (DuLinkList)malloc(sizeof(DuLNode))))
		return ERROR;
	s->data = e;
	s->prior = p->prior;
	p->prior->next = s;
	s->next = p;
	p->prior = s;
	return OK;

}

Status ListDelete(DuLinkList &L, int i, ElemType e)
{
	//删除带头结点的双循环链表的第 i 个元素,i 的合法值为 1 <= i <= 表长
	if (!(p == GetElem(L, i)))
		return ERROR;
	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	free(p);
	return OK;
}



单链表的应用--一元多项式的表示及相加
在计算机中表示一元多项式可由一个线性表来表示 P = (p0, p1, p2...pn);
每一项的指数 i 隐含在其系数 pi 的序号里
假设Q是一元m次多项式,同样可以用线性表Q表示:Q = (q0, q1, q2...qm);
一般,设 m < n 则两个多项式相加有 Rn = Pn + Qm;
R = (p0 + q0, p1 + q1, p2 + q2...pm + qm, ...pn);
抽象数据类型一元多项式的定义如下
ADT Polynomial{
	数据对象
	数据关系
	基本操作
	creatPolyn(&P,m)  //输入 m 项的系数和指数,建立一元多项式
    DestroyPolyn(&P)  //销毁一元多项式
	PrintPolyn(P)     //打印一元多项式
	PolynLength(P)    //返回一元多项式的项数
	AddPolyn(&Pa,&pb) //相加并销毁 Pb
	SubtractPolyn(&Pa,&Pb) //相减并销毁 Pb
	MultiplyPolyn(&Pa,&Pb) //相乘并销毁 Pb
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值