从【C和指针】这本书上看到在单向链表中插入节点时可以用一个二级指针来遍历,这个二级指针最初指向链表的头指针,随后的遍历过程中指向每个节点的pNext成员(pNext成员是指向下一个节点的指针),这样就可以将头指针看成一个空节点中的pNext成员,而不需要像使用一级指针那样将头指针作为一种特殊情况来考虑。也就是说使用二级指针后在链表的头部、中间、尾部插入节点的操作都可以看做同一种情况来处理,可以使用同一种操作方法,简化了插入函数。
类比到排序算法中,以插入排序为例,也可以使用二级指针来进行优化,当然由于链表实现的插入排序是直接将节点进行移动(不同于数组),所以每一次循环中是否有插入操作发生会产生两种不同的情况,需分开讨论,不过也只需加一个flag判断即可,具体在例程的注释中有体现,当然涉及较多的指针操作时,需要注意对NULL指针执行解引用操作会发生错误,但编译器不会报错(例如插入排序函数的最后一个if就是为了避免这样的错误)。
下面是例程:
#include
#include
typedef struct node{
int num;
struct node* pNext;
}Node;
#define SIZE sizeof(Node)
//
//创建一个链表并以输入为-1结束,返回链表头指针
//
Node* CreateList(){
int val;
Node *pHead, *pNode, *pTail;
pHead = pNode = pTail = NULL;
scanf("%d", &val);
while(val != -1){
pNode = (Node*)malloc(SIZE);
pNode->num = val;
pNode->pNext = NULL;
if(pHead == NULL)
pHead = pNode;
else
pTail->pNext = pNode;
pTail = pNode;
scanf("%d", &val);
}
return pHead;
}
//
//输出整个链表
//
void OutputList(Node *pHead){
while(pHead != NULL){
printf("%d ", pHead->num);
pHead = pHead->pNext;
}
printf("\b");
printf("\n");
}
//
//计算链表长度并返回节点数
//
int ListLength(Node *pHead){
int count = 0;
while(pHead != NULL){
count++;
pHead = pHead->pNext;
}
return count;
}
//
//删除所有值为val的节点并返回值1,若无值为val的节点则返回0
//
int DeleteNode(Node **ppHead, int val){
Node **ppNode = ppHead;
Node *pNode;
int flag = 0;
while(*ppNode != NULL)
if((*ppNode)->num == val){
pNode = *ppNode;
*ppNode = (*ppNode)->pNext;
free(pNode);
flag = 1;
}
else
ppNode = &((*ppNode)->pNext);
return flag;
}
//
//在链表第index个节点前插入值为val的节点并返回1,
//若链表长度不足index+1则返回0
//
int InsertNode(Node **ppHead, int val, int index){
Node **ppNode = ppHead;
Node *pNode;
int i;
//判断是否超出数列范围,其中需考虑包括数列末尾位置
if(ListLength(*ppHead) < index - 1)
return 0;
for(i = 1; i < index; i++)//找到插入位置
ppNode = &((*ppNode)->pNext);
pNode = (Node*)malloc(SIZE);
pNode->num = val;
pNode->pNext = *ppNode;
*ppNode = pNode;
return 1;
}
//
//升序插入排序
//
void InsertSort(Node **ppHead){
Node **ppNode = &((*ppHead)->pNext), **ppCursor;
Node *pNode;
//标记是否有插入发生
int flag;
while(*ppNode != NULL){
//每次循环均从头开始寻找插入位置
ppCursor = ppHead;
flag = 0;
while(ppCursor != ppNode)
if((*ppCursor)->num >= (*ppNode)->num){
//用一中间变量pNode指向需要被插入到前面已排序好的数列中的节点
pNode = *ppNode;
*ppNode = (*ppNode)->pNext;
pNode->pNext = *ppCursor;//插入节点
*ppCursor = pNode;
//*ppNode指向的节点被插入到前面已排序好的数列中
//则循环末尾无需移动ppNode即可进行下一节点的插入操作
flag = 1;
break;
}
else
ppCursor = &((*ppCursor)->pNext);
//*ppNode指向的节点没有被插入到前面已排序好的数列中
//则需使ppNode指向下一个节点的pNext成员
if(*ppNode != NULL&&flag == 0)
ppNode = &((*ppNode)->pNext);
}
}
//
//生成倒序链表
//
Node* ReverseList(Node *pHead){
Node *rpHead = NULL, *pNode;
while(pHead != NULL){
pNode = (Node*)malloc(SIZE);
pNode->num = pHead->num;
pNode->pNext = rpHead;
rpHead = pNode;
pHead = pHead->pNext;
}
return rpHead;
}
int main(void){
Node *ptrHead, *rPtrHead;
int DeleteNumber, InsertNumber, Index;
printf("输入一个数列并以-1结束\n");
ptrHead = CreateList();
printf("输入要删除的数\n");
scanf("%d", &DeleteNumber);
if(DeleteNode(&ptrHead, DeleteNumber))
OutputList(ptrHead);
else
printf("Delete Failure!\n");
printf("输入要插入的数及将要插入该数在第几节点前\n");
scanf("%d%d", &InsertNumber, &Index);
if(InsertNode(&ptrHead, InsertNumber, Index))
OutputList(ptrHead);
else
printf("Insert Failure!\n");
printf("倒序输出原链表\n");
rPtrHead = ReverseList(ptrHead);
OutputList(rPtrHead);
printf("进行插入排序\n");
InsertSort(&ptrHead);
OutputList(ptrHead);
}