链表定义比较简单,在此就不介绍了。简单地说明一下设计链表时一些细节,并附上源代码,希望对读者有帮助。链表分为单链表、双链表和循环链表。
在设计链表时建议留出一个标志节点,即表头或哑节点,这样方便删除操作,特别是在设计栈和队列时特别有用。
单链表数据结构和接口定义:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;
List createList();
void deleteList(List L);
void insertNode(ElementType X, List L, Position P);
void deleteNode(ElementType X, List L);
Position findNode(ElementType X, List L);
Position findPreNode(ElementType X, List L);
struct Node
{
ElementType Element;
Position next;
};
/* 创建有表头的单链表 */
List createList()
{
List L = (List)malloc(sizeof(struct Node));
if(L == NULL)
return NULL;
L->next = NULL;
return L;
}
void deleteList(List L)
{
Position p;
Position tmp;
if(L == NULL)
return;
p = L->next;
free(L);
while(p != NULL)
{
tmp = p;
p = p->next;
free(tmp);
}
return;
}
void insertNode(ElementType X, List L, Position P)
{
Position tmp;
tmp = (Position)malloc(sizeof(struct Node));
if(tmp == NULL)
return;
tmp->Element = X;
tmp->next = P->next;
P->next = tmp;
return;
}
void deleteNode(ElementType X, List L)
{
Position p;
Position tmp;
p = findPreNode(X, L);
if(p == NULL)
return;
tmp = p->next;
p->next = tmp->next;
free(tmp);
return;
}
Position findPreNode(ElementType X, List L)
{
Position p;
p = L;
while(p->next != NULL && p->next->Element != X)
p = p->next;
if(p->next == NULL)
return NULL;
return p;
}
Position findNode(ElementType X, List L)
{
Position p;
p = L->next;
while(p != NULL && p->Element != X)
p = p->next;
return p;
}
简单写了一个测试程序,读者可自行改进:
void printList(List L)
{
Position P;
if(L == NULL || L->next == NULL)
return;
P = L->next;
while(P != NULL)
{
printf("%d", P->Element);
P = P->next;
}
printf("\n");
return;
}
int main()
{
List L;
Position P;
L = createList();
if(L == NULL)
return 0;
P = L;
for(int i=0; i<10; i++)
{
insertNode(i, L, P);
}
printList(L);
deleteNode(6, L);
printList(L);
insertNode(8, L, P);
printList(L);
deleteList(L);
system("pause");
return 0;
}
双链表在设计时对每个节点添加了一个前继指针,这增加了空间的需求,同时也使得插入和删除的开销增加了一倍。
双链表的具体实现如下:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct DNode *PtrToDNode;
typedef PtrToDNode DList;
typedef PtrToDNode Position;
DList createDList();
void deleteDList(DList L);
void insertDNode(ElementType X, DList L, Position P);
void deleteDNode(ElementType X, DList L);
Position findDNode(ElementType X, DList L);
struct DNode
{
ElementType Element;
Position pre;
Position next;
};
/* 创建有表头的单链表 */
DList createDList()
{
DList L = (DList)malloc(sizeof(struct DNode));
if(L == NULL)
return NULL;
L->pre = NULL;
L->next = NULL;
return L;
}
void deleteDList(DList L)
{
Position p;
Position tmp;
if(L == NULL)
return;
p = L->next;
free(L);
while(p != NULL)
{
tmp = p;
p = p->next;
free(tmp);
}
}
void insertDNode(ElementType X, DList L, Position P)
{
Position tmp;
tmp = (Position)malloc(sizeof(struct DNode));
if(tmp == NULL)
return;
tmp->Element = X;
tmp->pre = P;
tmp->next = P->next;
if(P->next != NULL)
P->next->pre = tmp;
P->next = tmp;
return;
}
void deleteDNode(ElementType X, DList L)
{
Position p;
Position pre;
Position next;
p = findDNode(X, L);
if(p == NULL)
return;
pre = p->pre;
next = p->next;
pre->next = next;
next->pre = pre;
free(p);
return;
}
Position findDNode(ElementType X, DList L)
{
Position p;
p = L->next;
while(p != NULL && p->Element != X)
p = p->next;
return p;
}
测试程序:
void printDList(DList L)
{
Position P;
P = L->next;
while(P != NULL)
{
printf("%d", P->Element);
P = P->next;
}
printf("\n");
return;
}
int main()
{
DList L;
Position P;
L = createDList();
if(L == NULL)
return 0;
P = L;
for(int i=0; i<10; i++)
{
insertDNode(i, L, P);
}
printDList(L);
deleteDNode(6, L);
printDList(L);
insertDNode(6, L, P);
printDList(L);
deleteDList(L);
system("pause");
return 0;
}
表的应用:
(1)多项式ADT,使用数组或单链表。在项目比较少却次数高时用单链表比较好。
(2)基数排序。也称为卡式排序。基数排序是桶式排序的推广,即多趟桶式排序。
(3)多重表。将两个表合成一个表。所有的表都各有一个表头并且都是循环的。
(2)基数排序。也称为卡式排序。基数排序是桶式排序的推广,即多趟桶式排序。
(3)多重表。将两个表合成一个表。所有的表都各有一个表头并且都是循环的。