数据结构之链式表

        继顺序表之后,我们需要讨论一下数据结构的链式表。顺序表的特点是逻辑上相邻的元素在物理位置上也相邻,通常我们用数组来表示这种存储结构,这种存储结构简单,易于理解。但是同时必须看到它的缺点,那就是在作 插入和删除操作时,需要移动大量的数据 ,这必然造成效率低下。因此,产生了链式表,链式表不需要逻辑上相邻的元素在物理位置上也相邻,因此效率大大提高。另一方面,链表不需要像顺序表那样对空间进行预先的分配规划,而可以由程序员根据需求即时生成并且适时释放,因而这种方式实际上可以更为高效的利用紧缺的存储空间,这也是它被广泛使用的原因。也正是如此,链表比顺序表理解起来要更加复杂。
       链式表主要分为线性链表,静态链表,循环链表和双向链表。一开始一定要搞清楚每一种链表的数据结构定义。
       链式表的特点是用一组任意的存储单元存储线性表的数据元素,因为这些存储单元在物理上不一定是连续的,所以每一个数据元素除了包含数据信息外,还需要包含至少其它元素的位置信息。我们称这样的存储单元为 结点,节点由数据域和指针域组成。
        线性链表的节点中只包含一个指针域,因此又成为单链表。单链表最后一个结点的指针域为NULL.这个和循环链表是不同的。
       单链表的数据结构可以定义为
       typedef struct LNode{
            ElemType data;
            struct LNode *next;
       }LNode,*LinkList;
       一般来说,单链表都包含一个头节点,头节点可以不存储任何信息,也可以存储诸如线性表的长度等附加信息。
       静态链表是用数组来进行描述的链表这种方法便于在不设“指针”类型的高级程序设计语言中来使用。
      静态链表的数据结构定义如下:
      #define MAXSIZE 1000
      typedef struct{
          ElemType data;
          int cur;
    }component,SLinkList[MAXSIZE];
      这种方式仍然需要事先分配一个较大的空间,但是仍然具有线性链表的在插入删除等操作方面的优点。不过个人感觉没有指针那么灵活,用的不是很多,不作为优先选择。
       循环链表的特点是表中最后一个结点的指针域指向头节点,整个链表形成一个环。因此,从表中的任意一个结点出发均可以找到表中的其他节点。
      循环链表的数据结构和双向链表是相同的。
      由于以上的链表结构只有一个指示直接后继的指针域,因此从某个结点出发只能往后查找其它结点,如果查找该之前的节点,则只能从表头查起,效率显然很低。为了克服这种缺点,出现了双向链表。
      双向链表具有连个指针域,一个指向直接后继,一个指向直接前驱。
      双向链表的数据结构定义如下:
      typedef struct DulNode{
          ElemType data;
          struct DulNode *prior;
          struct DulNode *next;
      }DulNode, *DuLinkList;
      注意,双向链表也有循环表,并且有两个环。
      

      由于链表在空间上的合理利用和插入、删除操作上的有点,在很多场合都作为首先的存储结构。但是由于实际应用的不同,我们必须要 合理的定义和使用链表的存储结构,做到灵活处理,而不能生搬硬套。
       以下是根据实际应用定义的线性链表的类型和抽象数据类型的实现,可以作为参考。
       线性链表的数据结构类型定义:
       
typedef struct LNode
{
	ElemType data;
	struct LNode *next;
}LNode,*Link, *Position;

typedef struct
{ 
	Link head,tail;
	 int len;
 }LinkList;
     
/* 
Description: allocate a node which is pointed by p and the value of the node is e.
*/
Status MakeNode(Link &p, ElemType e)
{
	p = (Link)malloc(sizeof(LNode));
	if(p == NULL)
	{
		return ERROR;
	}
	p->data = e;
	p->next = NULL;
	return OK;
}

/*
Description: construct a vacant linear list which only has a head node.
*/
Status InitList(LinkList &L)
{
	int e = 0;
	MakeNode(L.head,e);
	L.len = 0; // the length of a Linear List is zero.
	L.tail = L.head;
	return OK;
}

/*
Description: release the space pointed by p.
*/
Status FreeNode(Link &p)
{
	free(p);
	return OK;
}

