//
//这是一个单循环链表的实现 例子
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node
{
int i;
struct Node *next;
}node ,*pnode;
typedef struct Link
{
char title;
pnode next;
}link,*pLink;
pLink createlink(void);
bool insertlink(pnode,int);
bool showdata(pLink);
pnode GetTail(pnode);//返回表尾元素指针
pnode mergelink(pLink,pLink);//合并循环链表
bool nodeSort(pLink);
int GetLinkLength(pnode);//返回链表的长度
bool nodeSwap(pnode,pnode);//链表中的两个数据进行交换
int main()
{ int iNumber;
printf("循环链表的操作实例\n");
pLink pl= createlink();
puts("您要插入几个数据");
scanf("%d",&iNumber);
for(int t=0;t<iNumber;t++)
{
insertlink(pl->next,''''c''''+t);
}
showdata(pl);
pLink pl2=createlink();
for(int v=0;v<iNumber;v++)
{
insertlink(pl2->next,''''A''''+v);
}
showdata(pl2);
puts("显示合并结果");
mergelink(pl,pl2);
showdata(pl);
puts("下面是对链表进行排序");
nodeSort(pl);
return 0;
}
pLink createlink()
{
pLink newLink; pnode pNew;
newLink=new link;
newLink->title=''''a'''';
pNew=new node;
pNew->i=0;
pNew->next=pNew;
newLink->next=pNew;
return newLink;
}
bool showdata(pLink lk)
{
pnode nodeTemp;
if(lk==NULL)
{
puts("表头结点为空 不是正常的链表");
return false;
}
printf("表头为= %c \n",lk->title);
nodeTemp=lk->next;
do
{
if(nodeTemp->i!=0)
printf("数据内容为 %d\n",nodeTemp->i);
nodeTemp=nodeTemp->next;
}while(nodeTemp->i!=0);
return true;
}
pnode GetTail(pnode pl)
{ pnode perNode,nextNode;
if(pl==NULL)
{
puts("取表尾指针错误");
exit(0);
}
perNode=pl; nextNode=pl->next;
if(nextNode->i==0)
return nextNode;
do
{ perNode=nextNode;
nextNode=nextNode->next;
}while(nextNode->i!=0);
return perNode;
}
bool insertlink(pnode pn,int c)//在表尾插入一个数据
{ pnode nodeNewData,nodeInsertData;
if(pn==NULL)
{
puts("插入数据错误 请检查");
return false;
}
nodeNewData= (pnode)malloc(sizeof(node));
nodeNewData->i=c;
nodeInsertData=GetTail(pn);
nodeNewData->next= nodeInsertData->next;
nodeInsertData->next=nodeNewData;
return true;
}
pnode mergelink(pLink l1,pLink l2)
{
pnode nodeL1Head,nodeL2Head,nodeL1Tail,nodeL2Tail;
pnode nodeTemp;
nodeL1Head=l1->next; nodeL2Head=l2->next;
nodeL1Tail=GetTail(nodeL1Head);
nodeL2Tail=GetTail(nodeL2Head);
nodeL2Tail->next=nodeL1Head;
nodeL1Tail->next=nodeL2Head->next;
nodeTemp=nodeL2Head;
free(nodeTemp);
return nodeL1Head;
}
bool nodeSort(pLink plk)
{ int iTemp,iCount;
if(plk==NULL)
{ puts("您要排序的链表有问题请检查");
return false;
}
iCount=GetLinkLength(plk->next);
pnode nodeHead=plk->next;
pnode nodeCurrent=nodeHead->next;
pnode nodeNext=nodeCurrent->next;
for(iTemp=0;iTemp<iCount;iTemp++)
{
for(int t=0;t<iCount;t++)
{
if(nodeCurrent->i > nodeNext->i)
{//进行数据交换
nodeSwap(nodeCurrent,nodeNext);
}
nodeCurrent=nodeNext;
nodeNext=nodeNext->next;
}
}
return true;
}
int GetLinkLength(pnode bc)//返回链表的长度
{
pnode nodeTemp;int iLengt=0;
if(bc==NULL)
{
puts("您提供的链表有问题 请检查");
return -1;
}
nodeTemp=bc->next;
while(nodeTemp->i!=0)
{
iLength++;
nodeTemp=nodeTemp->next;
}
return iLength;
}
bool nodeSwap(pnode nodeCur,pnode nodeNxt)
{ pnode nodeTempSwap;
if(nodeCur==NULL || nodeNxt==NULL)
{
puts("数据交换错误请检查交换数据");
return false;
}
nodeTempSwap=nodeNxt;
return true;
}
C++技术网会员解答:
您好,感谢对C++技术网的支持与信任。
据你介绍,代码的功能是没有问题的。所以,我就不看算法本身的实现问题了。我主要从代码规范和一些细节改进提供一些建议,希望帮助你提高代码质量。
代码规范:
1.排版问题
代码排版确实有很大的问题。在网页里看到的排版,都是没有缩进的。而且代码还是参差不齐。即使我直接用Notepad++打开的代码文件,依然是参差不齐的。但是我推测你在你的IDE里,代码是整齐的。这个概率会大一点,但是也可能是不整齐的。好的排版有助于阅读代码和发现Bug,有利于提高代码质量。
造成代码不整齐的原因,一是本来就没有写整齐,这个就要平时注意排版了。另外一种就是制表符(按Tab键打出来的符号)和空格混用。不同的软件会对制表符有不同的处理策略,有的就直接忽略,比如我们网页,有的将制表符当做一个空格,也有的将制表符当做四个空格的宽度来处理。所以本来在IDE里很整齐的,当你将代码复制到其他编辑器里打开,就发现排版乱的不行。你还不知道为什么!如果全部是制表符,尽管不同软件处理不一致,但是总的来说,还是整齐的,只是缩进的效果不一样。而空格则不受软件的影响。所有软件对待空格都是一样的。
所以建议尽量用空格来缩进排版。而在IDE里,设置一下制表符的处理策略,请将制表符替换为4个空格就可以了。VS的设置方法请参考《win32使用Tab键输入制表符或者替换为四个空格》。
2.函数命名规范
不同的人对函数有不同的命名方式。但是,对于一个人来讲,就是一种风格。在一种风格的情况下,所有的命名应该保持一致。否则就不是风格的事,而是不规范了。你可以统一用驼峰命名法命名函数名。比如CreateLink。但是不要一个用驼峰,另外一个又全是小写。这是不规范的做法,让人看起来挺难受的。
3.类型名命名
使用typedef定义新的类型名的时候,建议使用大写的字母来命名。这样可以很好的区分类型名和普通的变量名。比如:
pnode pn;
你能一眼看得出来pnode是一个类型吗?第一眼甚至以为你的代码是不是写错了。这就尴尬了。而如果使用大写定义类型名,如下:
PNODE pn;
这是很容易看得出来的。代码可读性也就提高了。
4.含义定义名称
不管是函数名、类型名还是变量名,在定义的时候,尽量使用单词的含义来定义,或者用这些单词的常见缩写来定义名称。这样含义一目了然,否则今后再看这个代码时,你自己都很难分得清楚那些意义不明的名称是有什么用。这样代码的可维护性也就降低了,维护成本就变高了,自己以后都不愿意维护了,而其他人呢?
5.函数声明时,尽量带上参数名
参数名和函数名的意义也是差不多的,可以帮助你理解函数的作用。虽然省略没有语法错误,但是那种代码不建议给人看。
6.内存分配统一
要么使用C语言的方式malloc来分配内存,要么就使用C++的的new来分配内存。切忌不要两者混用,然后将free和delete混用。这样不仅仅是规范问题,而且还有错误。C语言的方式和C++的方式所做的工作都不一样,所以混用是一种错误,而不是一种潇洒。
7.自己分配内存,一定要做到自己释放
看到了createlink,却没有看到deletelink这样的函数。不用说,已经内存泄漏了。而且在处理内存问题时,要做到,哪里创建的哪里释放。尽量将内存分配和释放限制在同一个地方,方便管理,以防忘记而泄漏内存。在传递内存指针的时候,一定要注意,不要弄丢内存了。具体的代码的内存管理我就没有看了,请按照这个方式再审视自己的代码。分配内存一定要和释放内存成对出现。
细节决定成败,平时就要注意养成习惯,形成条件映射。当写了分配内存的时候,就想到要写好对应的释放内存,而不要等到过一会再说。你可以先写好释放内存的,占个位置。再写其他的代码。
下面是主要代码的详细的注释说明:
// C_2.cpp : Defines the entry point for the console application.
//
//这是一个单循环链表的实现 例子
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node
{
int i;
struct Node *next;
}node ,*pnode;//建议:类型的定义,建议使用大写字母,如NODE,PNODE,以免和普通的变量混淆
typedef struct Link//这个Link不必要写
{
char title;
pnode next;
}link,*pLink;//建议:同上
pLink createlink(void);//建议:函数命名要么建议使用驼峰命名法CreateLink,这样比较直观,而且多处
bool insertlink(pnode,int);//建议:一般情况下,建议在参数中带上参数名,便于复制使用和查看参数的意义
bool showdata(pLink);
pnode GetTail(pnode);//返回表尾元素指针
pnode mergelink(pLink,pLink);//合并循环链表
//建议:代码排版很重要。好的排版有助于阅读代码和发现Bug,有利于提高代码质量。
int main()
{ int iNumber;
printf("循环链表的操作实例\n");
pLink pl= createlink();
puts("您要插入几个数据");
scanf("%d",&iNumber);
for(int t=0;t<iNumber;t++)
{
insertlink(pl->next,''''c''''+t);
}
showdata(pl);
pLink pl2=createlink();
for(int v=0;v<iNumber;v++)
{
insertlink(pl2->next,''''A''''+v);
}
showdata(pl2);
puts("显示合并结果");
mergelink(pl,pl2);
showdata(pl);
//结尾没有释放内存,造成了内存泄漏。一定要形成良好的习惯,自己分配了内存,一定要自己释放。
return 0;
}
pLink createlink()
{
pLink newLink; pnode pNew;//建议:注意排版
newLink=new link;
newLink->title=''''a'''';
pNew=new node;
pNew->i=0;
pNew->next=pNew;
newLink->next=pNew;
return newLink;
}
bool showdata(pLink lk)
{
pnode nodeTemp;
if(lk==NULL)
{
puts("表头结点为空 不是正常的链表");
return false;
}
printf("表头为= %c \n",lk->title);
nodeTemp=lk->next;
do
{
if(nodeTemp->i!=0)
printf("数据内容为 %d\n",nodeTemp->i);
nodeTemp=nodeTemp->next;
}while(nodeTemp->i!=0);
return true;
}
pnode GetTail(pnode pl)//建议:函数名风格忌不统一,让人一看就会觉得代码不规范。至于风格,那是个人喜好。
{ pnode perNode,nextNode;
if(pl==NULL)
{
puts("取表尾指针错误");
exit(0);
}
perNode=pl; nextNode=pl->next;
if(nextNode->i==0)
return nextNode;
do
{ perNode=nextNode;
nextNode=nextNode->next;
}while(nextNode->i!=0);
return perNode;
}
//建议:在取函数名的时候,一定要准确表达函数的作用,这里建议名InsertToLinkTail。这样不管是谁看,一般都很明白这个函数的作用了。
//建议:参数名命名时也要用明确含义,或者按照常见的缩写形式缩写,而不是随意顶一个字母组合。这里可以是pnode,ch
bool insertlink(pnode pn,int c)//在表尾插入一个数据
{ pnode nodeNewData,nodeInsertData;
if(pn==NULL)//可以写成if(!pn)
{
puts("插入数据错误 请检查");
return false;
}
nodeNewData= (pnode)malloc(sizeof(node));//建议:在代码里尽量保持风格一致,要么都用C风格的malloc,要么都用C++的new
nodeNewData->i=c;
nodeInsertData=GetTail(pn);
nodeNewData->next= nodeInsertData->next;
nodeInsertData->next=nodeNewData;
return true;
}
pnode mergelink(pLink l1,pLink l2)
{
pnode nodeL1Head,nodeL2Head,nodeL1Tail,nodeL2Tail;
pnode nodeTemp;
nodeL1Head=l1->next; nodeL2Head=l2->next;
nodeL1Tail=GetTail(nodeL1Head);
nodeL2Tail=GetTail(nodeL2Head);
nodeL2Tail->next=nodeL1Head;
nodeL1Tail->next=nodeL2Head->next;
nodeTemp=nodeL2Head;
free(nodeTemp);
return nodeL1Head;
}
写了// 建议 的位置就是建议的内容。因为功能已经实现,说明算法都是没有问题的,方法不存在标准的,只要不是特别别扭,都行。我也就不细看算法的了。