数据结构-链表

"单链表": 只有一个方向(下一个或上一个的指针)的链表

  • 不带头结点的单链表
  • 带头结点的单链表
struct Node
{
	ElemType data; //保存数据元素  "数据域"
	
					//"指针域": 关系
	struct Node *next;  //保存下一个数据元素的地址
	//struct Node *prev; //保存上一个数据元素的地址
};

关于链表的操作分为4个部分:“增”、“删”、“改”、“查”

下面举几个例子来讲解

1. 根据用户的输入,建立一个有序链表。
	
		"插入排序"算法
		
		(1) 找插入位置
			"遍历链表":找到链表中第一个值比待插入结点大的结点pk(以及pk前驱结点pr),
				pk前面就是插入位置
		
			pk = first;
			while (pk)
			{
				if (pk->data > p->data)
				{
					//找到了
					break;
				}
				
				pr = pk;
				pk = pk->next;
			
			}
        (2) “插入操作”
			遍历后,分两种情况:
				2.1找到pk   pk != NULL
					(1) pk为第一个结点  pk == first
						p比链表上所有结点的值都要小,
						p应该加入到链表的最前面。
						“头插法”
						
						p->next = first;
						first = p;
                    (2) pk为后面或中间的结点
						pr为pk的前驱动结点,
						p应该插入在pk的前面,pr的后面
						
						p->next = pk;
						pr->next = p;
                2.2没找到pk  pk == NULL
					链表上所有结点的值都比p要小,
					p应该加入到链表的末尾。
					“尾插法”
					last->next = p;
					last = p;
2. 在链表的合适的位置新增加一个节点
        把p指向的结点,加入到h指向的链表中去,
		h指向的链表是数值的升序排序,希望加入p后,指向的链表仍然为升序。
		NOTE:
			h可能为空
			函数返回值:
				返回加入p后,链表的第一个结点的指针。
		struct node * Add_a_Node(struct node *h, struct node *p)
		{
			if (h == NULL)
			{
				return p;
			}

			if (p == NULL)
			{
				return h;
			}
            //把p插入到h指向的链表中去。 “插入”
			// 1. 找插入位置
			//	一个一个地遍历(从第一个开始找,找到其值比p大的那个结点pk以及pk前驱结点pr)
			//	pk的前面就是插入位置
			//
			struct node *pk = NULL; //pk指向链表中第一个比p值大的结点
			struct node *pr = NULL; //pr指向pk前面的那个结点

			pk = h;
			while (pk)
			{
				if (pk->data > p->data)
				{
					break;
				}
				else  //pk->data <= p->data
				{
					pr = pk;
					pk = pk->next;
				}
			}
			
			// 2. 插入操作
			//  前面找位置的结果,只有两种情况:
			//		2.1 没找到   pk == NULL
			//				h指向的链表中所有结点的值都比p要小
			//				p应该加入到h指向的末尾。“尾插法”
			//				此时, pr正好指向最后那个结点
			//				pr->next = p;
			//  	2.2 找到了
			//			又分两种情况:
			//				pk为第一个结点: pk == h
			//					h指向的链表中所有结点的值都比p要大,
			//					p应该加入到h的前面。“头插法”
			//						p->next = h;
			//						h = p;
			//				pk为中间结点:
			//					p->next = pk;
			//					pr->next = p;
			if (pk == NULL)
			{
				pr->next = p;
			}
			else
			{
				if (pk == h)
				{
					p->next = h;
					h = p;
				}
				else
				{
					p->next = pk;
					pr->next = p;
				}

			}

			return h;
		
		}
3. 删除链表中的一个结点
		删除操作分为两个步骤:
			(1) 找到要删除的结点
				"遍历算法"
			(2) 删除操作
				(2.1) 先摘下来
				(2.2) 再free
		
		/*
			delete_x:在h指向的单链表中,删除值为x的结点(假设顶多只有一个结点的值为
			x)
			@h: 指向链表(不一定有序)
			@x: 要删除的结点的元素值
			返回值:
				返回删除后的链表的第一个结点的指针
		*/
		struct node * delete_x(struct node *h, ElemType x)
		{
		
		}
	
		struct node * delete_all_x(struct node *h, ElemType x)
		{
			
		}
4. 找单链表中最后一个结点,返回其指针
		"查找": 查找的条件,满足条件的结点
			最后一个结点:
				p->next == NULL
        struct node * find_last(struct node *h)
		{
			struct node *p = h; //指向最后一个结点
			if (p == NULL)
			{
				return NULL;
			}
		
			//while (p->next != NULL)
			//{
			//	p = p->next;
			//}	
			
			while (p)
			{
				if (p后面没有结点) // p->next == NULL
				{
					//p就是最后一个结点
					return p;
				}
				else
				{
					//p后面还有结点
					p = p->next;
				}
			}
			
		}
