欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

链式表示的线性表之一——单链表1

程序员文章站 2022-06-06 13:38:03
...

所谓线性表的链式存储,就是采用一组任意的存储单元存放线性表的元素。这组存储单元可以是连续的,也可以是不连续的。为了表示每个元素ai与其直接后继ai+1的逻辑关系,除了存储元素本身的信息外,还需要存储一个指示其直接后继元素的信息(直接后继元素的地址)。这两部分构成的存储结构称为结点(node)。即结点包括两个域:数据域和指针域。结点结构如图所示。

链式表示的线性表之一——单链表1

 

通过指针域将线性表中n个结点元素按照逻辑顺序链在一起就构成链链表。

一般情况下,我们只关心链表中结点的逻辑顺序,而不关心它的实际存储位置。通常用箭头表示指针,把链表表示成通过箭头链接起来的序列。线性表(Kang,Geng,Guan,Chen,Zhou,Hua,Feng)如图所示。

链式表示的线性表之一——单链表1

为了操作上的方便,在单链表的第一个结点之前增加一个结点,称之为头结点。头结点的数据域可以存放如线性表的长度等信息,头结点的指针域存放第一个元素结点的地址信息,使其指向第一个元素结点。带头结点的单链表如图所示。

链式表示的线性表之一——单链表1

若带头结点的单链表为空链表,则头结点的指针域为“空”,如图所示。

链式表示的线性表之一——单链表1

【存储结构】

/*02_01——存储结构*/
typedef struct Node
{
	DataType data;
	struct Node *next;
}ListNode, *LinkList;/*ListNode是链表的结点类型,LinkList是指向链表结点的指针类型*/

【基本运算】
【初始化单链表】

/*02_01——初始化单链表*/
void InitList(LinkList *head)
{
	if ((*head=(LinkList)malloc(sizeof(ListNode)))==NULL)
	{
		/*02_01为头结点分配一个存储单元*/
		exit(-1);
	}
	(*head)->next = NULL;/*02_01将单链表的头结点指针域置为空 */

}

【判断单链表是否为空】

/*02_02判断单链表是否为空*/
int ListEmpty(LinkList head)
{
	if (head->next==NULL)/*02_02如果单链表头结点的指针域为空*/
	{
		return 1;        /*02_02返回1*/
	}
	else
	{
		
		return 0;        /*02_02返回1*/
	}
}

【按照序号查找】

/*02_03按照序号查找*/
ListNode *Get(LinkList head, int i)
/*02_03按照序号查找单链表中第i个结点,查找成功返回该结点的指针表示成功;否则返回NULL表示失败*/
{
	ListNode *p;
	int j;
	if (ListEmpty(head))
	{
		return NULL;
	}
	if (i<1)
	{
		return NULL;
	}
	j = 0;
	p = head;
	while (p->next!=NULL&&j<i)
	{
		p = p->next;
		j++;

	}
	if (j==i)
	{
		return p;

	}
	else
	{
		return NULL;
	}

}

【查找元素值为e的结点】


/*02_04查找元素值为e的结点的元素,若查找成功则返回对应元素的结点指针,否则返回NULL表示失败*/
ListNode *LocateElem(LinkList head, DataType e)
{
	ListNode *p;
	p = head->next;
	while (p)
	{
		if (p->data!=e)
		{
			p = p->next;
		}
		else
		{
			break;
		}
	}
	return p;
}

【确定元素所在单链表中的序号】


/*02_05定位操作,确定元素所在单链表中的序号*/
/*02_05从头指针出发依次访问每个结点,并将结点的值与e比较,如果相等,则返回该结点序号表示成功*/
/*02_05如果没有与e相等的元素则返回0表示失败*/

int LocatePos(LinkList head, DataType e)
{
	ListNode *p;
	int i;
	if (ListEmpty(head))/*02_05在查找第i个元素之前,判断链表是否为空*/
	{
		return 0;
	}
	p = head->next;    /*02_05指针指向第一个结点*/
	i = 1;
	while (p)
	{
		if (p->data==e) /*02_05找到与e相等的元素*/
		{
			return i;   /*02_05返回该序号*/
		} 
		else
		{
			p = p->next;
			i++;
		}
	}
	if (!p)       /*02_05如果没有找到与e相等的元素*/
	{
		return 0; /*返回0*/
	}
}

【在第i个位置插入元素e】

