二、线性表

线性表:

  • 零个或多个数据元素的有限序列

ADT

ADT 线性表(List)
Data
	线性表的数据对象集合为{a1,a2,,an},每个元素的类型均为DataType。
	其中,
	除第一个元素a1外,每一个元素有且只有一个直接前驱元素,
	除了最后一个元素an外,每一个元素有且只有一个直接后继元素。
	数据元素之间的关系是一对一的关系。
Operation
	InitList(*L): 初始化操作,建立一个空的线性表L
	ListEmpty(L): 判断线性表是否为空表,若线性表为空,返回true,否则返回false
	ClearList(*L): 将线性表清空
	GetElem(L,i,*e): 将线性表L中的第i个位置元素值返回给e
	LocateElem(L,e): 在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败
	ListInsert(*L,i,e): 在线性表L中第i个位置插入新元素e
	ListDelete(*L,i,*e): 删除线性表L中第i个位置元素,并用e返回其值
	ListLength(L): 返回线性表L的元素个数
endADT

DEMO

// La表示A集合,Lb表示B集合。
// 把存在集合B中但不存在集合A中的元素插入到A中
void unionL(List *La, list Lb)
{
    int La_len, Lb_len, i;

    ElemType e;
    La_len = ListLength(*La);
    Lb_len = ListLength(Lb);

    for( i=1; i <= Lb_len; i++ )
    {
        GetElem(Lb, i, &e);
        if( !LocateElem(*La, e) )
        {
            ListInsert(La, ++La_len, e);
        }
    }
}

顺序存储

  • 第 i 个数据元素 ai 的存储位置可以由 a1 推算得出:LOC(ai) = LOC(a1) + (i-1)*c
  • 【随机存取】:可以随时计算出线性表中任意位置的地址
  • 存取O(1)
  • 插入、删除O(n)

存储结构

#define MAXSIZE 20              // 存取空间初始分配量
typedef int ElemType;           // 假设为int
typedef struct
{
	ElemType data[MAXSIZE];     // 数组存储数据元素,最多存储MAXSIZE个
	int length;                 // 线性表当前长度
} SqList;

GetElem

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;

// Status 是函数的类型,其值是函数结果状态代码,如OK等。
// 初始条件:顺序线性表L已存在,1 <= i <= ListLength(L)
// 操作结果:用e返回L中第i个数据元素的值。

Status GetElem(SqList L, int i, ElemType *e)
{
    if( L.length==0 || i<1 || i>L.length )
    {
        return ERROR;
    }
    *e = L.data[i-1];

    return OK;
}

ListInsert

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L)。 */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L长度+1。*/

Status ListInsert(SqList *L, int i, ElemType e)
{
    int k;

    if( L->length == MAXSIZE )  // 顺序线性表已经满了
    {
        return ERROR;
    }
    if( i<1 || i>L->length+1)   // 当i不在范围内时
    {
        return ERROR;
    }
    if( i <= L->length )        // 若插入数据位置不在表尾
    {   
        for( k=L->length-1; k >= i-1; k-- ) /* 将要插入位置后数据元素向后移动一位 */
        {
            L->data[k+1] = L->data[k];
        }
    }

    L->data[i-1] = e;          // 将新元素插入
    L->length++;

    return OK;
}

ListDelete

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度-1 */
Status ListDelete(SqList *L, int i, ElemType *e)
{
    int k;

    if( L->length == 0 )
    {
        return ERROR;
    }
    if( i<1 || i>L->length )
    {
        return ERROR;
    }

    *e = L->data[i-1];

    if( i < L->length )
    {
        for( k=i; k < L->length; k++ )
        {
            L->data[k-1] = L->data[k];
        }
    }

    L->length--;

    return OK;
}

链式存储

  • 结点:【数据域】 + 【指针域】
  • 第一个结点的存储位置叫做头指针,最后一个结点指针为空(NULL)
  • 存取 O(n)
  • 插入、删除 O(1)
头指针:
  • 头指针是指链表指向第一个结点的指针,若链表有头结点,则是【指向头结点的指针】
  • 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)
  • 无论链表是否为空,头指针均不为空
头节点:
  • 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)
  • 有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了

单链表

存储结构

typedef struct Node
{
	ElemType data;      // 数据域
	struct Node *Next;  // 指针域
} Node;
typedef struct Node *LinkList;    // 声明 LinkList 类型为指向 struct Node 的指针

GetElem

/* 操作结果:用e返回L中第i个数据元素的值 */

Status GetElem( LinkList L, int i, ElemType *e )
{
    int j;
    LinkList p;

    p = L->next;
    j = 1;

    while( p && j<i )              // p指针不为NULL
    {
        p = p->next;
        ++j;
    }

    if( !p || j>i )                // 第i个结点不存在
    {
        return ERROR;
    }

    *e = p->data;

    return OK;
}