5. 计算单链表中结点的数目
	
		int get_count(struct node *h)
		{
			int n = 0;
			while (h)
			{
				n++;
				h = h->next;
			}
		}

很多时候,我们可能经常需要求一个链表中有多少个结点?
    或者是求一个链表中的最后一个结点?……。
        => 通过第一个结点的指针,“遍历整个链表”

如果:用带头结点的链表,就可以轻松解决这些问题。
    
        头结点:用来管理和唯一标识一个链表的  LinkedListWithHead

typedef int ElemType ; //数据元素的类型
//数据结点
typedef struct node
{
	ElemType data; //"数据域":保存数据

	struct node *next; //“指针域”: 下一个节点的地址
}Node;
//头结点
struct LinkedList
{
	struct node *first;// 指向链表的第一个结点
	struct node *last;// 指向链表的最后一个结点
	int nodeNum; //记录链表中结点的数目
};

最后附上各链表的代码

单链表

LinkedList.c

#include "LinkedList.h"
#include <stdio.h>
#include <stdlib.h>
/*
	根据用户输入的数据元素,按用户输入(输入0表示结束)的自然顺序
	来建立一个单链表
	1 3 5 7 9
*/

Node creat_LinkedList()
{
	int d;
	Node first = NULL;//指向新建立的链表的第一个结点
	Node last = NULL;//指向新建立的链表的最后一个结点

	Node p=NULL;	//指向新建立的链表的当前结点

	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		p=malloc(sizeof(Node));
		p->data=d;
		p->next=NULL;

		if(first==NULL)	//代表链表为空,此时用户输入第一个元素
		{
			first=p;
			last=p;
		}
		else
		{
			last->next=p;	//尾插法
			last=p;
		}
	}

	return first;
	
}

void print_Node(Node h)
{
	Node p=h;
	while(p)
	{
		printf("%d  ",p->data);
		p=p->next;
	}
	printf("\n");
}

/*
	根据用户输入的数据元素,按用户输入(输入0表示结束)的逆序
	来建立一个单链表
	1 3 5 7 9
*/
Node creat_Rverse_LinkedList()
{
	int d;
	Node first = NULL;//指向新建立的链表的第一个结点
	Node last = NULL;//指向新建立的链表的最后一个结点

	Node p=NULL;	//指向新建立的链表的当前结点
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		
		p=malloc(sizeof(Node));
		p->data=d;
		p->next=NULL;
		
		if(first==NULL)
		{
			first=p;
			last=p;
		}
		else
		{
			p->next=first;	//头插法
			first=p;
		}
		
	}
	return first;
}

/*
	根据用户输入的数据元素,根据用户输入(输入0表示结束),按从小到大顺序排列
	来建立一个单链表
	1 3 5 7 9
*/
Node creat_Order_LinkedList()
{
	int d;
	Node first = NULL;//指向新建立的链表的第一个结点
	Node last = NULL;//指向新建立的链表的最后一个结点

	Node p=NULL;	//指向新建立的链表的当前结点
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		
		p=malloc(sizeof(Node));
		p->data=d;
		p->next=NULL;
		
		if(first==NULL)
		{
			first=p;
			last=p;
		}
		else
		{
			//"插入排序"
			//遍历链表,找到链表中的第一个比p值大的那个结点pk(以及pk前的那个结点)
			//pk的前面就是插入位置
			Node pk=NULL;//指向链表中比p值大的那个结点
			Node pr=NULL;//指向pk前面的那个结点
			pk=first;
			while(pk)
			{
				if(pk->data>p->data)
				{
					//此时找到了
					break;
				}
				pr=pk;
				pk=pk->next;
			}
			//遍历结束后,分两种情况
			//(1)找到了
			// (2)没找到
			//没找到,说明当前链表所有结点的值,都是p小=>"尾插法"
			if(pk==NULL)
			{
				last->next=p;//尾插法
				last=p;
			}
			else
			{
				if(pk==first)
				{
					p->next=first;//头插法
					first=p;
				}
				else
				{
					pr->next=p;//中间插入
					p->next=pk;
				}
			}
		}
	}
	return first;
}

