您的位置:首页 > 理论基础 > 数据结构算法

【数据结构与算法】——链表篇

2012-01-16 15:49 357 查看
链表属于线性结构之一,主要功能是提供可动态扩展的线性结构,可使用不连续的的内存空间,为程序的动态特性提供支持。逻辑结构如下(图片引用自CSDN博客)



一般的定义如下

/*The Data Structure of Link List*/
typedef int DataType;
typedef struct LinkNode
{
DataType         data;
struct LinkNode *next;
}*LinkList;
其中data用于存放数据,*next指向下一个结点,注意这个定义有递归的含义,因为*next的类型是LinkNode,这是在用自己定义自己,初学时也许会有点儿费解,但只要明白编译器仍然将其理解为一个指针就会清晰许多。下面给出链表的基本算法:(默认带头结点)
/*尾插法建立链表,链表元素顺序和数组一致*/
void CreateList(LinkList &L,DataType A[],int n)
{
LinkNode *rear;
L = rear = new LinkNode;
for(int i=0; i<n; ++i)
{
rear->next =  new LinkNode;
rear       =  rear->next;
rear->data = A[i];
}
rear->next = NULL;
}


/*头插法建立链表,链表元素顺序和数组相反*/
void CreateList_reverse(LinkList &L,DataType A[], int n)
{
L = new LinkNode;
L->next = NULL;

for( int i=0; i<n; ++i)
{
LinkNode *p = new LinkNode;
p->data = A[i];
p->next = L->next;
L->next = p;
}
}


/*链表逆置算法*/
void ReverseList(LinkList &L)
{
LinkNode *pre = NULL;    /*two temporary node is needed to traverse the LinkList*/
LinkNode * p  = L->next;

while( p != NULL)
{
L->next = p->next;   /* p->next will change,so store it in the head node*/
p->next = pre;

pre = p;             /* pre point to the new head */
p   = L->next;       /* p point to the next position */
}
L->next = pre;           /* L-next point to the head of the reversed list*/
}


/*删除链表中的元素x*/
void DeleteList(LinkList &L,DataType x)
{
for(LinkNode *pre=L,*p=L->next;  p!=NULL; pre=p,p=p->next )
if( p->data == x)
{
pre->next = p->next;
delete p;
return;
}
}


/*插入链表元素x*/
void InsertList(LinkList &L ,DataType x)
{
LinkNode *p = new LinkNode;
p->data = x;
p->next = L->next;
L->next = p;
}


/*显示链表所有元素,测试时比较有用*/
void DisplayList(LinkList &L)
{
cout<<endl;
for( LinkNode *p = L->next;  p != NULL;  p = p->next )
{ 	 cout<< p->data<<" ";	}
cout<<endl;
}
/*链表的排序算法*/
void InsertSortList(LinkList &L)
{
LinkNode *p     = L->next;        /*指向 待插入  结点 */
LinkNode *nextp = NULL;           /*指向 p的后继 结点 */

LinkNode *q     = L;              /*指向有序链表的头部*/
q->next         = NULL;

while( p != NULL )
{
q = L;
while(q->next!=NULL && p->data >= q->next->data) /*寻找插入位置,循环停止时,p应插入到q的后面*/
{	q = q->next;		}

nextp = p->next;
p->next = q->next;    /* p 的后继应是 q */
q->next = p;
p = nextp;
}
}
/*链表查找算法*/
LinkNode *SearchList(LinkList &L,DataType x)
{
LinkNode *p = L->next;
while( p!=NULL && p->data!=x )
{	p = p->next;	}
return p;
}
/*链表销毁算法*/
void DestroyList(LinkList &L)
{
if(L->next==NULL)
{
cout<<"delete"<<L->data<<endl;
delete L;	}
else
{
DestroyList(L->next);
cout<<"delete"<<L->data<<endl;
delete L;
}
}


上面给出了链表的的“创建”、“销毁”、 “插入”、“删除”、“查找”、 “逆置”、“遍历”、“排序”算法,这些基本算法可以构建起链表的几乎所有操作,下面给出基本测试代码:

int main()
{

int A[6]={1,2,3,4,5,6};
LinkList L;

CreateList (L,A,6);
DisplayList(L);

ReverseList(L);
DisplayList(L);

InsertList (L,9);
DisplayList(L);

DeleteList (L,5);
DisplayList(L);

cout<<"after sort:";
InsertSortList(L);
DisplayList   (L);
cout<<endl;

LinkNode *p = SearchList(L,6);
cout<<"search result:"<<p->data<<endl;

cout<<"delete processing:"<<endl;
DestroyList(L);

system("pause");
return 0;
}

其实链表算法的核心是两点:

(1)熟悉辅助结点的使用,我们访问链表必须通过辅助结点, for( LinkNode *p=L; p!=NULL ; p=p->next ) 与 for( int i=0 ; i<length ; ++i ) 并没有本质的区别。

(2)熟悉操作通过前驱结点的方式,链表的操作往往需要找到或者记录当前结点的前驱,这也许是陌生感的真正原由。 所以链表算法中经常看到nextp,nextq,prep,preq这样的辅助结点和p,q一起使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: