关于链表结构的基本操作 c 实现 (创建,插入删除,反转,合并链表,查找,是否有环,链表相交情况)

       最近一直为找工作的事困扰着,技术面中问的题中数据结构相关的链表操作就占了一半左右,所以好好补习下链表的操作,并用相关代码实现,有错误的地方,希望读者能够指出,非常感谢~!

       链表是最基本的数据结构,按结构分为:单链表,循环单链表,双链表,循环双链表。实际应用中单链表是应用最多的情况,也是最基本结构,学好这个简单的结构,才能更好的掌握复杂的数据结构。而按存储结构分为:顺序存储和链式存储。

现在给出单链表的链式存储结构的实现:

代码如下:(c 实现接口,c++测试)

头文件:(LinkList.h)

/*============================================================================================
Copyright (c) 2011 weedge.  All Rights Reserved.
	@coder:weedge	E-mail:weege@126.com
	@date: 2011/09/12(中秋节快乐!)
	@comment:
		单链表结构的基本操作对外提供相应的接口:
			1.创建createList, 插入节点insertListNode, 删除节点deleteListNode, 打印traverseList.
			2.非递归和递归实现链表反转reserveList 以及 两个单链表的合并mergeList
			3.对单链表排序sortList, 查找中间元素findMid,单链表中是否有环isLoopList
			4.链表相交isIntersectList, 相交的第一个节点intersectedFirstNode.
*=====================================================================================
*/
#ifndef LINK_LIST_H
#define LINK_LIST_H

#ifdef __cplusplus
extern "C"{
#endif

enum STATUS{INFEASIBLE=-2, ERROR=-1, FALSE=0, TRUE=1,OK=2};

typedef int ElemType;

/*线性表结构*/
typedef struct Node 
{
	ElemType data;
	struct Node *next;
}LNode,*LinkList;

typedef int (*cmpFun)(ElemType a, ElemType b);
typedef void (*printFun)(ElemType a);

/*--------------------------链表基本操作创建、销毁、空链表、链表长度等---------------------------*/
/* 创建有n个元素的链表*/
int createList(LinkList *L, int n);

/* 初始条件:线性表L已存在。操作结果:销毁线性表L */
void destroyList(LinkList *L);

/* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
int isEmpty(LinkList L);

/* 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L);

/* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
int GetElem(LinkList L,int i,ElemType *e);

/* 返回L中第1个与e满足关系compare()的数据元素的位序;若这样的数据元素不存在,则返回值为0 */
int LocaleElem(LinkList L,ElemType e,cmpFun compare);

/*若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE */
int PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e);

/*若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,返回OK;否则操作失败,next_e无定义,返回INFEASIBLE */
int NextElem(LinkList L,ElemType cur_e,ElemType *next_e);

/*--------------------------插入、删除节点---------------------------*/
/* 在单链线性表L中第i个位置之前插入元素e */
int insertListNode(LinkList *L,int i,ElemType e);

/* 在单链线性表L中,删除第i个元素,并由e返回其值 */
int deleteListNode(LinkList *L,int i,ElemType *e); 

/* 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数visit()打印 */
void traverseList(LinkList L,printFun visit);

/*--------------------------反转、合并节点---------------------------*/
/* 递归反转单链表 */
void reverseListRecursive(LinkList *head);

/* 非递归反转单链表 */
LNode* reverseList(LinkList head);

/* 递归合并两个链表 */
LNode* mergeList_recursive(LinkList head1, LinkList head2);

/* 非递归合并两个链表 */
LNode* mergeList(LinkList head1, LinkList head2);

/*--------------------------排序、查找链表---------------------------*/
/* 对链表进行冒泡排序(非递减) */
void sortList(LinkList *head);

/* 求链表中的中间节点,由midNod返回 */
void findMid(LinkList head,LNode **midNode);

/*--------------------------有环单链表---------------------------*/
/* 判断链表中是否有环 */
int isLoopList(LinkList head);

/*--------------------------2单链表相交---------------------------*/
/* 判断2个无环单链表是否相交 */
int isIntersectList(LinkList head1, LinkList head2);

/* 如果两个链表相交,找出相交的第一个节点 */
LNode* intersectedFirstNode(LinkList head1,LinkList head2);

#ifdef __cplusplus
}
#endif



#endif


 


源文件:(LinkList.C)