/*
	在链表的合适位置增加一个结点
	把p指向的结点,加入到h指向的链表中去,
	h指向的链表是数值的升序排序,希望加入p后,指向的链表仍然为升序
	NOTE:
		h可能为空
		函数返回值:
			返回加入p后,链表的第一个结点的指针

		@h:指向一个升序的链表
		@p:指向待插入的结点
		返回值:
			返回插入p后,新链表的第一个结点的指针
*/
Node Add_a_Node(Node h,Node p)
{
	if(h=NULL)
		return p;
	if(p=NULL)
		return h;
	/*
		把p插入到h指向的链表中去,“插入”
		1、找插入位置
		一个一个遍历(从第一个开始找,找到其值比p大的那个结点以及pk前驱结点pr)
		pk的前面就是插入位置
		
	*/
	Node pk = NULL;	//指向链表中第一个比p值大的结点
	Node pr = NULL;	//pr指向pk前面的那个结点

	pk=h;
	while(pk)
	{
		if(pk->data>p->data)
		{
			break;
		}
		pr=pk;
		pk=pk->next;
	}
	//2、插入操作
	/*
		前面找位置的结果,只有两种情况
			2.1没找到 pk=NULL
				h指向的链表中所有结点的值都比p要小
				此时pr正好指向最后那个结点
				pr-next=p;
			2.2找到了
				又分两种情况
					pk为第一个结点:pk==h
						h指向的链表中所有结点的值都比p要大,
						p应该加入到h的前面。“头插法”
						p->next=h;
						h=p;
					pk为中间结点
						p->next=pk;
						pr->next=p;
	*/
	if(pk==NULL)
	{
		pr->next=p;
	}
	else
	{
		if(pk==h)
		{
			p->next=h;
			h=p;
		}
		else
		{
			p->next=pk;
			pr->next=p;
		}
	}
	return h;
}

/*
	delete_x:在h指向的单链表中,删除值为x的结点(假设顶多只有一个结点的值为x)
	@h:指向链表(不一定有序)
	@x:要删除的结点的元素值
	返回值:
		返回删除后的链表的第一个结点的指针
*/
Node delete_allx(Node h,ElemType x)
{
	Node px=NULL;	//指向要删除的那个结点
	Node pr =NULL;	//指向px前面的那个结点
	Node ph=h;	//每次遍历的第一个(起始)结点
	if(h==NULL)
	{
		return h;
	}
	/*
		删除算法:
		1、先找到要删除的结点
			遍历算法:
			从链表的第一个结点开始,一个一个找
			找到其值为x的结点px(以及px前面的那个结点pr)
		2、删除
			(1)先摘除下来
			(2)删除free
	*/
	while(1)
	{
		px=ph;
		while(px)
		{
			if(px->data==x)
			{
				break;
			}
			pr=px;
			px=px->next;
		}
		if(px==NULL)	//没有找到
		{
			return h;
		}
		ph=px->next;
		if(px==h)
		{
			h=h->next;
			px->next=NULL;		
		}
		else	//找到的那个结点是中间结点
		{	
			pr->next=px->next;
			px->next=NULL;
		}
		free(px);
		px=NULL;
	}
	
	return h;
}

/*
	找单链表中最后一个结点,返回其指针
*/
Node find_last(Node h)
{
	Node pr = NULL;
	Node px=NULL;
	if(h==NULL)
	{
		return h;
	}
	px=h;
	while(px)
	{
		pr =px;
		px=px->next;
	}
	return pr;
}
/*
	计算单链表中结点的数目
*/
int get_count(Node h)
{
	int i=0;
	Node px=NULL;
	px=h;
	while(px)
	{
		i++;
		px=px->next;		
	}
	return i;
}
int main()
{
	Node h=creat_Order_LinkedList();
	print_Node(h);
	int x;
	int i;
	i = get_count(h);
	printf("链表的数目为:%d\n",i);
	scanf("%d",&x);
	h=delete_allx(h, x);
	printf("after linkedlist:");
	print_Node(h);
	h=find_last(h);
	printf("after linkedlist:");
	print_Node(h);
	
}

LinkedList.h

#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__

typedef int ElemType;	//数据元素类型

typedef struct node
{
	ElemType data;	//"数据域":保存数据
	struct node *next;	//"指针域":下一个结点的地址
}*Node;

Node creat_LinkedList();


Node Insert_A_Node();

void print_Node(Node h);

Node creat_Rverse_LinkedList();

Node creat_Order_LinkedList();

Node Add_a_Node(Node h,Node p);

Node delete_allx(Node h,ElemType x);

Node find_last(Node h);

int get_count(Node h);


#endif

带头结点的单链表

LinkedListWithHead.c

#include <stdio.h>
#include <stdlib.h>
#include "LinkedListWithHead.h"

hNode creat_order_LinkedList()
{
	hNode l=malloc(sizeof(hNode));
	l->first=NULL;
	l->last=NULL;
	l->nodeNum=0;
	Node p=NULL;
	int d;
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}	
		p=malloc(sizeof(Node));	//创建一个结点存入用户输入的数
		p->data=d;
		p->next=NULL;
		if(l->first==NULL&&l->last==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			Add_a_Node(l,p);
		}
	}
	return l;
}

