单链表

本文详细介绍了链表这一基本数据结构的实现与操作,包括初始化、创建、遍历、求长、判断空、查找、删除及插入等核心方法,并提供了具体的程序代码。

引言:


       数据结构中,很重要的一个结构就是链表。链表虽然简单,但却能很好的考查程序员的基本功,因此在很多面试、笔试中会直接或间接的考查它。下面就对链表进行系统的复习。


分析描述:


       下面就对链表的构建、删除、插入、查找、求长等操作进行系统描述。并给出程序代码。其中结点的定义如下:

typedef int ElementType;
typedef struct ListNode{
	ElementType data;
	struct ListNode *NextNode;
}ListNode, *PListNode;

       ◆◆◆◆◆链表的初始化。一般认为链表是由头结点开始,后面跟着的各个结点。因此,创建链表之前,可以先创建一个头结点。头结点的结构可以如图表示,。由图可以看出来,头结点的指针为空指针"NULL"。前面头结点的前面可以存放链表长度等信息。

PListNode InitList(void)
{
	PListNode L;

	L = (PListNode)malloc(sizeof(ListNode));
	if(L == NULL){
		fprintf(stderr, "init list fail.\n");	
		exit(EXIT_FAILURE);
	}
	L->NextNode = NULL;

	return L;
}

       此处,只是创建了一个头结点L,并让其NextNode指向了空指针NULL。并未对它的data进行赋值。该程序要注意的一点是L->NextNode = NULL语句。


       链表的创建。链表的创建就是从链表的头结点开始,往链表中添加结点元素。如图所示:

PListNode CreateList(PListNode L)
{
	PListNode TmpNode = (PListNode)malloc(sizeof(ListNode));	
	if(TmpNode == NULL){
		fprintf(stderr, "malloc list node fail.\n");	
		return L;
	}

	while(scanf("%d", &TmpNode->data) != 0){

		TmpNode->NextNode = L->NextNode;
		L->NextNode = TmpNode;

		TmpNode = (PListNode)malloc(sizeof(ListNode));
		if(TmpNode == NULL){
			fprintf(stderr, "malloc list node fail.\n");	
			return L;
		}
	}
	free(TmpNode);

	return L;
}

        注意,上面的一段代码有些技巧,比如while(scanf("%d", &TmpNode->data) !=  0)就是利用了scanf函数的返回值,另外,在while之前先分配结点空间,最后while之后用free释放多分配的一个空间(因为scanf函数如果失败,就用不到最后分配的空间内存了,为了规范代码,应该用free释放掉这块内存)。


        链表的遍历。链表的遍历就是指对从链表的头结点开始,依次输出链表中各个结点的值。

PListNode TraverseList(const PListNode L)
{
	PListNode TmpNode = L;

	if(TmpNode->NextNode == NULL){
		printf("ListNode is empty.\n");
		return;
	}

	while(TmpNode->NextNode != NULL){
		printf("%d ", TmpNode->NextNode->data);
		TmpNode = TmpNode->NextNode;
	}
	putchar('\n');
	return L;
}

          注意:这段代码容易有一个极易出错的地方:因为传给函数的参数是链表的指针,所以在函数内部应该先用PListNode TmpNode = L语句将指针赋值给临时结点指针,否则如果直接用L->NextNode = L->NextNode->NextNode会使得链表的结构破坏。


         链表的求长。求出所给链表的长度,即链表中除头结点之外的其他结点的个数。

int ListLength(PListNode L)
{
	int count = 0;
	PListNode Tmp = L;
	while(Tmp->NextNode != NULL){
		Tmp = Tmp->NextNode;
		count++;	
	}
	return count;
}

        该段代码同样应该注意PListNode Tmp = L语句,以免破坏链表。


        判断链表是否为空。如果链表为空,即链表只包含头结点,其NextNode指针应该为NULL,因此程序很简单。

int IsEmpty(PListNode L)
{
	return (L->NextNode == NULL);
}


       链表中查找某元素的位置。在给定的链表中查找某个给定值是否位于链表中,如果在链表中,就给出具体的位置。如果不存在链表中,就返回0,以表示未找到该元素。

int FindPosition(ElementType data, PListNode L)
{
	int Index = 0;
	PListNode TmpNode = L;

	while(TmpNode->NextNode != NULL){
		Index++;
		if(TmpNode->NextNode->data == data)
			return Index;	
		TmpNode = TmpNode->NextNode;
	}	

	return 0;
}

        删除链表中的元素。删除链表中某个存在的结点,如果结点不存在,则不做任何事。

PListNode Delete(ElementType data, PListNode L)
{
	PListNode TmpNode = L;
	
	if(TmpNode->NextNode == NULL){
		printf("the list you give is an empty list.\n");	
		return L;
	}

	while((TmpNode->NextNode != NULL) && (TmpNode->NextNode->data != data)){
		TmpNode = TmpNode->NextNode;
	}

	if(TmpNode->NextNode == NULL){
		printf("there is no elementtype %d.\n", data);	
		return L;
	}

	TmpNode->NextNode = TmpNode->NextNode->NextNode;

	return L;
	
}

        链表的插入。给定一个结点的值,将它插入到链表中的某个位置处。

PListNode Insert(ElementType data, PListNode L, int Index)
{
	int index = 0;
	PListNode TmpList = L;

	if(L == NULL || Index < 0 || Index > ListLength(L)){
		printf("the input element wrong.\n");	
		return;
	}
			

	PListNode TmpNode = (PListNode)malloc(sizeof(ListNode));
	if(TmpNode == NULL){
		printf("malloc memory fail.\n");	
		return;
	}
	TmpNode->data = data;

	for(index = 0; index < Index; index++){
		TmpList = TmpList->NextNode;		
	}
	TmpNode->NextNode = TmpList->NextNode;
	TmpList->NextNode = TmpNode;

	return L;
}

         注意,对于要插入的操作,一定要明白是插入到某个位置的前面还是插入到某个位置的后面。


         链表的删除。删除链表就是将链表中的结点全部释放掉,即释放掉除头结点以外所有结点的内存空间。

PListNode DeleteList(PListNode L)
{
	PListNode Tmp1, Tmp2;

	Tmp1 = L->NextNode;
	while(Tmp1){
		Tmp2 = Tmp1->NextNode;
		free(Tmp1);
		Tmp1 = Tmp2;
	}
	L->NextNode = NULL;

	return L;
}


          总结:


       1、对于链表中的某些操作,应该特别注意对内存指针的使用。一定要清楚应该何时使用malloc来获取一个新的单元。要记住,声明指向一个结构的指针并不创建该结构,而只是给出足够的空间容纳结构可能会使用的地址。

       2、由于在给链表相关的函数传递参数时,经常传递的是指针,这样很有可能导致指针所指向的位置发生改变,一个好的解决办法就是在函数内,创建临时的指针变量,使其与指针参数相等。指针参数作为返回值返回给调用函数,这样就能保证指针参数不会因为函数内部的某些动作而修改。

       3、链表的这些操作是非常基础的,在面试笔试题中经常会直接或间接的考查到,因此一定要牢牢掌握。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值