/*==============================================================================================
Copyright (c) 2011 weedge.  All Rights Reserved.
	@coder:weedge	E-mail:weege@126.com
	@date: 2011/09/12(中秋节快乐!)
	@comment:
		实现对单链表结构的基本操作:
			1.创建createList, 插入节点insertListNode, 删除节点deleteListNode, 打印traverseList.
			2.非递归和递归实现链表反转reserveList 以及 两个单链表的合并mergeList
			3.对单链表排序sortList, 查找中间元素findMid,单链表中是否有环isLoopList,
			4.链表相交isIntersectList, 相交的第一个节点intersectedFirstNode
*=====================================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "LinkList.h"

/* 创建有n个元素的链表*/
int createList(LinkList *L, int n)
{
	int i;
	LNode *p,*s;
	if (n<1)
	{
		return FALSE;
	}
	*L = (LinkList)malloc(sizeof(LNode));
	p = *L;
	printf("please input %d data\n",n);
	for (i=0;i<n;i++)
	{
		s = (LNode *)malloc(sizeof(LNode));
		scanf("%d",&(s->data));
		/* 插入到表尾 */
		p->next = s;
		p = s;

		/* 插入到表头 */
		/*
		s->next=p->next; 
		p->next=s;
		*/
	}
	*L = (*L)->next;/* 无头节点 */
	s->next = NULL;/* 插入到表尾时最后节点的指针域指空 */
	return TRUE;
}

/* 初始条件:线性表L已存在。操作结果:销毁线性表L */
void destroyList(LinkList *L)
{ 
	LinkList q;
	while(*L)
	{
		q=(*L)->next;
		free(*L);
		*L=q;
	}
}

/* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
int isEmpty(LinkList L)
{ 
	if(L!=NULL) /* 非空 */
		return FALSE;
	else
		return TRUE;
}
/* 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{ 
	int i=0;
	LinkList p=L; /* p指向第一个结点 */
	while(p) /* 没到表尾 */
	{
		i++;
		p=p->next;
	}
	return i;
}
/* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
int GetElem(LinkList L,int i,ElemType *e) 
{ 
	int j=1; /* j为计数器 */
	LinkList p=L; /* p指向第一个结点 */
	while(p&&j<i) /* 顺指针向后查找,直到p指向第i个元素或p为空 */
	{
		p=p->next;
		j++;
	}
	if(p==NULL||j>i) /* 第i个元素不存在 */
		return ERROR;
	*e=p->data; /* 取第i个元素 */
	return OK;
}

/* 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为0,否则为1) */
/* 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。 */
/*           若这样的数据元素不存在,则返回值为0 */
int LocaleElem(LinkList L,ElemType e,cmpFun compare)
{ 
	int i=0;
	LinkList p=L;
	while(p)
	{
		i++;
		if((*compare)(p->data,e)==0) /* 找到这样的数据元素 */
			return i;
		p=p->next;
	}
	return 0;
}

/* 初始条件: 线性表L已存在 */
/* 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */
/*           返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE */
int PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e)
{ 
	LinkList q,p=L; /* p指向第一个结点 */
	while(p->next) /* p所指结点有后继 */
	{
		q=p->next; /* q为p的后继 */
		if(q->data==cur_e)
		{
			*pre_e=p->data;
			return OK;
		}
		p=q; /* p向后移 */
	}
	return INFEASIBLE;
}

/* 初始条件:线性表L已存在 */
/* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */
/*           返回OK;否则操作失败,next_e无定义,返回INFEASIBLE */
int NextElem(LinkList L,ElemType cur_e,ElemType *next_e)
{
	LinkList p=L; /* p指向第一个结点 */
	while(p->next) /* p所指结点有后继 */
	{
		if(p->data==cur_e)
		{
			*next_e=p->next->data;
			return OK;
		}
		p=p->next;
	}
	return INFEASIBLE;
}

/* 在单链线性表L中第i个位置之前插入元素e */
int insertListNode(LinkList *L,int i,ElemType e) 
{ 
	int j=0;
	LinkList p2,p1=*L,s;

	if(p1==NULL||i<1) /* i小于1*/
		return ERROR;

	s=(LinkList)malloc(sizeof(struct Node)); /* 生成新结点 */
	s->data=e; 
	while(p1->next!=NULL && j<i-1) /* 寻找第i-1个结点 */
	{
		p2 = p1;
		p1=p1->next;
		j++;
	}

	if (j == 0)
	{/* 在头之前加入s */
		s->next = *L;
		*L = s;
	}
	else if (NULL==p1->next)
	{/* 在尾部加入s */
		p1->next = s;
		s->next = NULL;
	}
	else 
	{/* s插入L中 */
		p2->next = s;
		s->next=p1;
	}
	return OK;
}

/* 在单链线性表L中,删除第i个元素,并由e返回其值 */
int deleteListNode(LinkList *L,int i,ElemType *e) 
{
	int j=0;
	LinkList p1=*L,p2;
	if(p1==NULL||i<1) /* i小于1*/
		return ERROR;

	while(p1->next!=NULL && j<i-1) /* 寻找第i个结点,并令p1指向其节点 */
	{
		p2 = p1;
		p1=p1->next;
		j++;
	}
	if (p1==*L)
	{/* 删除第一个结点 */
		*L = p1->next;
		*e = p1->data;
		free(p1);
		p1 = NULL;
	}
	else
	{/* 删除并释放结点 */
		p2->next = p1->next;
		*e = p1->data;
		free(p1);
		p1 = NULL;
	}
	return OK;
}