/*
	往“带头结点的单链表”添加一个"数据结点"
	Add_a_Node:把p指向的数据结点,添加到l指向的带头结点的单链表中去
	在添加p后l仍然保持升序
	@l:l指向的单链表是升序
	@p:
*/
void Add_a_Node(hNode l,Node p)
{
	Node pk=NULL;
	Node pr=NULL;
	if(l->first==NULL&&l->last==NULL)
	{
		l->first=p;
		l->last=p;
	}
	pk=l->first;
	while(pk)
	{
		if(pk->data>=p->data)
		{
			break;	//此时找到了第一个比p结点大的结点
		}
		pr=pk;
		pk=pk->next;
	}
	/*
		1、没有找到比p结点大的结点,利用尾插法插入
	*/
	if(pk==NULL)
	{
		l->last->next=p;
		l->last=p;
	}
	/*
		2、找到了
			2.1找到的结点为第一个结点
			2.2找到的结点为中间结点
	*/
	if(pk==l->first)
	{
		p->next=l->first;
		l->first=p;
	}
	else
	{
		pr->next=p;
		p->next=pk;
	}
}

void print_list(hNode l)
{
	Node pk = l->first;
	while(pk)
	{
		printf("%d  ",pk->data);
		pk=pk->next;
	}
	printf("\n");
}

/*
	在"带头结点的单链表"上删除结点
	delete_all_x:在l指向的带头结点的单链表,删除值为x的所有结点
	@l:
	@x;
	返回值:
		无
*/
void delete_all_x(hNode l,ElemType x)
{
	Node pk=NULL;	//指向所找到的那个目标结点
	Node pr=NULL;	//指向目标结点的前一个结点
	Node ph=l->first;	//删除结点后,遍历的当前位置
	
	if(l->first==NULL)
	{
		return;
	}
	while(1)
	{
		pk=ph;
		while(pk)
		{
			if(pk->data==x)	//找到了目标值
			{
				break;
			}
			pr=pk;
			pk=pk->next;
		}
		/*
		分两种情况:
		1、没有找到
		2、找到了
			2.1找到的那个结点为第一个结点
			2.2找到的那个结点为最后一个结点
			2.3找到的那个结点为中加结点
		*/
		if(pk==NULL)
		{
			return;
		}
		ph=pk->next;
		if(pk==l->first)//要删除的是第一个结点
		{
			if(l->first==l->last)
			{
				//链表中只有一个结点,并且要删除这个结点
				l->first=l->last=NULL;
			}
			else
			{
				l->first=pk->next;
				pk->next=NULL;
			}			
		}
		else if(pk==l->last)//要删除的是最后一个结点
		{
			//链表至少会有两个及以上的结点
			pr->next=pk->next;
			l->last=pr;
		}
		else
		{
			pr->next=pk->next;
			pk->next=NULL;
		}
		free(pk);
		pk=NULL;
	}
}
int main()
{
	hNode l=creat_order_LinkedList();
	print_list(l);
	int x;
	scanf("%d",&x);
	delete_all_x(l,x);
	printf("after linkedlist:\n");
	print_list(l);
	return 0;
}

LinkedListWithHead.h

#ifndef __LINKEDLISTWITHHEAD__
#define __LINKEDLISTWITHHEAD__

typedef int ElemType;
typedef struct node
{
	ElemType data;	//"数据域":保存数据
	struct node* next;//指针域:下一个结点的地址
}*Node;

typedef struct hnode
{
	Node first;	//指向链表的第一个结点
	Node last;	//指向链表的最后一个结点
	int nodeNum;	//记录链表中结点的数目
}*hNode;

hNode creat_order_LinkedList();

void Add_a_Node(hNode l,Node p);

void print_list(hNode l);

void delete_all_x(hNode l,ElemType x);




#endif

双向链表

biLinkedList.c

#include <stdio.h>
#include <stdlib.h>
#include "biLinkedList.h"

/*
	创建一个双向链表
*/
hbiNode creat_biLinkedList()
{
	hbiNode l = malloc(sizeof(hbiNode));
	l->first=NULL;
	l->last=NULL;
	int d;
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		biNode p=malloc(sizeof(biNode));
		p->data=d;
		p->next=NULL;
		p->prev=NULL;
		if(l->first==NULL&&l->last==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			l->last->next=p;
			p->prev=l->last;
			l->last=p;
		}
	}
	return l;
}

/*
	创建一个有序双向链表
*/
hbiNode creat_order_biLinkedList()
{
	hbiNode l = malloc(sizeof(hbiNode));
	l->first=NULL;
	l->last=NULL;
	int d;
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		biNode p=malloc(sizeof(biNode));
		p->data=d;
		p->next=NULL;
		p->prev=NULL;
		if(l->first==NULL&&l->last==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			add_a_biNode(l, p);
		}
	}
	return l;
}

