数据结构与算法 表

一.概念

将形如 A 1 , A 2 . . . A N A_1,A_2...A_N A1,A2...AN的数据类型称为(List)或线性表(Linear List), A i A_i Ai为其元素.即线性表是指由0/多个数据元素组成的有限序列,其中的元素间具有1对1的关系.包括 N N N个元素的表的大小为 N N N.特别地,大小为0的表称为空表(Empty List).表中的元素可以是任意的,但今后以整数为例.对非空表,称 A i + 1 A_{i+1} Ai+1 A i A_i Ai后继元, A i − 1 A_{i-1} Ai1 A i A_{i} Ai前驱元,或 A i + 1 A_{i+1} Ai+1后继 A i , A i − 1 A_i,A_{i-1} Ai,Ai1前驱 A i A_{i} Ai.对首元素 A 1 A_1 A1,不定义其前驱元;对尾元素 A N A_N AN,不定义其后继元.元素 A i A_i Ai在表中的位置是 i i i.表ADT的相关操作包括:PrintList(打印表中所有元素),MakeEmpty(清空表),Find(返回指定元素首次出现的位置),Insert(在指定位置插入指定元素),Delete(删除指定位置的元素),FindKth(返回指定位置处的元素).线性表有2种存储方式:顺序存储结构与链式存储结构

二.顺序存储结构

顺序存储的线性表使PrintList和Find以线性时间执行,FindKth则花费常数时间;但Insert和Delete的花费是高昂的,如在位置0插入元素(即插入新的首元素)需要将整个数组后移1位,删除首元素需要将整个数组前移1位,即最坏情况为O(N),平均来看,仍然需要线性时间;只通过 N N N次插入建立1个表需要2次时间.此外,还需要对表的大小进行估计,这通常需要估计得大一些,而这会浪费大量空间,尤其是在有许多未知大小的表的情况下.如数组就是1种常见的顺序存储的线性表

三.链式存储结构或链表(Linked List)
1.单链表(Singly Linked List)的概念:

为避免插入/删除的线性开销,允许表不连续存储,以避免移动整个表,这是单链表的一般想法
在这里插入图片描述
链表由一系列不必在内存中相连的结构组成,每个结构均含有表元素(所在区域称为=数据域=)和Next指针(所在区域称为指针域)
Next指针是指向包含该元素后继元的结构的指针;最后1个单元的Next指针指向NULL
NULL的值由C定义并且不能与其他指针混淆,ANSI C规定NULL为0
在这里插入图片描述
1个示例如下:
在这里插入图片描述

2.执行操作:

为执行PrintList(L)/Find(L,Key),只需要用1个指针指向该表的首元素,再通过NEXT指针遍历该表即可,这显然是线性时间的,虽然会比数组实现稍大
FindKth(L,i)花费O(i)时间以显性方式遍历链表,效率低于数组实现;不过在实践中这个上限常常是保守的,因为调用FindKth(L,i)常常是按顺序的,如FindKth(L,2),FindKth(L,3),FindKth(L,4),FindKth(L,6)可通过对表的1次扫描完成
Delete(L,i)可通过修改1个指针来实现(如下图)
在这里插入图片描述
Insert(L,i)需要使用malloc()获得1个新的单元,并调整2个指针(见下图)
在这里插入图片描述

3.几个问题:

几个问题:
①缺少在表的起始端插入元素的方法
②从表的起始端删除元素会导致表的丢失
③需要知道被删除元素前面的表元

解决方法:
①为了解决前2个问题,可以在起始端留出1个标志节点,称为表头(Header)或哑节点(Dummy Node),并约定表头在位置0处.不过,一些观点认为为处理特殊情况而添加表头单元,理由是不充分的
在这里插入图片描述
当然,也可以增加这2种特殊情况下的处理代码
②通过编写例程FindPrevious()找到前驱元:

4.(单链表的)具体实现
(1)指针实现:

#include <stdio.h>
#include <stdlib.h>

struct Node;
typedef struct Node * PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;

struct Node {
    int Element;
    Position Next;
};

void MakeEmpty(List L){//清空链表(只保留表头)
    Position P=L->Next,Tmp;
    L->Next=NULL;
    while (P!=NULL) {
        Tmp=P->Next;
        free(P);
        P=Tmp;
    }
}

int IsEmpty(List L){//测试是否为空表
    return L->Next==NULL;
}

int IsLast(Position P,List L) {//测试是否为尾元素
    return P->Next==NULL;
}

Position Find(int X,List L) {//返回某元素的内存地址(出现多次,只查找首个)
    Position P=L->Next;
    while (P!=NULL&&P->Element!=X) {
        P=P->Next;
    }
    return P;
}

Position FindPrevious(int X,List L) {//查找上1个元素的内存地址(出现多次,只查找首个的上1个元素的地址)
    Position P=L;
    while (P->Next!=NULL&&P->Next->Element!=X) {
        P=P->Next;
    }
    return P;
}

void Delete(int X,List L) {//删除指定元素(出现多次,只删除首个)
    Position P=FindPrevious(X,L),TmpCell;
    if (!IsLast(P,L)) {//如果X在表中存在
        TmpCell=P->Next;
        P->Next=TmpCell->Next;
        free(TmpCell);
    }
}