/* 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数visit()打印 */
void traverseList(LinkList L,printFun visit)
{ 
	LinkList p=L;
	while(p)
	{
		(*visit)(p->data);
		p=p->next;
	}
	printf("\n");
}

/**
	反转单链表
	1.利用递归的方法。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。
	不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL。
	因为要改变head指针,所以用指向head指针的指针。
*/
LNode* reverseList_recursive(LNode *p, LinkList *head)
{
	LNode *tmp;
	if (NULL == p || NULL == p->next)
	{
		*head = p;
		return p;
	}
	else
	{
		tmp = reverseList_recursive(p->next,head);
		tmp->next = p;//注意:反转后的最后2个结点会形成一个环
		return p;
	}
}
void reverseListRecursive(LinkList *head)
{
	LNode *p;
	if (*head==NULL||(*head)->next==NULL)
	{
		return;
	}
	p = *head;
	p = reverseList_recursive(p,head);
	if(isLoopList(*head)==TRUE){//有环
		p->next = NULL;//将函数的返回的节点的next域置为NULL。
	}
}

/**
	反转单链表
	2.利用非递归的方法。边遍历单链表,边反转指针,需要用到3个辅助指针,
	前2指针进行反转操作,后1指针用于遍历单链表,最后将head的下个元素为空,head指向反转后的头结点。
*/
LNode* reverseList(LinkList head)
{
	LNode *p1,*p2,*p3;
	if (head==NULL||head->next==NULL)
	{
		return head;
	}
	p1 = head;
	p2 = p1->next;
	p3 = p2->next;
	while (NULL != p3)
	{
		p2->next = p1;
		p1 = p2;
		p2 = p3;
		p3 = p3->next;
	}
	p2->next = p1;
	head->next = NULL;
	head = p2;
	return head;
}

/**
	已知两个链表head1 和head2 各自有序,把它们合并成一个链表依然有序(非递减)
	1.递归方法:
*/
LNode* mergeList_recursive(LinkList head1, LinkList head2)
{
	LinkList head = NULL;
	if (NULL == head1)
	{
		return head2;
	}
	if (NULL == head2)
	{
		return head1;
	}
	if (head1->data < head2->data)
	{
		head = head1;
		head->next = mergeList_recursive(head1->next,head2);
	}
	else
	{
		head = head2;
		head->next = mergeList_recursive(head1,head2->next);
	}
	return head;
}

/**
	已知两个链表head1 和head2 各自有序,把它们合并成一个链表依然有序(非递减)
	1.非递归方法:
*/
LNode* mergeList(LinkList head1, LinkList head2)
{
	LNode *currentNode;
	LNode *p1 = NULL;
	LNode *p2 = NULL;
	LinkList head = NULL;
	if (NULL == head1)
	{
		return head2;
	}
	if (NULL == head2)
	{
		return head1;
	}
	if(isLoopList(head1)==TRUE)printf("this is a loop\n");

	if(isLoopList(head2)==TRUE)printf("this is a Loop\n");


	if (head1->data < head2->data)
	{
		head = head1;
		p1 = head1->next;
		p2 = head2;
	}
	else
	{
		head = head2;
		p2 = head2->next;
		p1 = head1;
	}
	currentNode = head;
	while (p1 != NULL && p2 != NULL)
	{
		if (p1->data <= p2->data)
		{
			currentNode->next = p1;
			currentNode = p1;
			p1 = p1->next;
		}
		else
		{
			currentNode->next = p2;
			currentNode = p2;
			p2 = p2->next;
		}
	}
	if (p1 != NULL)
	{
		currentNode->next = p1;
	}
	if (p2 != NULL)
	{
		currentNode->next = p2;
	}
	return head;
}

/**
	对链表进行冒泡排序(非递减)。
*/
void sortList(LinkList *head)
{
	LNode *p;
	int i,j,listLen,swaped=1;
	ElemType temp;
	if (*head==NULL || (*head)->next==NULL)
	{
		return;
	}
	listLen = ListLength(*head);
	p = *head;
	for (i=1;i<listLen;i++)
	{
		swaped=0;
		p = *head;
		for (j=0;j<listLen-i;j++)
		{
			if (p->data > p->next->data)
			{
				temp = p->data;
				p->data = p->next->data;
				p->next->data = temp;
				swaped=1;
			}
			p = p->next;
		}
		if(swaped==0) break;
	}
	
}