/*
	add_a_biNode:在l指向的带头结点的双向链表中,添加一个数据结点p
	@l:l指向一个升序的双向链表,期望添加p后,l仍然升序
	@p:待添加的数据结点
*/
void add_a_biNode(hbiNode l,biNode p)
{
	if(l==NULL||p==NULL)
	{
		return;
	}
	biNode pk=NULL;//指向链表中第一个比p值大的结点
	biNode pr=NULL;//指向pk前面的那个结点
	pk=l->first;
	while(pk)
	{
		if(pk->data>p->data)
		{
			break;
		}
		pr=pk;
		pk=pk->next;
	}
	//2、插入操作
	if(pk==NULL)
	{
		//没找到,链表上所有结点的值,都比p要小,或者
		//l是一个空链表
		if(l->first==NULL)
		{
			p->next=NULL;
			p->prev=NULL;
			l->first=p;
			l->last=p;
		}
		else
		{
			//l不是一个空链表
			l->last->next=p;
			p->prev=l->last;
			l->last=p;
		}
	}
	else
	{
		if(pk==l->first)
		{
			//pk为第一个结点,头插入法
			p->next=l->first;
			l->first->prev=p;
			l->first=p;
		}
		else//中间插入
		{
			p->next=pk;
			pk->prev=p;
			pr->next=p;
			p->prev=pr;
		}
	}
	
}

void print_hbiLinkedList(hbiNode h)
{
	if(h==NULL)
	{
		return;
	}
	biNode pk=h->first;
	while(pk)
	{
		printf("%d  ",pk->data);
		pk=pk->next;
	}
	printf("\n");
}

/*
	从双向链表中删除一个数据结点
	算法:
		1、找到要删除的那个结点
		2、删除操作
			第一个结点
			中间结点
			最后一个结点
*/
void delete_all_x(hbiNode l,ElemType x)
{
	if(l==NULL)
	{
		return;
	}
	biNode ph = l->first;//每次遍历的起始结点
	biNode pk = NULL;//指向要找的那个结点
	biNode pr = NULL;//指向要找的那个结点的前一个结点
	while(1)
	{
		pk=ph;
		while(pk)
		{
			if(pk->data==x)
			{
				break;
			}
			pr=pk;
			pk=pk->next;
		}
		//1、没有找到,那么直接return
		if(pk==NULL)
		{
			return;
		}
		ph=pk->next;
		if(pk==l->first)
		{
			if(l->first==l->last)
			{
				l->first=NULL;
				l->last=NULL;				
			}
			else
			{
				l->first=pk->next;
				pk->next->prev=NULL;
				pk->next=NULL;	
			}			
		}		
		else if(pk==l->last)
		{
			l->last=pr;
			pr->next=NULL;
			pk->prev=NULL;
		}
		else
		{
			pr->next=pk->next;
			pk->next->prev=pr;
		}
		free(pk);
		pk=NULL;
	}
}
int main()
{
	hbiNode h=creat_order_biLinkedList();
	print_hbiLinkedList(h);
	int x;
	scanf("%d",&x);
	delete_all_x(h,x);
	print_hbiLinkedList(h);
	return 0;
}

biLinkedList.h

#ifndef __BILINKEDLIST_H__
#define __BILINKEDLIST_H__

typedef int ElemType;
typedef struct binode
{
	ElemType data;//数据域

	struct binode* next;//指向后续结点
	struct binode* prev;//指向前驱结点
}*biNode;

typedef struct hbinode
{
	biNode first;//指向双向链表的第一个结点
	biNode last;//指向双向链表的最后一个结点
}*hbiNode;
hbiNode creat_biLinkedList();

hbiNode creat_order_biLinkedList();

void add_a_biNode(hbiNode l,biNode p);
void print_hbiLinkedList(hbiNode h);

#endif

关于链表的一些习题,帮助理解

linkedlist.c

#include <stdio.h>
#include <stdlib.h>
#include "linkedlist.h"
/*
	创建一个带头结点的链表
*/
hNode create_list()
{
	hNode l=malloc(sizeof(hNode));
	Node p =NULL;
	l->first = NULL;
	l->last = NULL;
	l->nodeNum=0;
	int d;
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}
		p = malloc(sizeof(Node));
		p->data=d;
		p->next=NULL;
		if(l->first==NULL&&l->last==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			l->last->next=p;
			l->last=p;
		}
	}
	return l;
}
/*
	作业1:
	就地逆置一个单链表(不允许申请新的结点空间)
*/
void reverse(hNode h)
{
	Node pr=NULL;	//pr指向链表的第一个结点
	Node pk=NULL;	//pk指向链表的后一个结点
	Node pn=NULL;	//指向pk所指向结点的后一个结点

	pr=h->first;
	pk=h->first->next;
	pr->next=NULL;
	while(pk)
	{
		pn = pk->next;
		pk->next=pr;	//因为操作的毕竟是同一个结点,那么指向的下一个结点地址可以覆盖
		pr=pk;
		pk=pn;
	}
	h->first=pr;
}