/* 
Description: destroy Linear List L and it doesn't exist anymore.
*/
Status DestroyList(LinkList &L)
{
	int i;
	Link p;
	Link s;
	for(i=0;i<L.len;i++)
	{
		p = L.head->next;
		s = p->next;
		FreeNode(p);
		L.head->next = s;
	}
	FreeNode(L.head);
	return OK;
} 

/*
Description: Make the List become a vacant list and release the space.
*/
Status ClearList(LinkList &L)
{
	DestroyList(L);
	InitList(L);
	return OK;
}

/*
Description: if h pointed to the head node, insert node s before the first node.
*/
Status InsFirst(Link h, Link s)
{
	s->next = h->next;
	h->next = s;
	return OK;
}

/*
Description: delete the first node in the Linear List and return the address by p.
*/
Status DelFirst(Link h, Link &p)
{
	Link s;
	s = h->next;
	h->next = s->next;
	s->next = NULL;//remember to modify point s->next, as s has already lose relation with the primary list.
	p = s;
	return OK;
}

/*
Description: Link the nodes beginning with s to the end of L and modify the tail of L.
			 use point p to trace s.
*/
Status Append(LinkList &L, Link s)
{
	Link p = NULL;
	L.tail->next = s;// this is very important, we must link s to L.
	p = s;
	s = s->next;
	while(s)
	{
		p = s;
		s = s->next;
	}
	L.tail = p;
	return OK;
}

/*
Description: remove the tail of Linear List L and return the tail address by q, modify the tail of Linear L.
*/
Status Remove(LinkList &L, Link &q)
{
	Link s;
	q = L.tail;
	s = L.head->next;
	while(s->next != L.tail)
	{
		s = s->next;
	}
	FreeNode(L.tail);
	s->next = NULL;
	L.tail = s;
	return OK; 
}

/*
Description: add the node pointed by s to the position that is previous to the node pointed by p in the Linear List L.
			 modify the point p and make it pointed to the new inserted node.
*/
Status InsBefore(LinkList &L, Link &p, Link s)
{
	Link k;
	k = L.head->next;
	while(k->next != p)
	{
		k = k->next;
	}
	k->next = s;
	s->next = p;
	p = s;
	return OK;
}

/*
Description: Insert the node into the position after the node pointed by p. Modify the point p.
*/
Status InsAfter(LinkList &L, Link &p, Link s)
{
	s->next = p->next;
	p->next = s;
	p = s;
	return OK;
}

/*
Description: renew the element pointed by p with e.
*/
Status SetCurElem(Link &p, ElemType e)
{
	p->data = e;
	return OK;
}

/*
Description: return the element pointed by p.
*/
ElemType GetCurElem(Link p)
{
	return p->data;
}

/*
Description: if List L is a vacant list, return true, or return false.
*/
Status ListEmpty(LinkList L)
{
	if(L.len == 0)
	{
		return OK;
	}
	else
	{
		return ERROR;
	}
}

/*
Description: return the number of the element in the Linear List.
*/
int GetListLength(LinkList L)
{
	return L.len;
}

/*
Description: Get the position of the head node in the Linear List.
*/
Position GetHead(LinkList L)
{
	return L.head;
}

/*
Description: Get the position of the last node in the Linear List.
*/
Position GetLast(LinkList L)
{
	return L.tail;
}

/*
Description: Get the position which is previous to the node pointed by p.
*/
Position PriorPos(LinkList L, Link p)
{
	Link k;
	k = L.head;
	while(k->next != p && k->next != NULL)
	{
		k = k->next;
	}
	if(k->next == p)
	{
		return k;
	}
	else 
	{
		return NULL;
	}
}

/*
Description: Get the position next to the node pointed by p.
*/
Position NextPos(LinkList L, Link p)
{
	if(p->next != NULL)
	{
		return p->next;
	}
	else 
	{
		return NULL;
	}
}

/*
Description: return the position of the node i in the Linear List using p and return OK. if i is invalid, return ERROR. 
*/
Status LocatePos(LinkList L, int i, Link &p)
{
	int j = 1;
	Link k = L.head->next;
	if(i<1 || i>L.len)
	{
		return ERROR;
	}
	while(k!= NULL && j != i)
	{
		j++;
		k = k->next;
	}
	p = k;
	return OK;
}

