C语言单链表-增强

C语言创建单链表-入门部分的代码存在一系列问题,现在在其基础上写了第二版,改进了入门部分代码的问题并增强了其功能。

代码在“C语言网”为:
http://bbs.cyuyan.com.cn/thread-5310-1-1.html

#include <stdio.h> 
#include <malloc.h> 
#include <string.h> 
#include <ctype.h>
#include <process.h> 

/************************************************************************
 * Name 		:	linked_list(单链表)
 * Author      	:	luchunli
 * Date		  	:	2012-03-20
 * Description 	:	对链表的基本操作,如链表的创建、查找、插入、删除、翻转等
 * Environment	:	Turbo C/C++ for windows集成实验域学习环境2007.5
 * Functions	:	void help (void)
 *					void print (const struct node * head) 
 *					int getNo (void)	
 *					int isExists (struct node * q, int no)	
 *					struct node * create (struct node * head)	
 *					void queryByNo (const struct node * head)	
 *					void queryByName (const struct node * head)	
 * 					struct node * insert (struct node * head)
 * 					struct node * delete (struct node * head)
 * 					struct node * reverse (struct node * head)
 * ----------------------------------------------------------------------
 * Time				Modifier				Comment
 * 2012/03/22		luchunli				容错处理和功能扩展(链表翻转)
 * 
*************************************************************************/

#define TRUE  1
#define FALSE 0

static const int ZERO = 0;

/*
*  声明一个结构体,名称为node
*/
struct node {
	int no;
	char name[20];
	struct node * next;
};

/**
* Author      :  luchunli
* Description :  帮助
* Date		  :  2012-03-22
*/
void help (void) {	
	printf("1、新建链表(create)\n");		
	printf("2、根据编号查找(q_by_no)\n");		
	printf("3、根据名称查找(q_by_name)\n");		
	printf("4、插入指定编号的节点(insert)\n");		
	printf("5、删除指定编号的节点(delete)\n");		
	printf("6、链表翻转(reverse)\n");
	printf("7、查看链表节点数据(print)\n");
	printf("8、清除屏幕显示(clear)\n");
	printf("9、退出系统(exit)\n");
	printf("10、帮助信息(?)\n");		
}