void print_list(hNode h)
{
	Node pk=h->first;
	while(pk)
	{
		printf("%d  ",pk->data);
		pk=pk->next;
	}
	printf("\n");

}
/*
	创建一个带头结点的有序链表
*/
hNode creat_order_LinkedList()
{
	hNode l=malloc(sizeof(hNode));
	l->first=NULL;
	l->last=NULL;
	l->nodeNum=0;
	Node p=NULL;
	int d;
	while(1)
	{
		scanf("%d",&d);
		if(d==0)
		{
			break;
		}	
		p=malloc(sizeof(Node));	//创建一个结点存入用户输入的数
		p->data=d;
		p->next=NULL;
		if(l->first==NULL&&l->last==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			Add_a_Node(l,p);
		}
	}
	return l;
}

/*
	往“带头结点的单链表”添加一个"数据结点"
	Add_a_Node:把p指向的数据结点,添加到l指向的带头结点的单链表中去
	在添加p后l仍然保持升序
	@l:l指向的单链表是升序
	@p:
*/
void Add_a_Node(hNode l,Node p)
{
	Node pk=NULL;
	Node pr=NULL;
	if(l->first==NULL&&l->last==NULL)
	{
		l->first=p;
		l->last=p;
	}
	pk=l->first;
	while(pk)
	{
		if(pk->data>=p->data)
		{
			break;	//此时找到了第一个比p结点大的结点
		}
		pr=pk;
		pk=pk->next;
	}
	/*
		1、没有找到比p结点大的结点,利用尾插法插入
	*/
	if(pk==NULL)
	{
		l->last->next=p;
		l->last=p;
	}
	/*
		2、找到了
			2.1找到的结点为第一个结点
			2.2找到的结点为中间结点
	*/
	if(pk==l->first)
	{
		p->next=l->first;
		l->first=p;
	}
	else
	{
		pr->next=p;
		p->next=pk;
	}
}


/*
	归并两个有序链表,要求归并后的链表也有序
*/
hNode merger(hNode ha,hNode hb)
{
	Node pk = NULL;	//指向hb链表的第一个结点
	Node pn = NULL;	//指向pk的后一个结点

	pk=hb->first;
	while(pk)
	{
		pn=pk->next;
		pk->next=NULL;//此时摘除了pk指向的那个结点
		Add_a_Node(ha,pk);
		pk=pn;		
	}	
	return ha;
}
/*
	返回一个链表倒数第k个结点的指针
	算法:
		拍两个哨兵 p1 p2
		p2先走k步,p1和p2间隔k个结点

		然后p1和p2同时移动一个结点,直到
		p2为NULL,p1就是倒数第k个结点
	
*/
int bottom(hNode h,int k)
{
	Node p1=NULL;//哨兵1
	Node p2=NULL;//哨兵2
	p1=h->first;
	p2=h->first;
	for(int i=0;i<k;i++)
	{
		p2=p2->next;	//此时p2与p1指向的位置间隔k个结点
	}
	while(p2)
	{
		p1=p1->next;	//此时p1指向的结点就是链表倒数第k个结点的指针
		p2=p2->next;
	}
	return p1->data;
}
/*
	返回一个链表中间结点的指针(只遍历一次)
	算法:
		派两个哨兵p1,p2
		每次p1走一步,p2走两步
		......
		当p2为NULL的时候,p1就是在链表的中间结点
*/
int find_middle(hNode h)
{
	Node p1=NULL;
	Node p2=NULL;
	p1=h->first;
	p2=h->first;
	while(p2)
	{		
		if(p2->next)
		{	
			p1=p1->next;
			p2=p2->next->next;
		}
		else
		{
			break;
		}
	}
	return p1->data;
}
/*
	请设计一个算法,判断一个单链表中是否存在环
	算法:
		两个哨兵,一个跑两步,一个跑一步
		如果有环,最终会相遇
		如果没有环,跑得快的那个最终会到NULL
*/
int is_circle(hNode h)
{
	Node pa=h->first;
	Node pb=h->first;
	while(pb)
	{
		pa=pa->next;
		if(pb->next)
		{
			pb=pb->next->next;
		}
		else
		{
			pb=NULL;
		}
		if(pa==pb)
		{
			return 1; //有环
		}
		if(pb==NULL)
		{
			return 0; //没有环
		}
	}
}

