Note
- 使用的工具是CodeBlocks 16.01
- 启用了C99标准
单向链表是这样是个序列:对于单向链表中的某一个元素ai,ai.data保存了ai的值,ai.pointer保存了ai+1的地址,最后一个Node的pointer为NULL。
如果我们得到了第一Node的地址或第一个Node变量,我们就可以访问到所有的Node。
Node结构体的声明如下:
// node.c
typedef struct node{
int data;
struct node* pointer;
}Node;
有了Node的模型,我们怎样创建一个单向链表呢?我们可以创建一个个的Node,然后把它们连接起来,如下所示:
// node.c
typedef struct node{
int data;
struct node* pointer;
}Node;
int main(void)
{
Node n1,n2,n3,n4,n5,n6,n7; // 创建n1~n7共7个Node
//把n1~n7连接起来
n1.pointer = &n2;
n2.pointer = &n3;
n3.pointer = &n4;
n4.pointer = &n5;
n5.pointer = &n6;
n6.pointer = &n7;
n7.pointer = NULL; //n7是最后一个Node,其pointer为NULL
//在n1~n7中存储数据
n1.data = 1;
n2.data = 2;
n3.data = 3;
n4.data = 4;
n5.data = 5;
n6.data = 6;
n7.data = 7;
return 0;
}
使用上面的代码,我们创建了一个具有7个元素的单向链表,其结构图如下:
使用上面的方法,我们完全可以创建一个可以保存很多元素的单向链表,只要我们声明一个Node,并且把它添加到链表中即可。例如,如果我们还有添加一个元素n8,我们可以这样做:
Node n8;
n8.data = 8;
n8.pointer = NULL;
n7.pointer = &n8;
使用这种方式,我们可以很方便第知道自己的单向链表有多少个元素;可以很方便第访问其中的任一个元素;可以很方便的添加、删除元素。但是使用这种方法也有缺点:
- 每次都要创建一个节点,然后为其赋值,并将其添加到单向链表的末尾。
- 如果需要添加的元素成百上千,我们就需要创建成百上千的Node变量,很麻烦。
因此,如果可以有一个函数,我们提供给他第一个Node或者最后一个Node的地址和需要添加的元素的值,它就可以帮我们创建一个节点,并为节点赋值,将节点添加到单向链表的末尾,岂不是一个好事?!因此,我打算写一个函数append(Node * pNode,int data)
,它接收第一个Node的地址(通过第一个Node,我们可以找到所有的Node)和需要添加的元素值data。
bool append(Node*pNode,int data)
{
Node * pEnd = pNode; // 保存最后一个元素的地址
while(pEnd->pointer!=NULL){
pEnd = pEnd->pointer; // 让pEnd保存下一个Node的地址,直到pEnd保存了最后一个Node的地址
}
// 创建一个新的Node
Node *pNew = (Node *)malloc(sizeof(Node));
if(pNew==NULL){
printf("内存分配失败!\n");
exit(-1);
}
// 保存元素;将新创建的Node变为最后一个Node;将新创建的Node和其他的Node连接起来
pNew->data = data;
pNew->pointer = NULL;
pEnd->pointer = pNew;
return true;
}
我们写了上面的append(Node * pNode,int data)
函数后,我们就可以像这样往单向链表中添加元素了。
//node.c
#include
#include
#include
typedef struct node{
int data;
struct node * pointer;
}Node;
bool append(Node*pNode,int data)
{
Node * pEnd = pNode; // 保存最后一个元素的地址
while(pEnd->pointer!=NULL){
pEnd = pEnd->pointer; // 让pEnd保存下一个Node的地址,直到pEnd保存了最后一个Node的地址
}
// 创建一个新的Node
Node *pNew = (Node *)malloc(sizeof(Node));
if(pNew==NULL){
printf("内存分配失败!\n");
exit(-1);
}
// 保存元素;将新创建的Node变为最后一个Node;将新创建的Node和其他的Node连接起来
pNew->data = data;
pNew->pointer = NULL;
pEnd->pointer = pNew;
return true;
}
int main(void)
{
// 先创建第一个Node,因为append(Node*pNode,int data)需要第一个Node的地址。
Node n1;
n1.data = 1;
n1.pointer = NULL;
append(&n1,2);
append(&n1,3);
append(&n1,4);
append(&n1,5);
append(&n1,6);
append(&n1,7);
append(&n1,8);
//输出
Node *pEnd = &n1;
while(pEnd->pointer!=NULL){ // 如果pEnd保存的不是最后一个元素的地址
printf("%d ",pEnd->data);
pEnd = pEnd->pointer;
}
// 循环结束后,pEnd保存的是最后一个元素的地址
printf("%d \n",pEnd->data);//输出最后一个元素的值
return 0;
}
到现在为止,似乎没有什么问题。现在我想写一个函数,求单向链表的长度。我们知道通过第一个节点的地址,我们可以找到所有节点,因此函数需要第一个Node的地址作为参数:
int len(Node*pNode){
int len = 1; //这里设置了初始值为1,因为我们传递过来的第一个节点是有值的
Node*pEnd = pNode;
while(pEnd->pointer!=NULL){
len++;
pEnd = pEnd->pointer;
}
return len;
}
发现没有,似乎我们的单向链表的长度永远不可能为0啊!我们也无法清空我的单向链表,因为其长度最少都是1。怎么办呢?这就需要在第一个节点的前面引入另一个节点,这个节点的data中不保存数据,pointer则指向我们存储数据的第一个节点。这样可以方便我们创建空的单向链表,清空链表,在第一个存储数据的节点前插入节点。
- 我们在第一个存储数据的Node前插入的节点又叫头节点
- 存储头结点地址的变量叫头指针
- 第一个存储数据的节点叫首节点
Note:头节点的data中并没有存储数据;头节点的pointer存储了首节点的地址;如果头节点的pointer为NULL,则说明单向链表为空。
知道这些东西后,我们就可以编写操作单向链表的函数了。如下:linked_list.h
中定义了单向链表的结构体和可以对其进行操作的函数。
linked_list.h
#include
typedef struct node{
struct node * pNext; // 指针域:指向下一个元素的指针
int element; // 数据域:保存元素值
}Node,LinkedList; // 给struct node数据类型起了两个别名Node,LinkedList
// LinkedList别名倾向于表示整个的单向链表
// Node别名倾向于表示单向链表中的节点
void linkedListInit(Node * pNode); // 初始化单向链表
void linkedListShow(const LinkedList * const pList);//显示单向链表中的所有元素
bool linkedListEmpty(const LinkedList * const pList);//判断单向链表是否为空
bool linkedListAppend(LinkedList*pList,int e);//在单向链表的末尾追加元素
bool linkedListInsert(LinkedList*pList,int i,int e);//在单向链表的第i处插入元素
int linkedListLen(const LinkedList*const pList);//计算单向链表中有多少个元素
bool linkedListRemove(LinkedList*pLisy,int i,int*e);//移除单向链表中指定位置的元素
bool linkedListPop(LinkedList*pList,int*e);//弹出单向链表中最后一个元素
void linkedListSort(LinkedList*pList);//排序
bool linkedListContain(const LinkedList*const pList,int e);//判断单向链表中是否包含某个元素
bool linkedListGet(const LinkedList*const pList,int i,int *e);//得到单向链表中某一个位置的元素
int linkedListIndex(const LinkedList*const pList,int e);//得到元素e在单向链表中的索引
void linkedListClear(LinkedList*pList);//清空单向链表
linkedList.c
是以上定义的函数的实现代码。
linkedList.c
#include
#include"linked_list.h"
#include
/** \brief 初始化LinkedList对象
*
* \param pNode Node* 指向LinkedList对象的指针
* \return void
*
*/
void linkedListInit(LinkedList * pList)
{
pList->pNext=NULL; //让头结点的pointer为空,以创建一个空的单向链表;注意并没有为头结点的data赋值。
return;
}
/** \brief 打印LinkedList对象的每一个元素
*
* \param pList const LinkedList*const LinkedList对象
* \return void
*
*/
void linkedListShow(const LinkedList*const pList)
{
if(linkedListEmpty(pList)) // 如果单向链表为空
{
printf("LinkedList is empty!");
return;
}
LinkedList* pTemp = pList->pNext;
while(pTemp!=NULL)
{
printf("%d ",pTemp->element);
pTemp = pTemp->pNext;
}
printf("\n");
return;
}
/** \brief 判断LinkedList是否为空
*
* \param const LinkedList * const pList 判断是否为空的LinkedList对象
* \return bool 如果为空则返回true,否则返回false
*
*/
bool linkedListEmpty(const LinkedList * const pList)
{
return pList->pNext==NULL;
}
/** \brief 向LinkedList末尾添加元素
*
* \param LinkedList*pList LinkedList对象
* \param e int 添加的元素
* \return bool 添加成功则返回true,否则返回false
*
*/
bool linkedListAppend(LinkedList*pList,int e)
{
Node* pNode = pList;
while(pNode->pNext!=NULL)
{
pNode=pNode->pNext;
}
Node* pNew = (Node*)malloc(sizeof(Node));
if(pNew==NULL)
{
printf("分配内存失败!\n");
exit(-1);
}
pNew->element=e;
pNew->pNext=NULL;
pNode->pNext=pNew;
return true;
}
/** \brief 在LinkedList的某一个位置i处插入元素e
*
* \param LinkedList*pList 指向LinkedList对象的指针
* \param i int 插入元素的位置,从0开始
* \param e int 被插入的元素e
* \return bool 如果插入的位置不属于[0,len-1],则插入失败
*
*/
bool linkedListInsert(LinkedList*pList,int i,int e)
{
int len = linkedListLen(pList);
if(i>=0 && ielement=e;
pNew->pNext=pNode->pNext;
pNode->pNext=pNew;
return true;
}
pNode=pNode->pNext;
}
}
return false;
}
/** \brief 获取LinkedList的长度
*
* \param pList const LinkedList*const 指向LinkedList对象的指针
* \return int 长度
*
*/
int linkedListLen(const LinkedList*const pList)
{
int len = 0;
Node* pNode = pList->pNext;
while(pNode!=NULL)
{
pNode=pNode->pNext;
len++;
}
return len;
}
/** \brief 从LinkedList中移除指定位置的元素
*
* \param LinkedList*pLisy 指向LinkedList对象的指针
* \param i int 指定的位置,从0开始
* \param int *e 保存被移除的元素
* \return bool 如果LinkedList为空或者指定的位置不存在,则返回false。
*
*/
bool linkedListRemove(LinkedList*pList,int i,int *e)
{
if(linkedListEmpty(pList)){
return false;
}
if(i<0){
return false;
}
int len = linkedListLen(pList);
if(i>=len){
return false;
}
Node * pNode = pList;
for(int index = 0; index < len; index++){
if(index==i){
Node*pTemp = pNode->pNext;
*e = pTemp->element;
pNode->pNext=pTemp->pNext;
pTemp->pNext=NULL;
free(pTemp);
return true;
}
pNode=pNode->pNext;
}
}
/** \brief 从LinkedList中弹出最后一个元素
*
* \param LinkedList*pList 指向LinkedList对象的指针
* \param int *e 保存被弹出的元素
* \return bool 如果LinkedList为空,则返回false
*
*/
bool linkedListPop(LinkedList*pList,int *e)
{
if(linkedListEmpty(pList)){
return false;
}
int len = linkedListLen(pList);
Node* pNode = pList->pNext;
for(int index = 0; index < len-2; index++){
pNode=pNode->pNext;
}
Node* pTemp = pNode->pNext;
*e = pTemp->element;
free(pTemp);
pNode->pNext=NULL;
return true;
}
/** \brief 对LinkedList进行排序
*
* \param LinkedList*pList 指向LinkedList对象的指针
* \return void
*
*/
void linkedListSort(LinkedList*pList)
{
if(linkedListEmpty(pList))
{
printf("List is empty!\n");
return;
}
Node* p1 = pList->pNext;
Node* p2;
if(p1->pNext!=NULL)
{
p2=p1->pNext;
}
while(p1!=NULL)
{
while(p2!=NULL)
{
if(p1->element>p2->element)
{
p1->element=p1->element^p2->element;
p2->element=p1->element^p2->element;
p1->element=p1->element^p2->element;
}
p2=p2->pNext;
}
if(p1->pNext==NULL){
return;
}else{
p1 = p1->pNext;
}
if(p1->pNext==NULL){
return;
}else{
p2=p1->pNext;
}
}
return;
}
*/
/** \brief 判断LinkedList中是否包含元素e
*
* \param pList const LinkedList*const 指向LinkedList对象的指针
* \param e int e元素
* \return bool 如果包含,则返回true,否则返回false
*
*/
bool linkedListContain(const LinkedList*const pList,int e)
{
if(linkedListEmpty(pList))
{
return false;
}
Node*pNode = pList->pNext;
while(pNode!=NULL)
{
if(pNode->element==e)
{
return true;
}
pNode=pNode->pNext;
}
return false;
}
/** \brief 获取指定索引处的元素
*
* \param pList const LinkedList*const 指向LinkedList对象的指针
* \param i int 索引
* \return bool 如果获取该元素在LinkedList中,则获取成功,否则获取失败
*
*/
bool linkedListGet(const LinkedList*const pList,int i,int *e)
{
if(linkedListEmpty(pList))
{
printf("LinkedList是空的!\n");
return false;
}
int index=0;
Node* pNode = pList->pNext;
while(pNode!=NULL)
{
if(index==i)
{
*e=pNode->element;
return true;
}
pNode=pNode->pNext;
index++;
}
return false;
}
/** \brief 在LinkedList中查找某一个元素e索引,索引从0开始
*
* \param pList const LinkedList*const 指向LinkedList对象的指针
* \param e int 查找的元素e
* \return int 找到则返回e在LinkedList中的索引,否则返回-1
*
*/
int linkedListIndex(const LinkedList*const pList,int e)
{
if(linkedListEmpty(pList))
{
return -1;
}
int index = 0;
Node* pNode = pList->pNext;
while(pNode!=NULL)
{
if(pNode->element==e)
{
return index;
}
pNode=pNode->pNext;
index++;
}
return -1;
}
/** \brief 清空LinkedList
*
* \param LinkedList*pList 需要清空的LinkedList对象
* \return void
*
*/
void linkedListClear(LinkedList*pList)
{
if(linkedListEmpty(pList))
{
return;
}
Node* pNode = pList->pNext;
pList->pNext=NULL;
Node* pTemp;
while(pNode->pNext!=NULL)
{
pTemp=pNode->pNext;
free(pNode);
pNode=pTemp;
}
free(pNode);
return;
}
测试代码
linkedListTest.c
#include"linked_list.h"
#include
int main(void)
{
LinkedList myList,*pMyList;
pMyList = &myList;
linkedListInit(pMyList);
linkedListAppend(pMyList,89);
linkedListAppend(pMyList,90);
linkedListAppend(pMyList,78);
linkedListAppend(pMyList,82);
linkedListAppend(pMyList,87);
linkedListAppend(pMyList,60);
linkedListShow(pMyList);
printf("%d在LinkedList中的索引是:%d\n",90,linkedListIndex(pMyList,90));
//linkedListClear(pMyList);
linkedListShow(pMyList);
int e;
linkedListGet(pMyList,5,&e);
printf("第%d个位置的元素是:%d\n",5,e);
if(linkedListContain(pMyList,60)){
printf("True\n");
}else{
printf("false\n");
}
printf("linkedList的长度是:%d\n",linkedListLen(pMyList));
printf("------排序-----\n");
linkedListSort(pMyList);
//linkedListReverse(pMyList);
linkedListShow(pMyList);
printf("------删除-----\n");
linkedListRemove(pMyList,5,&e);
printf("%d\n",e);
linkedListShow(pMyList);
linkedListInsert(pMyList,0,12);
linkedListInsert(pMyList,1,40);
linkedListInsert(pMyList,5,88);
linkedListInsert(pMyList,3,72);
linkedListShow(pMyList);
linkedListPop(pMyList,&e);
linkedListShow(pMyList);
return 0;
}