数据结构学习笔记 --- 线性表 (单链表)

时间:2022-01-12 11:11:12

1. 引言


单链表有带有结点和不带头结点之分,本文分别讨论带头结点的单链表和不带头结点的单链表的一些基本操作,和用头插法、

插法创建单链表,以及两个算法。


2. 带头结点的单链表


2.1 带头结点的单链表的存储结构
typedef struct LNode{
	ElemType 		data;
	struct LNode	        *next;
}LNode, *LinkList;

2.2 带头结点的单链表的基本操作


本文不做一一介绍,只介绍几个比较重要的基本操作。


(1)返回L中第1个与e满足关系compare()的数据元素的位序

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

	while(p)
   	{
     	i++;
     	if(compare(p->data,e)) // 找到这样的数据元素
       		return i;
     	p=p->next;
   	}
   	return 0;
}

(2)前驱和后继

// 初始条件: 线性表L已存在
// 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
//           返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE
Status PriorElem(LinkList L, ElemType cur_e, ElemType &pre_e)
{
	LinkList q,p=L->next; // 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
Status NextElem(LinkList L, ElemType cur_e, ElemType &next_e)
{
	LinkList 		p = L->next;  // p指向第一个结点
	
	while (p->next)  // p所指结点有后继
	{
		if ( p->data == cur_e)
		{
			next_e = p->next->data;
			return OK;
		}
		p = p->next;
	}
	return INFEASIBLE;
}
(3)插入和删除

// 在带头结点的单链线性表L中第i个位置之前插入元素e
Status InsertList(LinkList L, int i, ElemType e)
{
	int			j = 0;
	LinkList	p = L, pnew;
	
	// 寻找第i-1个结点
	while (j < i-1 && p)
	{
		p = p->next;
		j++;
	}
	
	if (!p || j > i-1) // i小于1或者大于表长
		return ERROR;
	
	pnew = (LinkList)malloc(sizeof(LNode));
	if (!pnew)	exit(OVERFLOW);
	
	pnew->data = e;
	pnew->next = p->next;
	p->next = pnew;
	
	return OK;
}

// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
Status DeleteList(LinkList L, int i, ElemType &e)
{
	int			j = 0;
	LinkList	p = L, q;
	
	// 寻找第i个结点,并令p指向其前驱
	while (j < i-1 && p->next)
	{
		p = p->next;
		j++;
	}
	
	if (!(p->next) && j>i-1 ) // 删除位置不合理
		return ERROR;
	q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);
	
	return OK;
}

(4)头插法和尾插法创建单链表

// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L
void HeadCreList(LinkList &L, int n)
{
	LinkList 	head;
	L = (LinkList)malloc(sizeof(LNode));
	if (!L) exit(OVERFLOW);
	L->next = NULL;
	
	printf("please input %d element.\n", n);
	for (int i = 0; i < n; i++)
	{
		head = (LinkList)malloc(sizeof(LNode));
		if (!head) exit(OVERFLOW);
		scanf("%d", &(head->data));
		head->next = L->next; // 插入到表头
		L->next = head;
	}
}

// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表L
void TailCreList(LinkList &L, int n)
{
	LinkList 	temp, tail;
	L = (LinkList)malloc(sizeof(LNode));
	if (!L) exit(OVERFLOW);
	L->next = NULL;
	tail = L;   // 总是让tail指向链表的尾结点
	
	printf("please input %d element.\n", n);
	for (int i = 0; i < n; i++)
	{
		temp = (LinkList)malloc(sizeof(LNode));
		if (!temp) exit(OVERFLOW);
		scanf("%d", &(temp->data));
		
		tail->next = temp;
		tail = tail->next;  // 总是让tail指向链表的尾结点
	}
	tail->next = NULL;		// 不要忘记将尾结点的next指针置为空
}

(5)两个算法

// 将所有在线性表Lb中但不在La中的数据元素插入到La中
void Union(LinkList &La, LinkList Lb)
{
	int 		la_len = ListLength(La);
	LinkList 	pa = La->next, pb = Lb->next;
	
	while (pb)
	{
		if (!LocateElem(La, pb->data, compare))
			InsertList(La,++la_len, pb->data);
		pb = pb->next;
	}
}