ListInsert

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */

Status ListInsert(LinkList *L, int i, ElemType e)
{
    int j;
    LinkList p, s;

    p = *L;
    j = 1;

    while( p && j<i )                      // 用于寻找第i个结点
    {
        p = p->next;
        j++;
    }

    if( !p || j>i )                        // 第i个结点不存在
    {
        return ERROR;
    }

    s = (LinkList)malloc(sizeof(Node));    // 生成新结点
    s->data = e;

    s->next = p->next;
    p->next = s;

    return OK;
}

ListDelete

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */

Status ListDelete(LinkList *L, int i, ElemType *e)
{
    int j;
    LinkList p, q;

    p = *L;
    j = 1;

    while( p->next && j<i )  // 遍历寻找第i-1个结点
    {
        p = p->next;
        ++j;
    }

    if( !(p->next) || j>i )   // 第i个结点不存在
    {
        return ERROR;
    }

    q = p->next;
    p->next = q->next;

    *e = q->data;
    free(q);
    
    return OK;
}

CreateListHead

/* 随机产生n个元素的值,头插法建立单链表L */

void CreateListHead(LinkList *L, int n)
{
    LinkList p;
    int i;

    srand(time(0));                          // 初始化随机数种子

    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;                       // 建一个带头节点的单链表

    for( i=0; i < n; i++ )
    {
        p = (LinkList)malloc(sizeof(Node));  // 生成新结点
        p->data = rand()%100+1;              // 随机生成100以内的数字
        p->next = (*L)->next;
        (*L)->next = p;
    }
}

CreateListTail

/* 随机产生n个元素的值,尾插法建立单链表L */

void CreateListTail(LinkList *L, int n)
{
    LinkList p, r;
    int i;

    srand(time(0));
    *L = (LinkList)malloc(sizeof(Node));
    r = *L;                                 // *r指向尾部结点

    for( i=0; i < n; i++ )
    {
        p = (Node *)malloc(sizeof(Node));   // 生成新结点
        p->data = rand()%100+1;
        r->next = p;
        r = p;                 
    }

    r->next = NULL;
}

ClearList

/* 将L置空 */

Status ClearList(LinkList *L)
{
    LinkList p, q;

    p = (*L)->next;

    while(p)
    {
        q = p->next;   // 需要用q指向p指向的下一个结点
        free(p);
        p = q;
    }

    (*L)->next = NULL;

    return OK;
}

静态链表

  • 用数组描述的链表
  • 存储结构是链式存储
  • 【备用链表】:未被使用的数组元素
  • 【数组一个元素】:存放备用链表第一个结点下标
  • 【数组最后元素】:存放第一个插入元素的下标,链表为空存放0

存储结构

#define MAXSIZE 1000    // 假设链表最大长度1000
typedef struct
{
	ElemType data;     // 数据
	int cur;           // 游标(Cursor)
} Component, StaticLinkList[MAXSIZE];

InitList

/* 初始化链表,将一维数组space中各分量链成一个备用链表 */

Status InitList(StaticLinkList space)
{
	int i;
	for( i=0; i < MAXSIZE-1; i++ ){
		space[i].cur = i + 1;
	}
	space[MAXSIZE-1].cur = 0;  // 链表为空,最后一个元素cur为0
	return OK;
}

ListInsert

/* 若备用链表非空,放回分配的结点下标,否则返回0 */

int Malloc_SLL(StaticLinkList space)
{
	int i = space[0].cur;          // 当前数组第一个元素的cur
	if( space[0].cur ){
	space[0].cur = space[i].cur;   // 把它的下一个分量用来作为备用
	}                         
	return i;
}

/* 在静态链表L中第i个元素之前插入新的数据元素e */

Status ListInsert( StaticLinkList L, int i, ElemType e )
{
    int j, k, l;

    k = MAX_SIZE - 1;              // k首先赋值为数组的最后一个元素的下标
    if( i<1 || i>ListLength(L)+1 )
    {
        return ERROR;
    }

    j = Malloc_SLL(L);             // 获得空闲分量的下标
    if( j )
    {
        L[j].data = e;
        for( l=1; l <= i-1; l++ )  
        {
            k = L[k].cur;          // 找到第i个元素之前的位置
        }
        L[j].cur = L[k].cur;       // 正式插入
        L[k].cur = j;

        return OK;
    }
    
    return ERROR;
}

ListDelete

/* 删除在L中的第i个数据元素 */
Status ListDelete(StaticLinkList L, int i)
{
    int j, k;

    if( i<1 || i>ListLength(L) )
    {
        return ERROR;
    }

    k = MAX_SIZE - 1;

    for( j=1; j <= i-1; j++ )
    {
        k = L[k].cur;    // 找到第i-1个元素
    }

    j = L[k].cur;        // i的位置赋给j
    L[k].cur = L[j].cur; // i的游标赋给i-1的游标

    Free_SLL(L, j);

    return OK;
}