先来看看如何在单链表中插入一个节点。假设存储元素e的结点为p,要将p指向的结点插入到pre和pre->head之间,无需移动结点元素,只需要改变p和pre指针指向即可。先把*p变成*pre的直接后继结点,如图所示,代码如下:

链式表示的线性表之一——单链表1

p->next=pre->next;
pre->next=p;

链式表示的线性表之一——单链表1

注意:
插入结点的两行代码不能颠倒顺序。如果先进行pre->next=p;后进行p->next=pre->next;操作,则第一条代码就会被覆盖掉pre->next的地址,pre->next的地址就会变成p的地址,这样pre->next就相当与上一级断开了链接。如图

链式表示的线性表之一——单链表1


在单链表的第i个位置插入一个新元素e的步骤如下:

step1.在链表中找到其直接前驱结点,即第i-1个结点,并由指针pre指向该结点,如图所示:

链式表示的线性表之一——单链表1


step2.申请一个新结点空间,由p指向该结点,将值e赋值给p指向结点的数据域。

step3.修改*p和*pre结点的指针域,如图所示

链式表示的线性表之一——单链表1

/*02_06在第i个位置插入元素e*/

int InsertList(LinkList head, int i, DataType e)
/*02_06在单链表中的第i个位置插入一个结点,结点的元素值为e。插入成功返回1,失败返回0.*/
{
	ListNode *pre, *p;/*02_06定义第i个元素的前驱结点指向pre,指针p指向新生成的结点*/
	int j;
	pre = head;/*02_06指针p指向头结点*/
	j = 0;
	while (pre->next != NULL&&j < i - 1)
		/*02_06找到第i-1个结点,即第i个结点的前驱结点*/
	{
		pre = pre->next;
		j++;
	}
	if (j!=i-1)/*02_06如果没有找到说明插入位置错误*/
	{
		cout << "02_06插入位置错误!" << endl;
		return 0;
	}
	/*02_06新生成一个结点,并将e赋值给该结点的数据域*/
	if ((p=(ListNode*)malloc(sizeof(ListNode)))==NULL)
	{
		exit(-1);
	}
	p->data = e;
	/*02_06插入结点操作*/
	p->next = pre->next;
	pre->next = p;
	return 1;

}

【删除第i个结点】

先来看看如何删除链表的第i个结点。假设p指向第i个结点,要将*p结点删除,只需将它的直接前驱结点的指针绕过,使其直接指向它的直接后继结点即可。如图所示:

链式表示的线性表之一——单链表1

删除第i个结点的步骤如下:

step1.找到第i个结点的直接前驱结点,即第i-1个结点,并用pre指向该结点,p指向其直接后继结点,即第i个结点,如图所示

链式表示的线性表之一——单链表1

step2.将*p结点的数据域赋值给e;

step3.删除第i个结点,并释放*p结点的内存空间。过如图

链式表示的线性表之一——单链表1

/*02_07删除第i个结点*/

int DeleteList(LinkList head, int i, DataType *e)
/*02_07删除单链表中的第i个结点;删除成功返回1,失败返回0。*/
{
	ListNode *pre, *p;
	int j;
	pre = head;
	j = 0;
	while (pre->next!=NULL&&pre->next->next!=NULL&&j<i-1)
	/*02_07判断是否找到前驱结点*/
	{
		pre = pre->next;
		j++;

	}
	if (j!=i-1)/*02_07如果没有找到要删除的节点位置,说明删除有误*/
	{
		cout << "02_07删除位置有误" << endl;
		return 0;
	}
	/*02_07指针p指向单链表中的第i个结点,并将该结点的数据域赋值给e*/
	p = pre->next;
	*e = p->data;
	/*02_07将前驱结点的指针域指向要删除结点的下一个结点,也就是将p指向的结点与单链表断开*/
	pre->next = p->next;
	free(p); /*02_07释放p指向的结点*/
	return 1;

}

【求表长的操作】

/*02_08求表长的操作*/
int ListLength(LinkList head)
{
	ListNode *p;
	int count = 0;
	p = head;
	while (p->next!=NULL)
	{
		p = p->next;
		count++;

	}
	return count;
}

【销毁链表操作】


/*02_09销毁链表操作*/

void DestoryList(LinkList head)
{
	ListNode *p, *q;
	p = head;
	while (p!=NULL)
	{
		q = p;
		p = p->next;
		free(q);
	}
}

 

相关标签: 单链表