本文不单单只是讲单链表的操作,更重要的是通过写一个简单的有序单链表操作,来分析如何写出体积更小,效率高的代码。优化代码,本来就是有能正确解释问题的能力,通常来说,对认识一个问题都有比较深入的了解。否则,只能写出一些额外的代码。
在一个单链表中,每个节点包含指向链表下一个节点的指针,链表最后一个节点的指针值为NULL,表示链表后面不在有其它节点。根据单链表的特点,从链表中,找到第一个节点后,就可以访问剩下的节点。可以设置一个头节点,作为链表的头部,找到头结点可以访问该链表,除头结点以外的所有节点,也可以设置一个根指针,指向该链表的第一个节点。注意,该根指针不包含任何数据。因为单链表只能前一个节点指向后一个节点,所以操作单链表时候,一般需要两个指针配合。
下面结合例子说明。
例1:实现一个有序(降序)单链表。
思路分析:
算法实现还是比较简单。按顺序逐一访问链表节点,新值与当前的点比较,如果比当前的点的值大,则停下来,比如新值为11 ,当前点10,停下来时候,当前点已经移到15.单链表又不能倒退,所以解决方法就是保存一个当前节点的前一个节点的指针。一前一后两个指针移动。
第一次写的代码:
//有序单链表插入
int ListOrderInsert(NODE *pRoot,int lNewValue)
{
NODE *pstCurrent = NULL;
NODE *pstPreviours = NULL;
NODE *pstNew = NULL;
if( NULL == pRoot)
{
printf("pRoot Null\n");
return -1;
}
pstPreviours = pRoot;
pstCurrent = pRoot->next;
//新手容易忽略pstCurrent != NULL判断,会导致循环时候越过链表尾部,pstCurrent = NULL而对NULL访问是非法的。
while(pstCurrent != NULL && pstCurrent->lvalue > lNewValue )
{
pstPreviours = pstCurrent;
pstCurrent = pstCurrent->next;
}
pstNew = (NODE *)malloc(sizeof(NODE)) ;
if(NULL == pstNew)
{
printf("pstnew malloc failed\n");
return -1;
}
pstNew->lvalue = lNewValue;
pstNew->next = pstCurrent;
pstPreviours->next = pstNew;
return 0;
}
第二种写法:
保存一个指向下一个节点的指针,修改该节点的指针指向就好了。
//有序单链表插入法2
int ListOrderInsert1(NODE *pRoot,int lNewValue)
{
NODE *pstCurrent = NULL;
NODE **pstLink = NULL;
NODE *pstNew = NULL;
if( NULL == pRoot)
{
printf("pRoot Null\n");
return -1;
}
pstLink = &pRoot->next;
pstCurrent = *pstLink;
while(pstCurrent != NULL && pstCurrent->lvalue > lNewValue )
{
pstLink = &(*pstLink)->next;
pstCurrent = *pstLink;
}
pstNew = (NODE *)malloc(sizeof(NODE)) ;
if(NULL == pstNew)
{
printf("pstnew malloc failed\n");
return -1;
}
pstNew->lvalue = lNewValue;
pstNew->next = pstCurrent;
*pstLink = pstNew;
return 0;
}
最终优化的版本:
使用一个指针就可以解决该问题。
//有序单链表插入法2简化
int ListOrderInsert2(NODE *pRoot,int lNewValue)
{
NODE **pstLink = NULL;
NODE *pstNew = NULL;
if( NULL == pRoot)
{
printf("pRoot Null\n");
return -1;
}
pstLink = &pRoot->next;
while(*pstLink != NULL && (*pstLink)->lvalue > lNewValue )
{
pstLink = &(*pstLink)->next;
}
pstNew = (NODE *)malloc(sizeof(NODE)) ;
if(NULL == pstNew)
{
printf("pstnew malloc failed\n");
return -1;
}
pstNew->lvalue = lNewValue;
pstNew->next = *pstLink;;
*pstLink = pstNew;
return 0;
}
总结:一、这个改进的函数,之所以能把代码体积变小,依赖于C语言能够取得地址的能力即指针。可见,指针的威力巨大,用指针,可以让代码体积变得更小,效率更高。
二、之所以能优化该函数,让该函数的代码体积更小,有两方面的原因,第一个因素是,能够正确解释问题。除非你能在不同的操作中总结出一些共性,不然,你只能编写额外的代码消除特殊情况。这通常对数据结构了解很清楚的前提下,才能获得这种能力。第二个因素就是C语言提供指针这种便利的工具,让你去随意发挥。
下面补充一些链表操作的程序:
void print(NODE *psthead)
{
NODE *pstPos = NULL;
if(NULL == psthead)
{
printf("psthead NULL\n");
return ;
}
pstPos = psthead;
while((pstPos = pstPos->next) != NULL)
{
printf("%d ",pstPos->lvalue);
}
printf("\n");
return;
}
NODE *ListFind(NODE *pRoot,int lKeyValue)
{
NODE **pstLink = NULL;
if(NULL == pRoot)
{
printf("proot null\n");
return NULL;
}
for(pstLink = &pRoot->next; pstLink != NULL;pstLink = &(*pstLink)->next)
{
if(lKeyValue == (*pstLink)->lvalue)
{
return *pstLink;
}
}
}
void FreeNode(NODE *psthead)
{
NODE *pos = NULL;
if(NULL == psthead)
{
printf("psthead null\n");
return;
}
pos = psthead;
while((pos = pos->next) != NULL)
{
// pos = pos->next;
free(pos);
}
printf("free node suecess\n");
return;
}
//单链表插入的实现
int ListInsert(NODE *pRoot,int lNewValue)
{
//NODE *pstCurrent = NULL;
//NODE *pstPreviours = NULL;
NODE *pstNew = NULL;
if( NULL == pRoot)
{
printf("pRoot Null\n");
return -1;
}
//pstCurrent = pRoot;
//while(pstCurrent->next != NULL)
while(pRoot->next != NULL)
{
//pstCurrent = pstCurrent->next;
pRoot = pRoot->next;
}
pstNew = (NODE *)malloc(sizeof(NODE)) ;
if(NULL == pstNew)
{
printf("pstnew malloc failed\n");
return -1;
}
pstNew->lvalue = lNewValue;
pstNew->next = NULL;
// pstCurrent->next = pstNew;
pRoot->next = pstNew;
return 0;
}
int ListRemove(NODE *pRoot,NODE *pNode)
{
NODE **pstLink = NULL;
NODE *pnext = NULL;
if(pRoot == NULL || pNode == NULL)
{
printf("pRoot or pNode Null\n");
return -1;
}
pstLink = &pRoot->next;
while(*pstLink != NULL)
{
if(pNode == *pstLink)
break;
pstLink = &(*pstLink)->next;
}
if(pNode == *pstLink)
{
pnext = (*pstLink)->next;
free(*pstLink);
*pstLink = pnext;
}
else
{
printf("false\n");
return -1;
}
return 0;
}
//移出某个节点,使用OS(1)
int ListRemove1(NODE *pRoot,NODE *pNode)
{
NODE *pstNext = NULL;
NODE *pos = NULL;
if(pRoot == NULL || pNode == NULL)
{
printf("pRoot or pNode Null\n");
return -1;
}
if(pNode->next != NULL) //不是尾节点
{
pstNext = pNode->next;
pNode->next = pstNext->next;
pNode->lvalue = pstNext->lvalue;
free(pstNext);
pstNext = NULL;
}
else //尾节点
{
for(pos = pRoot->next;pos != NULL; pos = pos->next)
{
if(pos == pNode)
{
free(pos);
pos = NULL;
}
}
}
}
int FindListNum(NODE *pRoot,int lk)
{
NODE *p1 = NULL;
NODE *p2 = NULL;
NODE *pos = NULL;
if(NULL == pRoot)
{
printf("proot is null\n");
return -1;
}
if(lk <=0)
{
printf("lk <=0 err\n");
return -1;
}
p1 = p2 = pRoot;
while(lk--)
{
p2=p2->next;
}
if(p2->next == NULL)
{
printf("%d\n",p1->next->lvalue);
return 0;
}
for(pos=pRoot;pos->next != NULL;pos = pos->next)
{
p1 = p1->next;
p2 = p2->next;
if(NULL == p2)
{
printf("%d\n",p1->lvalue);
return 0;
}
}
return -1;
}
//简化
NODE *FindListNum1(NODE *pRoot,int lk)
{
NODE *p1 = NULL;
NODE *p2 = NULL;
if(NULL == pRoot)
{
printf("proot is null\n");
return NULL;
}
if(lk <=0 )
{
printf("lk <=0 err\n");
return NULL;
}
p1 = p2 = pRoot;
while(lk--)
{
p2=p2->next;
}
while(p2->next != NULL)
{
p1 = p1->next;
p2 = p2->next;
}
return p1->next;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//定义单链表节点
typedef struct list{
int lvalue;
struct list *next;
}NODE;
NODE stRoot = {0,NULL};
void print(NODE *psthead);
void FreeNode(NODE *psthead);
int ListInsert(NODE *pRoot,int lNewValue);
int ListOrderInsert(NODE *pRoot,int lNewValue);
int ListOrderInsert1(NODE *pRoot,int lNewValue);
//有序单链表插入法2简化
int ListOrderInsert2(NODE *pRoot,int lNewValue);
int ListRemove(NODE *pRoot,NODE *pNode);
int ListRemove1(NODE *pRoot,NODE *pNode);
NODE *ListFind(NODE *pRoot,int lKeyValue);
int FindListNum(NODE *pRoot,int lk);
NODE *FindListNum1(NODE *pRoot,int lk);
int main()
{
NODE *pstNode = NULL;
NODE *pstFind = NULL;
NODE *result = NULL;
int i = 0;
int j;
for(i = 0; i < 10; i++)
{
j = i*2;
// ListInsert(&stRoot,j);
ListOrderInsert(&stRoot,j);
}
ListOrderInsert2(&stRoot,7);
print(&stRoot);
pstFind = ListFind(&stRoot,7);
// ListRemove(&stRoot,pstFind);
ListRemove1(&stRoot,pstFind);
print(&stRoot);
result = FindListNum1(&stRoot,2);
printf("%d\n",result->lvalue);
FreeNode(&stRoot);
return 0;
}