上一篇博文中主要总结线性表中的链式存储结构实现,比如单向链表、循环链表,还通过对比链表和顺序表的多项式的存储表示,说明链表的优点。可以参看上篇博文http://blog.csdn.net/lg1259156776/article/details/47018813
下面先对没有介绍的链表中的双链表进行介绍,并通过稀疏矩阵的三元组的链式结构来深入理解较为复杂的链表存储结构。最后对三次博文所讲述的内容进行梳理,以帮助在实际应用中选择最合适的存储结构:顺序表和链表,来组织实现自己的算法和功能。
双向链表
下面用示意图对比一下几种链表的表示:
单向链表表头
单向链表的结构如下:
循环链表的结构如下:
在循环单链表中,从任意一个结点出发,沿着向后链能够访问到表中的所有元素;
对循环链表的操作与普通单链表基本相同,只是判表空和判断表尾元素的条件有所不同
判断表尾:p->next == head
判断表空:head->next == head
双向链表
指在前驱和后继方向都能遍历的线性链表,每个结点结构如下图所示:
双向链表结构如下:
双向链表的优点是:实现双向查找(单链表不容易做到),缺点是:空间开销大。
双向链表通常采用带表头结点的循环链表形式,结构如下:
双向链表结点CDNode的抽象数据类型:
typedef struct CDNode
{
DataType m_dData;
CDNode *m_pNext, *m_pPrev;
}
CDNode的基本操作如下:
void SetDNode(DNode *front);
DNode *GetPrev(DNode *ptr);
DNode *GetNext(DNode *ptr);
void InsertPrev(DNode *pNode);
void InsertNext(DNode *pNode);
Void DeleteDNode(DNode *ptr);
双向链表CDList的实现:
typedef CDList
{
CDNode *m_pHead;
int Size;
}
CDList的基本操作:
Void SetDLList();//构造
Void FreeDLList();//析构
int DLListSize ();
int DLListIsEmpty();//
int DLListLocate();
DataType DLListGetData();
void DLListInsert();
void DLListDelete();
针对几个比较抽象的操作进行图例说明:
********************************************************************************************
注:在链表的操作中要注意修改指针的顺序
********************************************************************************************
有序表
有序表的插入
在有顺序表L中插入新的结点,使得L仍然有序
void OrderInsert( SqList L, ElemType x ) {
//L非递减有序的顺序表,插入新元素x,使L仍然有序
i=L.length-1; //i指向表尾元素
while(i>=0 && x<L.elem[i]) {
L.elem[i+1]=L.elem[i]; //大于x的元素右移
i--;
} //while
L.elem[i+1]=x; L.length++;
} //OrderInser
void OrderInsert( LinkList *L, ElemType e ) {
//L是带头结点且非递减有序的单链表,插入新元素e,使L仍然有序
p=L;
while(p->next!=NULL &&p->next->data<e)
p=p->next; //找插入位置
s=(LNode*)malloc(sizeof(LNode));
s->next=p->next; s->data=e;
p->next=s;
} //OrderInsert O(n)
有序表的合并
使得合并后的表仍然有序:
void MergeList( LinkList *La, LinkList *Lb ) {
//La和Lb是带头结点且非递减有序的单链表,将他们归并成Lc
pa=La->next; pb=Lb->next;
Lc=pc=La; //La的头结点作为Lc的头结点
while(pa&& pb)
if(pa->data<=pb>data)
{
pc->next=pa; pc=pa; pa=pa->next;
}
else {
pc->next=pb; pc=pb; pb=pb->next;
}
pc->next = pa? pa : pb;
delete Lb;
}
链表应用:稀疏矩阵
稀疏矩阵:只有少数非 0 矩阵元素
顺序结构:
三元组顺序表: (i,j,data)
Typedef struct{
Int I,j;
DataType item;
}Triple;
Typedef struct{
Triple data[MaxSize+1];
Int mu,nu,tu;
}TSMatrix;
在矩阵操作时矩阵非零元素会发生动态变化,用稀疏矩阵的链接表示可适应这种情况
稀疏矩阵的链接表示采用正交链表:行链表列链表十字交叉
行链表与列链表都是带表头结点的循环链表用表头结点表征是第几行,第几列。
稀疏矩阵的行列表头结点结构:0行0列表头结点共用一个表头结点。
列表头:next: 给出下一个列表头结点地址;down:给出本列中第一个非0矩阵元素结点地址
行表头:right:给出本行中第一个非0矩阵元素结点地址
给出稀疏矩阵的正交链表表示的图示:
数据结构的实现:
type struct OLNode
{
int i,j;//row,col
DataType e;
struct OLNode *rnext,*cnext;
}OLNode,OLink
typedef struct{
OLink *rhead,*chead;
int m,n,t;
}
线性表总结
以下几个纲要是线性表的最主要的内容:
<1> 线性表的逻辑结构和各种存储表示方法
<2> 定义在逻辑结构上的各种基本运算(操作)
<3> 在各种存储结构上如何实现这些基本运算
<4> 各种基本运算的时间复杂性
<5> 线性表的长度能否预先确定?处理过程中变化范围如何?
长度确定、变化小时用顺序表
长度变化大、难以估计最大长度时宜采用链表
<6> 对线性表的操作形式如何?
查询操作多、删除和插入操作少时使用顺序表
频繁插入和删除操作宜采用链表
*************************************************************************************************************************************