/*
Description: return the position of the node whose element fits the  compare relationship with element e in the list. 
*/
Position LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
	Link k;
	k = L.head->next;
	while(!compare(k->data,e) && k!= NULL)
	{
		k = k->next;
	}
	if(k == NULL)
	{
		return NULL;
	}
	return k;
}

/*
Description: use the function visit() to process every element in the List. If visit() returns ERROR, then the function return
			 ERROR.
*/
Status ListTraverse(LinkList L, Status(*visit)(ElemType))
{
	Link s;
	s = L.head->next;
	while((visit(s->data)) && (s!=NULL))
	{
		s = s->next;
	}
	if(s == NULL)
	{
		return OK;
	}
	else
	{
		return ERROR;
	}
}
以下是一个测试程序:
已知链表La,Lb中的元素按照非递减的顺序排列,归并La,Lb到新的链表Lc,使Lc中的元素也按照非递减的顺序排列。
int compare(ElemType a, ElemType b)
{
	if(a <= b)
	{
		return ERROR;
	}
	else
	{
		return OK;
	}
}
/*
Description: insert the elment e into the position i in the List
*/
Status ListInsert_L(LinkList &L, int i, ElemType e)
{
	Link h,s;
	if(!LocatePos(L,i-1,h))
	{
		return ERROR;
	}
	if(!MakeNode(s,e))
	{
		return ERROR;
	}
	InsFirst(h,s);
	return OK;
}

/*
Description: Merge La and Lb into List Lc. The elements in Lc grow from small to big.
*/
Status MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc, int(*compare)(ElemType, ElemType))
{
	Link ha = NULL;
	Link hb = NULL;
	Link pa = NULL;
	Link pb = NULL;
	Link q = NULL;
	int a,b;
	if(!InitList(Lc))
	{
		return ERROR;
	}
	ha = GetHead(La);
	hb = GetHead(Lb);
	pa = NextPos(La,ha);
	pb = NextPos(Lb,hb);
	while(pa && pb)
	{
		a = GetCurElem(pa);
		b = GetCurElem(pb);
		if((*compare)(a,b) <= 0)
		{
			DelFirst(ha,q);
			Append(Lc,q);
			pa = NextPos(La,ha);
		}
		else
		{
			DelFirst(hb,q);
			Append(Lc,q);
			pb = NextPos(Lb,hb);
		}
	}
	if(pa)
	{
		Append(Lc,pa);
	}
	else
	{
		Append(Lc,pb);
	}
	FreeNode(ha);
	FreeNode(hb);
	return OK;
}

void main(void)
{
	int i = 0;
	int ea = 6;
	int eb = 4;
	Link p = NULL;
	Link s = NULL;
	LinkList La,Lb,Lc;
	La.head = NULL;
	La.tail = NULL;
	Lb.head = NULL;
	Lb.tail = NULL;
        //Creat the list La,Lb.
        InitList(La); 
	InitList(Lb);
	for(i=0;i<ListLength;i++)
	{
		MakeNode(p,ea++);
		Append(La,p);
		La.len++;
	}
	for(i=0;i<ListLength;i++)
	{
		MakeNode(p,eb++);
		Append(Lb,p);
		Lb.len++;
	}
	p = La.head->next;
	while(p!=NULL)
	{
		cout << p->data;
		p = p->next;
	}
	cout<< endl;
	p = Lb.head->next;
	while(p!=NULL)
	{
		cout << p->data;
		p = p->next;
	}
	cout<< endl;
	MergeList_L(La,Lb,Lc,compare);//the function name can be used as the parameter to transfer.
	s = Lc.head->next;
	while(s != NULL)
	{
		printf("%d",s->data);
		s = s->next;
	}

}



在过程中碰到的问题都是关于地址的,链表中最重要的也是地址问题。非常重要的一点是,因为内存是由我们自己分配和管理的,如果我们不进行释放,那么内存中的数据是不会改变的。我们是按照 地址来操作该地址所对应的内存空间中的内容。
       




       

猜你喜欢

转载自blog.csdn.net/Davidsdu/article/details/19350915