// 已知单链线性表La和Lb的元素按值非递减排列。
// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
void MergeList(LinkList La, LinkList Lb, LinkList &Lc)
{
	LinkList pa = La->next, pb = Lb->next, pc;
	
	Lc = pc = La;
	
	//同时遍历链表La和Lb
	while (pa && pb)
	{
		if (pa->data > pb->data)
		{
			pc->next = pb;
			pb = pb->next;
		}
		else
		{
			pc->next = pa;
			pa = pa->next;
		}
		
		pc = pc->next;
	}
	
	pc->next = pa ? pa : pb;// 插入剩余段
   	free(Lb); // 释放Lb的头结点
   	Lb=NULL;
}


3. 不带头结点的单链表


3.1 不带头结点的单链表的存储结构(和带头结点的一样)
typedef struct LNode{
	ElemType 		data;
	struct LNode	        *next;
}LNode, *LinkList;

3.2 不带头结点的单链表的基本操作


   不带头结点的单链表的基本操作和带头结点单链表差不多,插入和删除操作有点不一样

(1)插入和删除

// 在带头结点的单链线性表L中第i个位置之前插入元素e
Status InsertList(LinkList &L, int i, ElemType e)
{
	int			j = 1;
	LinkList	p = L, pnew;
	
	if(i<1) // i值不合法
    	return ERROR;
    
	pnew = (LinkList)malloc(sizeof(LNode));
	if (!pnew)	exit(OVERFLOW);
	pnew->data = e;   
    
    if (1 == i)
	{
		pnew->next = L;
		L = pnew; // 改变L
	}
	else
	{
		// 寻找第i-1个结点
		while (j < i-1 && p)
		{
			p = p->next;
			j++;
		}
		
		if (!p) // i大于表长
			return ERROR;
		pnew->next = p->next;
		p->next = pnew;
	}
	return OK;
}

// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
Status DeleteList(LinkList &L, int i, ElemType &e)
{
	int			j = 1;
	LinkList	p = L, q;
	
    if (1 == i)
	{
		L = p->next;	// L由第2个结点开始
		e = p->data;
		free(p);
	}
	else
	{
		// 寻找第i个结点,并令p指向其前驱
		while (j < i-1 && p->next)
		{
			p = p->next;
			j++;
		}
		
		if(!p->next || j>i-1) // 删除位置不合理
			return ERROR;
		q = p->next;
		e = q->data;
		p->next = q->next;
		free(q);
	}
	
	return OK;
}

(2)头插法和尾插法创建单链表

// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L
void HeadCreList(LinkList &L, int n)
{
	LinkList 	head;
	L = NULL;
	
	printf("please input %d element.\n", n);
	for (int i = 0; i < n; i++)
	{
		head = (LinkList)malloc(sizeof(LNode));
		if (!head) exit(OVERFLOW);
		scanf("%d", &(head->data));
		head->next = L; // 插入到表头
		L = head;
	}
}

// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表L
void TailCreList(LinkList &L, int n)
{
	LinkList 	temp, tail;
	
	printf("please input %d element.\n", n);
	for (int i = 0; i < n; i++)
	{
		temp = (LinkList)malloc(sizeof(LNode));
		if (!temp) exit(OVERFLOW);
		scanf("%d", &(temp->data));
		if (0 == i)
		{
			L = tail = temp;
		}
		else
		{
			tail->next = temp;
			tail = tail->next;  // 总是让tail指向链表的尾结点
		}
	}
	tail->next = NULL;		// 不要忘记将尾结点的next指针置为空
}
(3)两个算法

// 将所有在线性表Lb中但不在La中的数据元素插入到La中
void Union(LinkList &La, LinkList Lb)
{
	int 		la_len = ListLength(La);
	LinkList 	pa = La, pb = Lb;
	
	while (pb)
	{
		if (!LocateElem(La, pb->data, compare))
			InsertList(La,++la_len, pb->data);
		pb = pb->next;
	}
}

// 已知单链线性表La和Lb的元素按值非递减排列。
// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
void MergeList(LinkList La, LinkList Lb, LinkList &Lc)
{
	LinkList pa = La, pb = Lb, pc;
	int		 isfirst = 1;
	
	
	//同时遍历链表La和Lb
	while (pa && pb)
	{
		
		if (isfirst)
		{
			if (pa->data > pb->data)
			{
				Lc = pc= pb;
				pb = pb->next;
			}
			else
			{
				Lc = pc = pa;
				pa = pa->next;
			}
			isfirst = 0;
		}
		else
		{
			if (pa->data > pb->data)
			{
				pc->next = pb;
				pc = pb;
				pb = pb->next;
			}
			else
			{
				pc->next = pa;
				pc = pa;
				pa = pa->next;
			}
			
			
		}
	}
	
	pc->next = pa ? pa : pb;// 插入剩余段
}