/*
	删除单链表(无序的)中最小值结点(假设最小值唯一)
*/
void delete_min(hNode h)
{
	Node pmin=NULL;		//指向最小值结点
	Node pmin_r=NULL;	//指向最小值结点的前一个结点
	pmin=h->first;

	Node pk=h->first;
	Node pr=NULL;
	while(pk)//遍历链表
	{
		if(pk->data<pmin->data)
		{
			pmin_r=pr;
			pmin=pk;
		}
		pr=pk;
		pk=pk->next;
	}
	//2、分情况去删除
	if(pmin==h->first)
	{
		h->first=pmin->next;
		pmin->next=NULL;
	}
	else if(pmin==h->last)
	{
		pmin_r->next=NULL;
		h->last=pmin_r;
	}
	else
	{
		pmin_r->next=pmin->next;
		pmin->next=NULL;
	}
	free(pmin);
	pmin=NULL;
}

/*
	逆序存储整数的各个位,并实现两个整数相加
	"大数相加另外一个实现方式"
*/
hNode creat_list_by_digtal(int num)
{
	hNode l = malloc(sizeof(hNode));
	l->first=NULL;
	l->last=NULL;
	Node p= NULL;
	while(num)
	{
		p=malloc(sizeof(Node));
		p->data=num%10;
		p->next=NULL;

		num=num/10;
		if(l->first==NULL)
		{
			l->first=p;
			l->last=p;
		}
		else
		{
			l->last->next=p;
			l->last=p;
		}
	}
	return l;
}

hNode add_two_numbers(hNode ha,hNode hb)
{
	hNode hc = malloc(sizeof(hNode));
	hc->first=NULL;
	hc->last=NULL;

	Node h1 = ha->first;
	Node h2 = hb->first;
	Node p = NULL;
	int a,b;  //ha的当前位a,hb的当前位b
	int c=0;
	int sum=0;
	while(h1||h2)
	{
		a=h1?h1->data:0;
		b=h2?h2->data:0;
		sum=a+b+c;
		c=sum/10;
		
		p=malloc(sizeof(Node));
		p->data=sum%10;
		p->next=NULL;
		if(hc->first==NULL)
		{
			hc->first=p;
			hc->last=p;
		}
		else
		{
			hc->last->next=p;
			hc->last=p;
		}
		h1=h1?h1->next:NULL;
		h2=h2?h2->next:NULL;
	}
	if(c)	//存取最高为的c,有可能最后ha,hb的值都加完了,但还是存在进位,这个时候还需要保存一下
	{
		p=malloc(sizeof(Node));
		p->data=c;
		p->next=NULL;

		hc->last->next=p;
		hc->last=p;
	}
	return hc;
}
/*
	链表A,B,判断A是否为B的子序列(子序列:连续的一部分)
	int is_sequence(hNode ha,hNode hb)
	返回值:1		是子序列
	返回值:0		不是子序列
*/
int is_sequence(hNode ha,hNode hb)
{
	if(ha==NULL||hb==NULL)
	{
		return 0;
	}
	Node h1=ha->first;
	Node h2=hb->first;
	while(h2)
	{
		if(h2->data==h1->data)
		{
			while(h1&&h2)
			{
				if(h1->data!=h2->data)
				{
					break;
				}
				h1=h1->next;
				h2=h2->next;
			}
			if(h1==NULL)
			{
				return 1;
			}
		}
		h2=h2->next;
	}
	return 0;
}
/*
	链表A存的是整数,有正有负,写一个程序,把链表A中的负数,放在前面
	算法:
	遍历链表找到第一个负数的结点px
	摘下来,然后用"头插法"加到原链表
	优化:
		先遍历链表找到第一个正数结点,pk
	从pk后面遍历找到第一个负数结点px
	然后摘下来,加到pk的前面(也可用"头插法"加入原链表)
*/
void neg_head(hNode h)
{
	Node pk=h->first;
	Node px=NULL;
	Node pr = NULL;	//指向px所指向结点的前一个结点
	Node pn = NULL;//指向px所指向结点的后一个结点
	while(pk)
	{
		if(pk->data>0)	//找到链表中的第一个正数
		{
			break;
		}
		pk=pk->next;
	}
	pr=pk;
	px=pk->next;
	while(px)
	{
		pn=px->next;
		if(px->data<0)
		{
			pr->next=px->next;
			px->next=h->first;
			h->first=px;
		}
		pr=px;
		px=pn;
	}
}
/*
	对一个链表进行排序
	算法:
	新链表:开始为空,从原链表中一个一个把结点摘下来
	然后再按"插入排序"算法把结点,加入到新链表中去
*/
void sort_list(hNode h)
{
	Node pk=h->first->next;	//产生的问题:再开辟一个新的链表空间去存储排序后的结点
	h->first->next=NULL;	//出现了问题,就会一直陷入死循环,一直动态开辟,存储那同一个结点的值
	h->last=h->first;
	Node pn=NULL;
	while(pk)
	{
		pn=pk->next;
		pk->next=NULL;
		Add_a_Node(h, pk);
		pk=pn;		
	}
}
/*
	设A,B为单链表,且A,B都为升序,求A、B的交集
	要求:
		重新组成一个新链表C,原链表A,B不动
		要求C也是升序
	(1)A,B本身无重复
	(2)A,B本身可能有重复,要求C无重复
*/
hNode get_intersection(hNode ha,hNode hb)
{
	hNode hc = malloc(sizeof(hNode));
	hc->first=NULL;
	hc->last=NULL;
	Node h1=ha->first;
	Node h2=hb->first;
	Node temp=NULL;
	Node h = malloc(sizeof(Node));
	while(1)
	{
		if(h1==NULL||h2==NULL)
		{
			break;
		}
		if(h1->data>h2->data)
		{
			h2=h2->next;
		}
		else if(h1->data<h2->data)
		{
			h1=h1->next;
		}
		else
		{
			if(temp==NULL||(temp->data!=h2->data))
			{
				h->data=h2->data;
				h->next=NULL;
				if(hc->first==NULL&&hc->last==NULL)
				{
					hc->first=h;
					hc->last=h;
					temp=h;
				}
				else
				{
					hc->last->next=h;
					hc->last=h;
					temp=h;
				}
			}
			h1=h1->next;	//在相等的时候,要同时往后挪一格,要不然总是跳进这个循环
			h2=h2->next;
		}
	}
	return hc;
}