/**
* Author      :  luchunli
* Date		  :  2012-03-21
* Description :  打印链表的数据
*/
void print (const struct node * head) {	
	struct node * q = head;	/**/	
	while(NULL != q) {
		printf("no is %d and name is '%s'.\n", q->no, q->name);
		q = q->next;
	}	
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  获取编号(执行校验或得到一个有效的编号)
* 
	关于scanf函数返回值的问题:
	1、若函数中只存在一种输入,即scanf("%d", &m),如果输入成功返回值为1,不成功返回值为0;
	2、若函数中存在多种输入,即scanf("%d %d %d", &m, &n, &k),
		如果输入的数据a为整数,b、c为无效数据,则返回1;
		如果输入的数据a、b为整数,c为无效数据,则返回2;
		如果输入的数据a、b、c均为整数,则返回3;
		如果a、b、c均为无效数据,则返回0。
	
	问题一:
		当我们执行scanf("%d", &m),但是如果我的输入为非数字的话(如一个字符),则会出现刷屏现象,
		即屏幕不停的滚动,这是因为%d不能接收的缘故。
		
		解决方式:
			使用k = scanf("%d", &m);然后判断k的值
		
	问题二:
		使用上述方式可以解决刷屏问题,但是当前输入的字符会影响下次的输入,因为键盘缓冲区就可能还有残余信息。
		
		解决方式:
			使用fflush(stdin);刷新缓冲区,可以清理掉标准输入流中的缓存数据;同理,标准输出流为stdout。
			
		扩展:
			scanf()函数应该只是扫描stdin流。
	
	问题三:
		使用scanf("%c", &c);时\n和\w等都会赋值给字符c
	 
*/
int getNo (void) {
	int no = ZERO;
	while(!scanf("%d", &no) || no < ZERO) {		/*保证输入的是有效的整数(而非字符或负数)*/
		fflush(stdin);				/*刷新缓冲区*/
		printf("编号输入错误,请输入编号(整数(1~65535)):");
	}
	return no;
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  判定指定编号的节点是否已存在
*/
int isExists (struct node * q, int no) {
	while(q) {
		if (q->no == no) {
			return TRUE;
		}
		q = q->next;
	}
	return FALSE;
}

/**
* Author      :  luchunli
* Date		  :  2012-03-21
* Description :  新建链表
* 	
	现在暂未使用该种创建链表的方式,因为这种方式在创建时每次都在最后添加新的节点,
	而不是根据编号的大小来创建有序的链表,详见insert()方法。
*/
struct node * create (struct node * head) {
	/*定义局部结构体类型的变量*/
	struct node * p = NULL;
	struct node * q = NULL;
	
	/*定义局部整形变量*/
	int no = 0;	
		
	/*获取编号*/
	printf("请输入节点编号(输入0表示结束):");	
	no = getNo();
	
	while (ZERO != no) {		
		p = (struct node *)malloc(sizeof(struct node));
		if (NULL == p) {
			printf("内存空间不足,分配失败!");
			exit(1);
		}
		
		/**/
		p->no = no;
		printf("请输入编号 %d 处的名称:", p->no);
		scanf("%s", p->name);
		printf("\n");
		
		/*每次有新的节点时都链接到链表的末尾*/
		if (NULL == head) {
			head = p;
		} else {
			q->next = p;
		}
		q = p;
		q->next = NULL;	/**/
		
		/**/		
		printf("请继续输入编号(输入0表示结束):");
		no = getNo();
		
		while(isExists(head, no)) {
			printf("编号已存在,请重新输入:");
			no = getNo();
		}
	}
	
	printf("\n");
		
	return head;
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  从链表中查找特定编号的节点
*/
void queryByNo (const struct node * head) {	
	struct node * q = NULL;
	
	int no = ZERO;
	
	printf("请输入查找的节点编号(输入0表示结束):");	
	no = getNo();
		
	while (ZERO != no) {
		q = head; 		/*必须的*/
		while(NULL != q) {
			if (q->no == no) {
				printf("编号为 %d 处的节点名称为:%s\n", q->no, q->name);
				break;
			}
			q = q->next;
		}
		
		if (NULL == q) {	/*找到链表最后都没有找到该编号的节点*/
			printf("编号为 %d 的节点不存在.\n", no);
		}
				
		/**/
		printf("请继续输入查找的节点编号(输入0表示结束):");		
		no = getNo();
	}
	
	printf("\n");	
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  从链表中查找特定名称的节点
*/
void queryByName (const struct node * head) {
	struct node * q = NULL;
	
	char name[20] = {'\0'};
	
	int flag = ZERO;
	
	printf("请输入查找的节点名称(输入0表示结束):");
	
	scanf("%s", name);
	
	while(ZERO != strcmp(name, "0")) {	/*相等返回0,结束while循环*/
		q = head; 		/*必须的*/
		flag = 0;
		while(NULL != q) {
			if (0 == strcmp(name, q->name)) {
				printf("名称为 %s 处的节点为:%d\n", q->name, q->no);
				if (!flag) {
					flag = 1;		/*找到了*/
				}
				/*可能不同编号的节点有相同的名称*/
				/*break;*/
			}
			q = q->next;
		}
		
		if (!flag) {
			printf("名称为 %s 的节点不存在.\n", name);
		}
		
		/**/
		printf("请输入继续查找的节点的名称(输入0表示结束):");		
		scanf("%s", name);
	}	
	
	printf("\n");
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  向链表中指定位置处插入指定编号的节点
*/
struct node * insert (struct node * head) {
	struct node * q = NULL;		/*遍历链表时指向不同的节点*/
	struct node * p = NULL;		/*指向每次分配的空间*/
	struct node * temp = NULL;
	
	int no = ZERO;
	
	printf("请输入新节点编号(输入0表示结束):");	
	no = getNo();
	
	while(isExists(head, no)) {
		printf("编号已存在,请重新输入:");
		no = getNo();
	}
	
	while (ZERO != no) {		
		p = (struct node *)malloc(sizeof(struct node));
		if (NULL == p) {
			printf("内存空间不足,分配失败!");
			exit(1);
		}
		
		p->no = no;
		printf("请输入编号为 %d 处的节点的名称:", no);
		scanf("%s", p->name);
		
		if (NULL == head) {		/*空链表*/
			head = p;
		} else {
			if (no < head->no) {	/*放在头节点之前*/
				p->next = head;
				head = p;
			} else{		/*放在头节点之后*/
				q = head;
				temp = q->next;		/*需要用temp来比较,而q节点记录其前一个节点,以便新节点接入链表*/
				while (NULL != temp) {
					 if (no < temp->no) {
					 	p->next = q->next;
					 	q->next = p;
					 	q = NULL;		/*在链表最后节点之前找到新则插入新节点,同时使p指向NULL,否则说明找到最后都没有找到*/
					 	break;
					 }
					 q = temp;
					 temp = q->next;
				}
				if (NULL != q) {		/*p不为空说明在链表中未找到插入的合适位置,在最后插入*/
					q->next = p;
					q = p;
					q->next = NULL;
				}
			}
		}
		
		/*输出链表*/
		printf("\n");
		print (head);
		printf("\n");
		
		printf("请继续输入新节点编号(输入0表示结束):");	
		no = getNo();
		while(isExists(head, no)) {
			printf("编号已存在,请重新输入:");
			no = getNo();
		}
	}
	
	printf("\n");
	
	return head;
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  从链表中删除特定编号的节点
*/
struct node * delete (struct node * head) {
	struct node * q = NULL;			/*遍历链表时指向不同的节点*/
	struct node * temp = NULL;	
	
	int no = ZERO;	
	
	printf("请输入删除的节点编号(输入0表示结束):");	
	no = getNo();
	while (ZERO != no && !isExists(head, no)) {
		printf("编号不存在,请重新输入:");
		no = getNo();
	}
	
	while (ZERO != no) {
		if (NULL == head) {				/*保证q = head;和q->next处不会报地址访问错误*/	
			printf("当前链表为空!\n");
		} else {
			q = head;
			if (no == head->no) {	/*要删除的不是第一个节点*/
				/*q = head;就是为了后面的free(p);*/
				head = head->next;
				free(q);	/*先使q指向head处的内存单元,head指向其next内存单元后,释放其原来占有的内存单元,用p来标识该单元*/
			} else {
				temp = q->next;	/*temp的作用和q=head;处head的作用一样,记下内存单元,方便free*/
				while (NULL != temp) {
					if (no == q->next->no) {
						q->next = q->next->next;	
						free(temp);		/*free q的next指向的内存单元*/
						break;		/*跳出while*/
					}
					q = q->next;
					temp = q->next;	/* 用q记下需要删除的节点的previous,因为我们这里的是单向链表 */
				}
			}
		}
		
		/*输出链表*/		
		printf("\n");
		print (head);			
		printf("\n");
		
		printf("请继续输入删除的节点编号(输入0表示结束):");		
		no = getNo ();
		while (ZERO != 0 && !isExists(head, no)) {
			printf("编号不存在,请重新输入:");
			no = getNo();
		}
	}	
	
	printf("\n");
	
	return head;
}

/**
* Author      :  luchunli
* Description :  链表翻转
* Date		  :  2012-03-22
*/
struct node * reverse (struct node * head) {
	struct node * newhead = NULL;	/*必须每次都将最后的节点摘下来*/
	struct node * q = NULL;
	struct node * p = NULL;
	struct node * temp = NULL;
	
	if (NULL == head) {
		return head;
	} else {
		q = head;
		while (NULL != q->next) {	/*用q的next记下哪个节点需要被从链表上摘下来,next域存储的节点的地址*/
			if (NULL == q->next->next) {	/*注意:必须要通过next的next来实现*/
				if (NULL == newhead) {
					newhead = q->next;
				}else {
					p->next = q->next;
				}
				p = q->next;
				p->next = NULL;
				q->next = NULL;
				q = head;
				continue;	/*当找到最后一个节点后,然后再从头head开始找*/
			}
			q = q->next;
		}
		p->next = q;
		p = q;
		p->next = NULL;	
		/*
		* 此时q和head都仍然指向第一个节点,也就是newhead指向的链表的最后一个节点
		*/
	}
	/*
	printf("\n----------------------ori~head~begin---------------------\n");
	print(head);
	printf("----------------------ori~head~end---------------------\n");
	
	printf("----------------------new~head~begin---------------------\n");
	print(newhead);
	printf("----------------------new~head~end---------------------\n");
	*/
	return newhead;
}

/**
* Author      :  luchunli
* Date		  :  2012-03-20
* Description :  程序入口地址
*/
int main(int argc, char * argv[]) {
	/*定义结构体类型的变量*/
	struct node * head = NULL;
	/*操作类型*/
	char optype[20] = {'\0'};
	
	help();	
	
	printf("\n");
	printf("请输入希望执行的操作的类型:");
	scanf("%s", optype);
	
	while (0 != strcmp(optype, "exit")) {
		if (0 == strcmp(optype, "create")) {
			/*创建链表*/						
			head = insert (head);		
			/*输出链表*/
			print (head);		
		} else if (0 == strcmp(optype, "q_by_no")) {
			/*从链表中查找特定编号的节点*/
			queryByNo (head);
		} else if (0 == strcmp(optype, "q_by_name")) {
			/*从链表中查找特定名称的节点*/
			queryByName (head);
		} else if (0 == strcmp(optype, "insert")) {
			/*向链表中指定位置处插入指定编号的节点*/
			head = insert (head);
			/*输出链表*/
			print (head);
		} else if (0 == strcmp(optype, "delete")) {
			/*从链表中删除特定编号的节点*/	
			head = delete (head);
			/*输出链表*/
			print (head);
		} else if (0 == strcmp(optype, "reverse")) {
			/*链表翻转*/	
			head = reverse (head);
			/*输出链表*/
			print (head);
		} else if (0 == strcmp(optype, "print")) {
			/*输出链表*/
			print (head);
		}  else if (0 == strcmp(optype, "clear")) {
			/*清除屏幕缓冲区及液晶显示缓冲区*/
			system("cls"); 
			/*clrscr();*/
		} else if (0 == strcmp(optype, "?")) {
			/*帮助信息*/
			help();
		} else {
			help();
		}
		
		printf("\n");		
		printf("请输入希望执行的操作的类型:");
		scanf("%s", optype);
	}
	
	return 0;
}



链表翻转的逻辑:
C语言单链表-增强_第1张图片

你可能感兴趣的:(c)