/* 将下标为k的空闲结点回收到备用链表 */
void Free_SLL(StaticLinkList space, int k)
{
    space[k].cur = space[0].cur;    // k放到空闲链表的表头
    space[0].cur = k;
}

ListLength

/* 返回L中数据元素个数 */
int ListLength(StaticLinkList L)
{
    int j = 0;
    int i = L[MAXSIZE-1].cur;

    while(i)
    {
        i = L[i].cur;
        j++;
    }

    return j;
}

循环链表

  • 带尾指针(rear)的循环链表 查找终端结点\开始结点 O(1)

存储结构

/*链表存储结构的定义*/
typedef struct CLinkList
{
    int data;
    struct CLinkList *next;
}node;

ds_init

/*初始化循环链表*/
void ds_init(node **pNode)
{
    int item;
    node *temp;
    node *target;

    printf("输入结点的值,输入0完成初始化\n");

	while(1)
	{
        scanf("%d", &item);
        fflush(stdin);

		if(item == 0)
            return;

        if((*pNode) == NULL)
		{ /*循环链表中只有一个结点*/
			*pNode = (node*)malloc(sizeof(struct CLinkList));
			
			if(!(*pNode))
				exit(0);
			
			(*pNode)->data = item;
			(*pNode)->next = *pNode;
		}
        else
		{
            /*找到next指向第一个结点的结点*/
            for(target = (*pNode); target->next != (*pNode); target = target->next)
				;

            /*生成一个新的结点*/
            temp = (node *)malloc(sizeof(struct CLinkList));

			if(!temp)
				exit(0);

			temp->data = item;
            temp->next = *pNode;
            target->next = temp;
        }
    }
}

ds_insert

/*插入结点*/
/*参数:链表的第一个结点,插入的位置*/
void ds_insert(node **pNode , int i)
{
    node *temp;
    node *target;
    node *p;
    int item;
    int j = 1;

    printf("输入要插入结点的值:");
    scanf("%d", &item);

    if(i == 1)
	{ //新插入的结点作为第一个结点
        temp = (node *)malloc(sizeof(struct CLinkList));

		if(!temp)
            exit(0);

		temp->data = item;

        /*寻找到最后一个结点*/
        for(target = (*pNode); target->next != (*pNode); target = target->next)
			;

		temp->next = (*pNode);
        target->next = temp;
        *pNode = temp;
    }
    else
	{
        target = *pNode;

		for( ; j < (i-1); ++j )
		{
			target = target->next;
		}  
		
		// target指向第三个元素的
		
		temp = (node *)malloc(sizeof(struct CLinkList));

		if(!temp)
            exit(0);

		temp->data = item;
		
        p = target->next;
        target->next = temp;
        temp->next = p;
    }
}

ds_delete

/*删除结点*/
void ds_delete(node **pNode, int i)
{
    node *target;
    node *temp;
    int j = 1;

    if(i == 1)
	{ //删除的是第一个结点
        /*找到最后一个结点*/
        for(target = *pNode; target->next != *pNode;target = target->next)
			;

		temp = *pNode;
        *pNode = (*pNode)->next;
        target->next = *pNode;
        free(temp);
    }
    else
	{
        target = *pNode;

		for( ; j < i-1; ++j)
		{
			target = target->next;
		}
		
		temp = target->next;
        target->next = temp->next;
        free(temp);
    }
}

ds_search

/*返回结点所在位置*/
int ds_search(node *pNode, int elem)
{
    node *target;
    int i = 1;

    for(target = pNode; target->data != elem && target->next != pNode; ++i)
	{
		target = target->next;
	}
	
	if(target->next == pNode) /*表中不存在该元素*/
        return 0;
    else
        return i;
}

带尾指针的循环链表的合并

//假设A,B为非空循环链表的尾指针
LinkList Connect(LinkList A,LinkList B)
{	
	LinkList p = A->next;		//保存A表的头结点位置
	
	A->next = B->next->next;	//B表的开始结点链接到A表尾
	
	free(B->next);	//释放B表的头结点,初学者容易忘记
	
	B->next = p;		
	
	return B;		//返回新循环链表的尾指针
} 

双向链表

存储结构

typedef struct DualNode
{
	ElemType data;
	struct DualNode *prior;  //前驱结点
	struct DualNode *next;   //后继结点
} DualNode, *DuLinkList;

插入

s->prior = p;	
s->next = p->next;	
p->next->prior = s;	
p->next = s;            // 必须放在2,3步之后

删除

p->prior->next = p->next;
p->next->prior = p->prior;	
free(p);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值