void Insert(int X,List L,Position P) {//插入元素
    Position TmpCell;
    TmpCell=malloc(sizeof(struct Node));
    TmpCell->Element=X;
    TmpCell->Next=P->Next;
    P->Next=TmpCell;
}

void Update(int X,int Y,List L) {//更新指定元素(出现多次,只更新首个)
    Position P=FindPrevious(X,L),TmpCell;
    if (!IsLast(P,L)) {//如果X在表中存在
       TmpCell=P->Next;
       TmpCell->Element=Y; 
    }
}

void DeleteList(List L) {//删除链表(释放所有节点)
    Position P=L->Next,Tmp;
    free(L);
    while (P!=NULL) {
        Tmp=P->Next;
        free(P);
        P=Tmp;
    }
}

List Create(int len) {//创建链表;len是不包括表头在内的链表长度 
    PtrToNode InitNode=NULL;//注意:该函数中没有创建表头 
    PtrToNode temp=(PtrToNode)malloc(sizeof(struct Node));
    temp->Element=0;
    temp->Next=NULL;
    InitNode=temp;
    int i;
    for (i=1;i<len;i++) {
        PtrToNode DataNode=(PtrToNode)malloc(sizeof(struct Node));
        temp->Next=DataNode;
        temp=DataNode;
	    temp->Element=i;
	    temp->Next=NULL;
    }
    return InitNode;
}

int main(void) {
    PtrToNode p=Create(10);
    return 0;
}

(2)游标实现:


5.(单)链表的扩展
(1)双链表(Doubly Linked List):

在链表的每个节点中额外添加1个域,其中包含1个指向上1个节点的指针,就形成了1个双链表.这个附加的链增加了空间开销,并使插入/删除的开销增加了一倍(因为有更多指针需要定位),但简化了删除操作

(2)循环链表:

使链表的尾节点中的指针指向首节点就形成了1个循环链表.这个首节点可以是表头也可以不是;并且该链表可以是单链表也可以是双链表

四.实例
1.多项式ADT
(1)数组实现:

#include <stdio.h>
#define N1 6
#define N2 5
#define MN N1+N2-1
#define DN N1-1

void addition(int* polynomial1, int n1, int* polynomial2, int n2) {//对多项式做加法,修改较大的数组
	int i;
	if (n1 < n2) {
		for (i = 0; i < n1; i++) {
			polynomial2[i] = polynomial1[i] + polynomial2[i];
		}
	}
	else {
		for (i = 0; i < n2; i++) {
			polynomial1[i] = polynomial1[i] + polynomial2[i];
		}
	}

}

void subtraction(int* polynomial1, int n1, int* polynomial2, int n2) {//对多项式做减法(polynomial1-polynomial2),修改较大的数组
	int i;
	if (n1 < n2) {
		for (i = 0; i < n1; i++) {
			polynomial2[i] = polynomial1[i] - polynomial2[i];
		}
	}
	else {
		for (i = 0; i < n2; i++) {
			polynomial1[i] = polynomial1[i] - polynomial2[i];
		}
	}
}

void multiplication(int* polynomial1, int n1, int* polynomial2, int n2,int* npolynomial,int mn) {//对多项式做乘法,修改结果数组npolynomial
	int i, sum;
	for (i = 0, sum = 0; i < mn; i++) {
		int i2=i,j;
		for (int i3=i2,j = 0; j <= i; j++) {
			if ((j < n1)&&(i3 - j < n2)) {
				sum += polynomial1[j] * polynomial2[i3 - j];
			}
		}
		npolynomial[i] = sum;
		sum = 0;

	}
}

void differential(int* polynomial, int n) {//对多项式做微分,直接修改原数组
	int i,past;
	for (i = 0; i < n; i++) {
		if (i == n - 1) {
			polynomial[i] = 0;
		}
		else if (i == 0) {
			polynomial[i]+= polynomial[i + 1] * (i + 1);
		}
		else {
			polynomial[i] = polynomial[i + 1] * (i + 1);
		}
	}
}

int main(void) {

	int polynomial1[N1] = { 1,1,1,1,1,1 };//polynomial[i]是i次项的系数
	int polynomial2[N2] = { 1,1,1,1,1 };
	int i;
	
	addition(polynomial1, N1, polynomial2, N2);
	for (i = 0; i < N1; i++) {
		printf("%d ", polynomial1[i]);
	}
	printf("\n");
	
	subtraction(polynomial1, N1, polynomial2, N2);
	for (i = 0; i < N1; i++) {
		printf("%d ", polynomial1[i]);
	}
	printf("\n");
	
	int npolynomial[MN];
	multiplication(polynomial1, N1, polynomial2, N2, npolynomial,MN);
	for (i = 0; i < MN; i++) {
		printf("%d ", npolynomial[i]);
	}
	printf("\n");

	differential(polynomial1, N1);
	for (i = 0; i < N1; i++) {
		printf("%d ", polynomial1[i]);
	}

	return 0;
}

(2)单链表实现:

2.基数排序:

3.多重表:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值