线性链表是数据结构中的第一个概念, 也是最基本的概念。如果对线性链表的操作不熟悉,就说明你对指针、算法等不甚了解,或者说对C语言的精髓、算法的初步都未入门。下文给出的一个线性表的示例,给大家演示线性表的操作,让大家熟悉对指针、链表的操作。该程序中大部分的代码都是标准操作,各种数据结构相关的教科书中都有,不过采用的不一定是C语言而已,但不是很难,所以不加任何介绍,只是在程序中给出响应的注释。不过有一段特别的代码,就是将链表倒置的代码
该函数的功能就是将线性表倒置。大家别小看这么一个操作,如果是顺序表(数组),这个操作是相当简单的,但是对于链表,可得想好了,否则,上机调试越调越乱〔其实所有的链表操作都是如此,如果事先没有想好就上机,除了浪费时间好像没有多大的意义〕。当然链表倒置操作方法不止一种,我们在此采用一个类似递归的的思想(别看见递归就头疼,你当我没说过递归)。
链表倒置,我们将链表看做两个表,LA和LB。其中LA是原表,LB是已经将LA前面的一部分已经倒置的线性表。我们操作的过程用语言描述就是一句话:将LA的第一个结点插入到LB的最前面。所以操作过程分开写就是:
1) 将LA指向当第一个结点插入到LB的第一个结点
2) 将LB指向刚插入的结点
3) LA后移动一个
这样循环操作直至LA指向空
当然实际操作中考虑到是否有头结点(程序中是使用头结点的),我们把原代码贴出来,然后逐一解释
... {
List p = L, q = L->next;
while (q != NULL) ...{
L->next = q->next;
q->next = p;
if (q->next == L)
q->next = NULL;
p = q;
q = L->next;
}
L->next = p;
}
p=L, q=L->next; 该操作是相当于q为LA,p为LB;
while(q!=NULL) 表示当LA不为空,也就是还有有效结点时继续循环
L->next = q->next; 将q的下一个结点插入到L的第一个结点(第0个为头结点)
q->next = p; 重新接好链表(否则链表就断了)
if(q->next==L) q->next = NULL; 不用看,这肯定是放在最后的,实际上也就是插入的第一个结点后要将“下一位”赋空,否则会死得很惨的。
p = q;
q = L->next; 重新赋值,也就是保证q总是在最开始,q是新表的第一个结点
我们可以看下图
下面是程序代码,以及运行结果
#include < malloc.h >
#pragma once
#define MAXINFOLEN 128
typedef struct _ElemType ... {
int m_nKey; // 关键字
char m_sInfo[MAXINFOLEN]; // 消息
} ElemType;
void Elem_Print(ElemType * e);
// Houjian TANG @ 20070330
typedef struct _NodeType
... {
ElemType *elem; // 节点元素指针
struct _NodeType *next; // 下一个节点
} NodeType, * List;
typedef int ( * COMPARE)(ElemType * e1, ElemType * e2);
int compare_key(ElemType * e1, ElemType * e2);
int compare_info(ElemType * e1, ElemType * e2);
// 打印一个节点
void LNode_Print(NodeType * e);
// 打印一个链表
void List_Print(List L);
// 初始化一个带头结点的链表,返回链表的头
List List_Init();
// 将元素e插入到链表L的pos位置处,如果pos<0或非常大,则插入到最后
void List_Insert(List L, int pos, ElemType * e);
// 删除链表L的pos处的节点,如果pos值非常大或者pos<0,返回失败
// 删除成功返回true,并将被删除的元素放到e指向的单元中(e!=NULL)
bool List_Delete(List L, int pos, ElemType * e);
int List_Locate(List L, COMPARE compare, ElemType * e);
List List_GetNode(List L, int pos);
bool List_Delete(List L, int pos, ElemType * e);
void List_Destroy(List L);
void List_Reverse(List L);
void List_Test();
int main()
... {
List_Test();
return 0;
}
void List_Test()
... {
List L;
ElemType e;
int i = 0;
L = List_Init();
// 建立5个节点的链表
printf("Create a link with 5 nodes: ");
for (i=0; i<5; i++) ...{
e.m_nKey = i;
sprintf(e.m_sInfo, "Elem Node %d", i);
List_Insert(L, i, &e);
}
List_Print(L);
printf("Reverse this link: ");
List_Reverse(L);
List_Print(L);
printf("Call Search(e.m_nKey==3): ");
e.m_nKey = 3;
i = List_Locate(L, compare_key, &e);
if (i >= 0) ...{
printf("founded! p = ");
LNode_Print(List_GetNode(L, i));
printf(" ");
printf("Delete this node: ");
List_Delete(L, i, &e);
List_Print(L);
}
else ...{
printf("Not founded! ");
}
printf("Call Search(e.m_sInfo=='Elem Node 4'): ");
sprintf(e.m_sInfo, "Elem Node 4");
i = List_Locate(L, compare_info, &e);
if (i >= 0) ...{
printf("founded! p = ");
LNode_Print(List_GetNode(L, i));
printf(" ");
printf("Delete this node: ");
List_Delete(L, i, &e);
List_Print(L);
}
else ...{
printf("Not founded! ");
}
e.m_nKey = 30;
sprintf(e.m_sInfo, "an other node");
printf("Insert a node = (%d, '%s') ",
e.m_nKey, e.m_sInfo);
List_Insert(L, 2, &e);
List_Print(L);
List_Destroy(L);
}
void LNode_Print(NodeType * e)
... {
printf("{%pH: || e=", e);
Elem_Print(e->elem);
printf("] next->%pH)", e->next);
}
void List_Print(List L)
... {
printf("// ------------------------------ ");
while ((L = L->next) != NULL) ...{
LNode_Print(L);
printf(" ");
}
printf("------------------------------ // ");
}
// 分配头节点
List List_Init()
... {
List L = (NodeType *)malloc(sizeof(NodeType));
L->next = NULL;
L->elem = (ElemType *)malloc(sizeof(ElemType));
// L->elem = NULL;
//L->elem->m_nKey = 0;
//L->elem->m_sInfo[0] = 0;
return L;
}
void List_Insert(List L, int pos, ElemType * e)
... {
List p = L, lTmp;
int i;
// 如果是负数,则插入到最后
if (pos < 0) ...{
while (p->next != NULL) // 该循环是将tail指向列表的末尾
p = p->next;
}
// 查找的pos个位置
for (i=0; i<pos; i++) ...{
if (p->next == NULL) // 如果已经到了末尾,就不继续了
break;
p = p->next;
}
// 先申请链表结点
lTmp = (NodeType *)malloc(sizeof(NodeType));
// 再分配链表结点的内容
lTmp->elem = (ElemType *)malloc(sizeof(ElemType));
*(lTmp->elem) = *e;
// 注意下面这两行,实现了链表的连接
lTmp->next = p->next;
p->next = lTmp;
}
bool List_Delete(List L, int pos, ElemType * e)
... {
List p = L, q = NULL;
int i = 0;
if (pos < 0)
return false;
for (i=0; i<pos; i++)...{
if (p->next->next == NULL)
return false;
p = p->next;
}
// 下面这两句是去掉q所指向的元素
q = p->next;
p->next = q->next;
if (e != NULL)
*e = *(q->elem);
// 注意一定要释放
free(q->elem);
q->next = NULL;
q->elem = NULL;
free(q);
return true;
}
void List_Destroy(List L)
... {
List p = L;
while (L != NULL) ...{
p = L;
L = L->next;
free(p->elem);
p->next = NULL;
p->elem = NULL;
free(p);
}
}
int List_Locate(List L, COMPARE compare, ElemType * e)
... {
int i = 0;
L = L->next;
while (L != NULL) ...{
if (compare(L->elem, e) == 0)
return i;
L = L->next;
i ++;
}
return -1;
}
void Elem_Print(ElemType * e)
... {
printf("{%03d: '%s'}", e->m_nKey, e->m_sInfo);
}
List List_GetNode(List L, int pos)
... {
L = L->next;
while (L != NULL) ...{
pos --;
if (pos < 0)
return L;
L = L->next;
}
return NULL;
}
void List_Reverse(List L)
... {
List p = L, q = L->next;
while (q != NULL) ...{
L->next = q->next;
q->next = p;
if (q->next == L)
q->next = NULL;
p = q;
q = L->next;
}
L->next = p;
}
int compare_key(ElemType * e1, ElemType * e2)
... {
return e1->m_nKey - e2->m_nKey;
}
int compare_info(ElemType * e1, ElemType * e2)
... {
return strcmp(e1->m_sInfo, e2->m_sInfo);
}
/**/ /*
运行结果:
// ------------------------------
{0x4F1EF0: elem={0: 'Elem Node 0'}, next=0x4F1E00}
{0x4F1E00: elem={1: 'Elem Node 1'}, next=0x4F1D10}
{0x4F1D10: elem={2: 'Elem Node 2'}, next=0x4F1C20}
{0x4F1C20: elem={3: 'Elem Node 3'}, next=0x4F1B30}
{0x4F1B30: elem={4: 'Elem Node 4'}, next=0x0}
------------------------------ //
// ------------------------------
{0x4F1B30: elem={4: 'Elem Node 4'}, next=0x4F1C20}
{0x4F1C20: elem={3: 'Elem Node 3'}, next=0x4F1D10}
{0x4F1D10: elem={2: 'Elem Node 2'}, next=0x4F1E00}
{0x4F1E00: elem={1: 'Elem Node 1'}, next=0x4F1EF0}
{0x4F1EF0: elem={0: 'Elem Node 0'}, next=0x0}
------------------------------ //
founded!
p = {0x4F1C20: elem={3: 'Elem Node 3'}, next=0x4F1D10}
// ------------------------------
{0x4F1B30: elem={4: 'Elem Node 4'}, next=0x4F1D10}
{0x4F1D10: elem={2: 'Elem Node 2'}, next=0x4F1E00}
{0x4F1E00: elem={1: 'Elem Node 1'}, next=0x4F1EF0}
{0x4F1EF0: elem={0: 'Elem Node 0'}, next=0x0}
------------------------------ //
--------------------------------
{0: 'ElemType 0'}
{1: 'ElemType 1'}
{2: 'ElemType 2'}
{3: 'ElemType 3'}
{4: 'ElemType 4'}
{5: 'ElemType 5'}
{6: 'ElemType 6'}
{7: 'ElemType 7'}
{8: 'ElemType 8'}
{9: 'ElemType 9'}
--------------------------------
{9: 'ElemType 9'}
{8: 'ElemType 8'}
{7: 'ElemType 7'}
{6: 'ElemType 6'}
{5: 'ElemType 5'}
{4: 'ElemType 4'}
{3: 'ElemType 3'}
{2: 'ElemType 2'}
{1: 'ElemType 1'}
{0: 'ElemType 0'}
Found! ElemType is : {6: 'ElemType 6'}
*/