int main()
{
	hNode ha=create_list();
	hNode hb = create_list();
	hNode hc = get_intersection(ha,hb);
	print_list(hc);
	return 0;
}

linkedlist.h

#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__

typedef int ElemType;
typedef struct node
{
	ElemType data;	//"数据域":保存数据
	struct node* next;//指针域:下一个结点的地址
}*Node;

typedef struct hnode
{
	Node first;	//指向链表的第一个结点
	Node last;	//指向链表的最后一个结点
	int nodeNum;	//记录链表中结点的数目
}*hNode;
hNode create_list();
void reverse(hNode h);
void print_list(hNode h);
hNode creat_order_LinkedList();
void Add_a_Node(hNode l,Node p);
hNode merger(hNode ha,hNode hb);
hNode creat_order_LinkedList();
/*
	返回一个链表倒数第k个结点的指针
	算法:
		拍两个哨兵 p1 p2
		p2先走k步,p1和p2间隔k个结点

		然后p1和p2同时移动一个结点,直到
		p2为NULL,p1就是倒数第k个结点
	
*/
int bottom(hNode h,int k);
/*
	返回一个链表中间结点的指针(只遍历一次)
	算法:
		派两个哨兵p1,p2
		每次p1走一步,p2走两步
		......
		当p2为NULL的时候,p1就是在链表的中间结点
*/
int find_middle(hNode h);
/*
	请设计一个算法,判断一个单链表中是否存在环
	算法:
		两个哨兵,一个跑两步,一个跑一步
		如果有环,最终会相遇
		如果没有环,跑得快的那个最终会到NULL
*/
int is_circle(hNode h);
/*
	删除单链表(无序的)中最小值结点(假设最小值唯一)
*/
void delete_min(hNode h);
/*
	逆序存储整数的各个位,并实现两个整数相加
	"大数相加另外一个实现方式"
*/
hNode creat_list_by_digtal(int num);
hNode add_two_numbers(hNode ha,hNode hb);

/*
	链表A,B,判断A是否为B的子序列(子序列:连续的一部分)
	int is_sequence(hNode ha,hNode hb)
	返回值:1		是子序列
	返回值:0		不是子序列
*/
int is_sequence(hNode ha,hNode hb);
/*
	链表A存的是整数,有正有负,写一个程序,把链表A中的负数,放在前面
	算法:
	遍历链表找到第一个负数的结点px
	摘下来,然后用"头插法"加到原链表
	优化:
		先遍历链表找到第一个正数结点,pk
	从pk后面遍历找到第一个负数结点px
	然后摘下来,加到pk的前面(也可用"头插法"加入原链表)
*/
void neg_head(hNode h);
/*
	对一个链表进行排序
	算法:
	新链表:开始为空,从原链表中一个一个把结点摘下来
	然后再按"插入排序"算法把结点,加入到新链表中去
*/
void sort_list(hNode h);
/*
	设A,B为单链表,且A,B都为升序,求A、B的交集
	要求:
		重新组成一个新链表C,原链表A,B不动
		要求C也是升序
	(1)A,B本身无重复
	(2)A,B本身可能有重复,要求C无重复
*/
hNode get_intersection(hNode ha,hNode hb);




#endif

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值