/**
	求链表中的中间节点,由midNod返回。
*/
void findMid(LinkList head,LNode **midNode)
{
	LNode *slow = head;
	while(head->next!=NULL && head->next->next!=NULL)
	{
		head = head->next->next;
		slow = slow->next;
	}
	*midNode = slow;
}

/**
	判断链表中是否有环。
*/
int isLoopList(LinkList head)
{
	LinkList p1=head, p2 = head;
	if (head ==NULL || head->next ==NULL) 
	{
		return FALSE;
	}
	do{
		p1= p1->next;
		p2 = p2->next->next;
	} while(p2 && p2->next && p1!=p2);        
	if(p1 == p2)
		return TRUE;
	else
		return FALSE;
}

/**
	判断2个无环单链表是否相交。参考:《编程之美》(2个链表无环条件下,有环时,先进行解环,然后在进行处理。
*/
int isIntersectList(LinkList head1, LinkList head2)
{
	LinkList end1, end2;
	while (head1->next!=NULL)
	{
		head1 = head1->next;
	}
	end1 = head1;
	while (head2->next!=NULL)
	{
		head2 = head2->next;
	}
	end2 = head2;
	if(end1==end2)
		return TRUE;
	else return FALSE;
}

/**
	如果两个链表相交,找出相交的第一个节点。
	在判断相交的过程中要分别遍历两个链表,同时记下各自的长度。然后再遍历一次:
	长链表节点先从头节点出发前进(lengthMax-lenghMin)步,之后两个链表同时前进,每次一步,
	相遇的第一个节点即为两个链表相交的第一个节点.
*/
LNode* intersectedFirstNode(LinkList head1, LinkList head2)
{
	LNode *head, *p, *q;
	int len1 = ListLength(head1);
	int len2 = ListLength(head2);
	assert(len1&&len2);
	if (isIntersectList(head1,head2)==TRUE)
	{
		int steps = abs(len1-len2);
		head = len1>len2 ? head1:head2;
		while (steps!=0)
		{
			head = head->next;
			steps--;
		}
		if (len1>len2)
		{
			p = head;
			q = head2;
		}else{
			q = head;
			p = head1;
		}
		while (p!=q)
		{
			p = p->next;
			q = q->next;
		}
		return p;
	}
	return NULL;
}


 

测试文件:(c++)

#include <iostream>
#include "LinkList.h"
using namespace std;

int comp(ElemType a, ElemType b)
{
	if (a==b)
	{
		return 0;
	}
	return (a-b)/abs(a-b);
}

void print(ElemType e)
{
	cout<<e<<"->";
}

int main()
{
	LinkList l1,l2,l3;
	LNode *midNode;
	int iElem,dElem,Elem,pElem,nElem;
	int pos;

	createList(&l1,5);
	traverseList(l1,&print);
	cout<<"the list length:"<<ListLength(l1)<<endl;
	if (isEmpty(l1)==FALSE)
	{
		cout<<"this is not empty list!"<<endl;
	}
	if(GetElem(l1,6,&Elem)==OK){
		cout<<Elem<<endl;
	}
	cout<<"Input a elem for LocalElem:"<<endl;
	cin>>Elem;
	pos = LocaleElem(l1,Elem,&comp);
	cout<<"the position of the elem is:"<<pos<<endl;
	cout<<"Input a elem for PriorElem:"<<endl;
	cin>>Elem;
	if (PriorElem(l1,Elem,&pElem)==OK)
	{
		cout<<"the prior of the elem is:"<<pElem<<endl;
	}
	cout<<"Input a elem for NextElem:"<<endl;
	cin>>Elem;
	if (NextElem(l1,Elem,&nElem)==OK)
	{
		cout<<"the next of the elem is:"<<nElem<<endl;
	}

	//destroyList(&l1);
	//if (isEmpty(l1)==TRUE)
	//{
	//	cout<<"this is empty list!"<<endl;
	//}

	findMid(l1,&midNode);
	cout<<"midNode:"<<midNode->data<<endl;
	traverseList(l1,&print);

	cout<<"Input insertElem:"<<endl;
	cin>>iElem;
	insertListNode(&l1,3,iElem);
	traverseList(l1,&print);
	findMid(l1,&midNode);
	cout<<"midNode:"<<midNode->data<<endl;

	deleteListNode(&l1,3,&dElem);
	cout<<"the deleteElem:"<<dElem<<endl;
	traverseList(l1,&print);
	
	sortList(&l1);
	traverseList(l1,&print);

	reverseListRecursive(&l1);
	traverseList(l1,&print);

	l1 = reverseList(l1);
	traverseList(l1,&print);

	createList(&l2,5);
	traverseList(l2,&print);

	//l1 = mergeList_recursive(l1,l2);
	//traverseList(l1,&print);

	l3 = mergeList(l1,l2);
	traverseList(l3,&print);

	system("pause");
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值