C语言数据结构之线性表(续)

0  前言

          上一讲我们讲解了线性表的顺序存储,但顺序存储具有插入和删除操作时都需要进行数据元素的移动,而链式存储就不存在这样的问题。链式存储的现行表也称为链表,它使用节点来存储数据元素,节点的地址可以连续,也可以不连续。而链表又可分为单链表、双链表和循环链表,这里我们将主要介绍单链表的使用。


1 单链表的数据结构

           单链表的节点由数据域和指针域两部分组成,数据域用来存储数据元素本身,指针域用来存储下一节点地址,也就是直接后继指针。每个链表都有一个头指针,通过它可以找到链表的第一个节点,然后又可以通过该节点的指针域找到它的后继节点。由于最后一个节点没有后继节点,因此它的指针域为NULL。

单链表结构示意图如下所示,其中左图为带头节点单链表,右图为不带头节点的单链表

                                  

 单链表节点的定义如下所示:                            

struct node
{
	ElemType data;
	struct node *next;
};
其中,ElemType为数据元素的类型,其选取由需要而定,next为指向结构体node的指针,也就是指向链表节点的指针。


2  单链表的创建

现在我们编写代码实现一个单链表的创建,要求从键盘重复读入字符作为新节点的数据元素存储到链表中

/*
 * =====================================================================================
 *
 *       Filename:  linkList.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  28/08/14 09:43:48
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  xinyi61 (xinyi61), [email protected]
 *        Company:  cqu
 *
 * =====================================================================================
 */

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

struct node
{
	char data;
	struct node *next;
};

typedef struct node LinkList;

LinkList *createLinkList()
{
	char ch;
	LinkList *p, *p1, *p2;
	
	p = (LinkList *)malloc(sizeof(LinkList));
	p->data = '\0';
	p->next = NULL;
	p1 = p;

	printf("init linklist,please input a string:");
	while((ch = getchar()) != '\n')
	{
		p2 = (LinkList *)malloc(sizeof(LinkList));
		p2->data = ch;
		p2->next = NULL;

		p1->next = p2;
		p1 = p2;
	}
	
	return p;
}

int main(int argc, char *argv[])
{
	LinkList *p, *q;
	p = createLinkList();
	q = p->next;

	printf("\nthe element is:");
	while(q)
	{
		printf("%c",q->data);
		q = q->next;
	}
	printf("\n");

	return 0;
}
其效果如下:

                                     

至此,链表就创建完成了,接下来我们看一下链表的查找节点、插入节点、删除节点以及链表表长的相关分析和代码实现。

3  单链表节点的查找

            链表是不支持随机节点访问的,这就使得查找数据元素时需要对节点逐个进行扫描,查找可以分为按序号查找和按值进行查找,这里将分别进行介绍

3.1   按序号查找

         单链表查找的结构示意图如下

                                    

LinkList *searchByNum(LinkList *p,int n)
{
	int i = 1;
	LinkList *q;
	q = p;
	while(q != NULL)
	{
		if(n == i)
		{
			return p;
		}
		++i;
		q = q->next;
	}

	return NULL;
}

该函数在指针p指向的单链表中查找第n个节点,如果存在,就返回指向该节点的指针,否则就返回NULL

3.2   按数据元素查找如下

LinkList *searchByVal(LinkList *p,char ch)
{
	LinkList *q;
	q = p;
	while(q != NULL)
	{
		if(ch == q->data;)
		{
			return p;
		}
		q = q->next;
	}

	return NULL;
}
该函数在指针p指向的单链表中查找数据元素为ch的节点,如果存在,就返回指向该节点的指针,否则就返回NULL

 4  插入节点

        在单链表中插入新的节点时,首先将插入位置后继节点的指针赋给新节点的指针域,然后在将插入位置的指针域更改为新节点的指针。其操作示意图如下所示

                                                                         

以下是对应的代码实现

int insertNewNode(LinkList *p, int n, char ch)
{
	LinkList *p1,*p2;
	p1 = searchByNum(p, n-1);
	if(NULL == p1)
	{
		printf("insert new node error\n");
		return -1;
	}
	p2 = (LinkList *)malloc(sizeof(LinkList));
	p2->data = ch;
	p2->next = p1->next;//1
	p1->next = p2;

	return 0;
}
在这个函数中,参数p为单链表的头指针,参数n为插入的位置,参数ch为要插入的新数据元素,函数成功则返回0,否则就返回-1.

5  删除节点

       当从单链表中删除节点时,只需要将将要删除的节点的直接前驱指针指向其直接后继即可,其操作如图所示

                                                          

int deleteNode(LinkList *p, int n)
{
	LinkList *p1,*p2;
	p1 = searchByNum(p, n-1);
	if(NULL == p1)
	{
		printf("insert new node error\n");
		return -1;
	}

	p2 = p1->next;
	p1->next = p2->next;
	free(p2);

	return 0;
}
在这个函数中,参数p为单链表的头指针,参数n为要删除节点的位置,函数成功则返回0,否则就返回-1.

6 链表长度的计算

      链表长度计算分为带头节点的和不带头节点的,其代码实现分别如下

//dai tou jie dian de dan lian biao
int listLength(LinkList l)
{
	LinkList *p = l;
	int length = 0;
	while(p->next)
	{
		p = p->next;
		++length;
	}
	
	return length;
}

//bu dai tou jie dian de dan lian biao
int listLength(LinkList l)
{
	LinkList *p = l;
	int length = 0;
	if(NULL == p)
	{
		return 0;
	}
	length = 1;
	while(p->next)
	{
		p = p->next;
		++length;
	}
	
	return length;
}

  到此关于单链表的创建、查找、删除、求表长就全部讲完了。

备注:

        这是微软面试开发工程师的一个常见问题:把一个字符串转换为整数。我们可以这样一个简单的实现

int strToInt(char *string)
{
	int number = 0;
	while(*string != 0)
	{
		number = number * 10 + *string - '0';
		++string;
	}

	return number;
}
是否这个题就这样OK啦?答案显而易见是否定的。这个题目表面上看起来很简单,但是随着我们解题的深入,我们发现除了完成基本功能以外,我们还应该考虑到边界条件,错误处理等各个方面的问题。这样以来这就不是个简单的问题了。首先我们应该考虑 输入的字符串中是否有非数字字符和正负号,其次还应考虑到最大的正整数和最小的负整数以及溢出问题,最后还应该考虑当输入的字符串不能转换为整数时,我们将做怎样的错误处理。





猜你喜欢

转载自blog.csdn.net/xxboy61/article/details/38893085