单链表的表示和实现 数据结构

欢迎访问我的新博客:http://www.milkcu.com/blog/

原文地址:

基本概念

链式存储结构,不要求逻辑上相邻的元素在物理上也相邻,没有顺序存储结构所具有的的弱点(在作插入或删除操作是,需移动大量元素),但同时也失去了顺序表可随机存取的优点。

单链表的结点由数据域和指针域构成,多个结点链结成一个链表。

代码实现

# include <stdio.h>
# include <stdlib.h>
# define OK 1
# define ERROR -1
# define OVERFLOW -2

typedef int ElemType;
typedef int Status;

typedef struct Lnode {
	ElemType data;
	struct Lnode * next;
} Lnode, * LinkList;

Status GetElem_L(LinkList L, int i, ElemType & e);
Status ListInsert_L(LinkList & L, int i, ElemType e);
Status ListDelete_L(LinkList & L, int i, ElemType & e);
void CreateList_L(LinkList & L, int n);
void MergeList_L(LinkList & La, LinkList & Lb, LinkList & Lc);
Status ShowList_L(LinkList L);

int main(void)
{
	//测试函数
	LinkList L;
	CreateList_L(L, 8);
	ShowList_L(L);    //print create
	ListInsert_L(L, 3, 3);
	ShowList_L(L);    //print insert
	ElemType e;
	ListDelete_L(L, 4, e);
	printf("delete e = %d\n", e);    //print e delete
	ShowList_L(L);    //print delete
	GetElem_L(L, 5, e);
	printf("getelem e = %d\n", e);    //print getelem
	LinkList Lb;
	CreateList_L(Lb, 5);
	ShowList_L(Lb);    //print Lb
	LinkList Lc;
	MergeList_L(L, Lb, Lc);
	ShowList_L(Lc);    //print Lc
	return 0;
}

Status GetElem_L(LinkList L, int i, ElemType & e)
{
	//算法2.8
	//L为带头结点的单链表的头指针
	Lnode * p = L->next;
	int j = 1;
	for(j = 1; p && j < i; j++) {
		p = p->next;
	}
	if(!p) {
		return ERROR;
	}
	e = p->data;
	return OK;
}

Status ListInsert_L(LinkList & L, int i, ElemType e)
{
	//算法2.9
	//在带头结点的单链线性表L中第i个位置之前插入元素e
	Lnode * p = L->next;
	int j = 1;
	Lnode * s = (Lnode * ) malloc(sizeof(Lnode));
	s->data = e;
	for(j =1; p && j < i - 1; j++) {
		p = p->next;
	}
	if(!p) {
		return ERROR;
	}
	s->next = p->next;
	p->next = s;
	return OK;
}

Status ListDelete_L(LinkList & L, int i, ElemType & e)
{
	//算法2.10
	Lnode * p = L->next;
	int j = 1;
	for(j = 1; p && j < i - 1; j++) {
		p = p->next;
	}
	if(!p) {
		return ERROR;
	}
	Lnode * q;
	q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);
	return OK;
}

void CreateList_L(LinkList & L, int n)
{
	//算法2.11
	//逆位序输入n个元素的值,建立带表头结点的单链线性表L
	L = (LinkList) malloc(sizeof(Lnode));
	L->next = NULL;
	for(int i = 0; i < n; i++) {
		Lnode * p = (Lnode * ) malloc(sizeof(Lnode));
		scanf("%d", & p->data);
		p->next = L->next;
		L->next = p;
	}
}

void MergeList_L(LinkList & La, LinkList & Lb, LinkList & Lc)
{
	//算法2.12
	//归并非递减排列的La和Lb到Lc
	//用La的头结点作为Lc的头结点
	//本算法具有副作用 
	Lnode * pa = La->next;
	Lnode * pb = Lb->next;
	Lc = La;
	Lnode * pc = Lc;
	while(pa && pb) {
		if(pa->data <= pb->data) {
			pc->next = pa;
			pc = pc->next;
			pa = pa->next;
		} else {
			pc->next = pb;
			pc = pc->next;
			pb = pb->next;
		}
	}
	pc = (pa ? pa : pb);
	free(Lb);
}

Status ShowList_L(LinkList L)
{
	Lnode * p = L->next;
	if(!p) {
		return ERROR;
	}
	while(p) {
		printf("%d  ", p->data);
		p = p->next;
	}
	putchar('\n');
	return OK;
}

算法分析

GetElem_L()、ListInsert_L()、ListDelete_L()的时间复杂度均为O(n)。

对于算法2.9的ListInsert_L()也可通过下面改进算法实现,该算法对于在单链表中指定结点前插入结点比较实用(时间复杂度为O(1)),代码如下:

Status ListInsert_L(LinkList & L, int i, ElemType e)
{
	//算法2.9改进版
	//在带头结点的单链线性表L中第i个位置之前插入元素e
	Lnode * p = L->next;
	int j = 1;
	Lnode * s = (Lnode * ) malloc(sizeof(Lnode));
	for(j = 1; p && j < i; j++) {
		p = p->next;
	}
	if(!p) {
		return ERROR;
	}
	//最终目的是在p前插入s 
	s->next = p->next;
	p->next = s;
	s->data = p->data;
	p->data = e;
}

对于算法2.10的ListDelete_L()也可通过下面函数实现,该算法对在于单链表中删除指定结点比较实用(时间复杂度为O(1)),代码如下:

Status ListDelete_L(LinkList & L, int i, ElemType & e)
{
	//算法2.10改进版
	//在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
	//未引入临时变量
	Lnode * p = L->next;
	int j = 1;
	for(j = 1; p && j < i; j++) {
		p = p->next;
	}
	if(!p) {
		return ERROR;
	}
	e = p->data;
	p->data = p->next->data;
	p->next = p->next->next;
	free(& p->next);
	return OK;
}

其实上面两个改进算法,在填空选择中经常用到。

参考资料

  • 《数据结构:C语言版》(严蔚敏,吴伟民编著)

(全文完)

原文地址:https://www.cnblogs.com/milkcu/p/3808890.html