数据结构:单链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 (百度百科)

用一张图来表示一下单链表这种数据结构:

这是一个带头结点的非空单链表

由一个指针指向单链表的头结点,头结点里由两个部分组成:

1.头结点的数据data

2.头结点的下一个结点的地址(也就是图中的0xCC)

单链表就是这样一种结构,将所有的数据放在一张线性表中。单链表也分为好几种形式:带头结点的空链表、不带头结点的空链表、带头结点的非空单链表,不带头结点的非空单链表。数据结构都和上图类似,在这不一一介绍了。

下面,我们具体了解下怎么完成一个单链表的基本操作(单链表的初始化、销毁、头插、尾插等等)

具体步骤及解释都在代码里注释。

单链表的初始化:

typedef int DataType;  

typedef struct SListNode{       //这里我们定义一个结构体,因为每一个结点都有两个字段
	DataType data;              //第一个字段是该结点所存的数据
	struct SListNode *pNext;    //第二个字段保存了下个结点的地址
}	SListNode;

void SListNodeInit(SListNode **ppFirst)
{
	assert(ppFirst != NULL);  
	*ppFirst = NULL;       //单链表的初始化很简单,就是让头结点指向空,让链表成为一个空链表就ok
}

单链表的销毁:


void SListNodeDestroy(SListNode **ppFirst)
{
	assert(ppFirst != NULL);
	SListNode *pNode, pNext;

	pNode = *ppFirst;
	while (pNode != NULL){      //这里用一个循环去遍历整个链表,然后依次free掉
		pNode = pNode->pNext;
		free(pNode);            /*因为我们每个结点的空间是用malloc函数在堆上创建的,所以在销毁
		pNode =pNode -> pNext;    的时候,必须free掉,以免造成内存泄露*/
	}
	*ppFirst = NULL;
}

结点的创建:


SListNode *CreateNewNode(int data)
{
	SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
	assert(pNewNode);

	pNewNode->data = data;   /* 新结点的数据
	pNewNode->pNext = NULL;     新结点的下一个结点的地址为NULL*/

	return pNewNode;
}

尾插


 void SListNodePushBack(SListNode **ppFirst,DataType data)
  {
         assert(ppFirst != NULL);
 
         SListNode *pNewNode = CreateNewNode(data);  //创建一个新结点
        if(*ppFirst == NULL){                        /*如果头结点为空,只需要让创建的新结点
                *ppFirst = pNewNode;                   成为头结点
        }                                            */
 
        SListNode *pNode;                     /*如果头结点不为空,需要定义一个指针让它从头开
        *ppFirst = pNode;                       始遍历链表,直到找到最后一个结点
         while(pNode->pNext != NULL){         */
                 pNode = pNode->pNext;
         }
         pNode->pNext = pNewNode;             /*把新结点插入到最后一个结点的下一个结点
  }

头插:


 void SListNodePushFront(SListNode **ppFirst,DataType data)
 {
          assert(ppFirst != NULL);
 
         SListNode *pNewNode = CreateNewNode(data);  //创建一个新结点
  
          pNewNode->pNext = *ppFirst;                //新结点的pNext指向头结点
          *ppFirst = pNewNode;                       //此时,让新结点成为头结点
 }

插入到指定结点pPOS前,pPOS肯定在链表里

 void SListNodeInsert(SListNode **ppFirst,SListNode *pPos,DataType data)
  {
          assert(ppFirst != NULL);
  
          SListNode *pNode;                           /*定义一个指针初始化为指向头结点
          pNode == *ppFirst;
 
          if(pPos == *ppFirst){                          如果此时头结点就是pPOS,只需调用一
                  SListNodePushFront(ppFirst,data);      次头插函数即可
                  return;                             
         }
          while(pNode->pNext != pPos){                   如果头结点不是pPOS,需要让先前定义
                  pNode = pNode->pNext;                  的指针从头开始遍历链表,直到找到
          }                                              pPos的前一个结点为止
          SListNode *pNewNode = CreateNewNode(data);     创建一个新结点
  
          pNode->pNext = pNewNode;                       将新结点插入到pPOS之前
          pNewNode->pNext = pPos;                        新结点的pNext指向pPOS
 }                                                       */

头删:

 void SListNodePopFront(SListNode **ppFrist)
 {
          assert(ppFirst != NULL);                /* 保证不为空链表
          assert(*ppFirst != NULL);                            */
  
         SListNode *pOldFirst = *ppFirst;         /* 先将先前的头结点指针保存起来
          *ppFirst = (*ppFirst)->pNext;              让头结点指针指向头结点的下一个结点
          free(pOldFirst);                           free掉之前保存的pOldFirst
 }                                                */

尾删:

void SListNodePopBack(SListNode **ppFirst)
{
	assert(ppFirst != NULL);                /* 保证不为空链表
	assert(*ppFirst != NULL);                              */

	if((*ppFirst)->pNext == NULL){          /* 如果头结点的下一个结点是NULL
		free(*ppFirst);                        将头结点释放掉
		*ppFirst = NULL;                       再让ppFirst指向NULL,以免变成野指针
		return;                                
	}
	SListNode *pNode = *ppFirst;               如果头结点的下一个结点不为NULL  
	while(pNode->pNext->pNext != NULL){        遍历链表,找到最后一个结点
		pNode = pNode->pNext;
	}
	free(pNode->pNext);                        释放掉该结点
	pNode->pNext = NULL;                       将指针指向NULL */
}

根据结点地址删除,结点肯定在链表里:

 void SListNodeErase(SListNode **ppFirst,SListNode *pPos)
 {
         assert(ppFirst != NULL);                 /* 保证不为空链表
         assert(*ppFirst != NULL);                                */
 
         if((*ppFirst) == pPos){                  /* 如果头结点就是pPOS,只需调用一次头删即
                SListNodePopFront(ppFirst);          可
                return;
         }
 
         SListNode *pCur = *ppFirst;                  定义一个指针指向头结点,然后遍历链表
         while(pCur->pNext != pPos){                  找到pPos,此时pCur的pNext应该是pPOS
                pCur = pCur->pNext;                   我们要删除pPos处的结点,需要将pCur的
         }                                            pNext指向pPos的pNext,然后释放掉pPos
         pCur->pNext = pPos->pNext;                   处的结点就可以
         free(pPos);                                */
 }

查找:


 SListNode *Find(SListNode *pFirst,DataType data)
 {
         SListNode *pNode = pFirst;             
         for(;pNode;pNode = pNode->pNext){          //遍历链表,找到了就返回找到的结点
                if(pNode->data == data){            
                         return pNode;
                 }
         }
         return NULL;                               //没找到,就返回NULL
 }

根据数据删除,删除遇到的第一个结点:

 void SListNodeRemove(SListNode **ppFirst,DataType data)
 {
         SListNode *pFound = Find(*ppFirst,data);  //定义一个变量去接收Find函数的返回值
 
         if(pFound != NULL){                       /*若要删除的数据在链表中存放,就调用
                 SListNodeErase(ppFirst,pFound);     根据结点地址删除的函数即可
         }                                         */           
 }

根据数据删除,删除遇到的所有结点:


 void SListNodeRemoveAll(SListNode **pPFirst,DataType data)
 {
         SListNode *pNode = *ppFirst;            
         SListNode *pNext;
 
         while(pNode->pNext){
                 if(pNode->pNext->data == data){            /*若头结点的下一个结点就为要
                         pNext = pNode->pNext;                删除的结点,就用pNext将其
                         pNode->pNext = pNode->pNext->pNext;  保存,pNode的pNext指向
                         free(pNext);                         pNext的pNext,最后释放掉                    
                }                                             pNext
                 else{            
			 pNode = pNode->pNext;                            若不是,就让pNode逐个向后
                 }                                            遍历,直到找到要删除的结点
         }
         if((*ppFirst)->data == data){                        特殊情况:如果头结点就是要
                SListNodePopFront(ppFirst);                   删除的结点,直接头删即可
        }
}

到这里,单链表的所有基本操作都完成了

下面是完整代码:


#include<stdio.h>
#include<stlib.h>
#include<assert.h>
 
   typedef int DataType;
   
   typedef struct SListNode{
           DataType data;
           struct SListNode *pNext;
  }       SListNode;
  
  void SListInit(SListNode **ppFirst)
  {
          assert(ppFirst != NULL);
  
          *ppFirst = NULL;
  }
  
  void SListDestroy(SListNode **ppFirst)
  {
          assert(ppFirst != NULL);
  
          SListNode *pNode,*pNext;
  
          pNode = *ppFirst;
          while(pNode != NULL){
                  pNode = pNode->pNext;
                  free(pNode);
                  pNode = pNext;
          }
          *ppFirst = NULL;
  }
  
  SListNode *CreateNewNode(DataType data)
 {
          SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
          assert(pNewNode);
  
          pNewNode->data = data;
          pNewNode->pNext = NULL;
  
          return pNewNode;
 }
  
 void SListNodePushBack(SListNode **ppFirst,DataType data)
 {
         assert(ppFirst != NULL);
 
         SListNode *pNewNode = CreateNewNode(data);
        if(*ppFirst == NULL){
                *ppFirst = pNewNode;
        }
 
        SListNode *pNode;
        *ppFirst = pNode;
         while(pNode->pNext != NULL){
                 pNode = pNode->pNext;
         }
         pNode->pNext = pNewNode;
 }
 
 void SListNodePushFront(SListNode **ppFirst,DataType data)
 {
          assert(ppFirst != NULL);
 
         SListNode *pNewNode = CreateNewNode(data);
  
          pNewNode->pNext = *ppFirst;
          *ppFirst = pNewNode;
 }
  
  void SListNodeInsert(SListNode **ppFirst,SListNode *pPos,DataType data)
  {
          assert(ppFirst != NULL);
  
         SListNode *pNode;
          pNode == *ppFirst;
 
          if(pPos == *ppFirst){
                  SListNodePushFront(ppFirst,data);
                  return;
         }
          while(pNode->pNext != pPos){
                  pNode = pNode->pNext;
          }
          SListNode *pNewNode = CreateNewNode(data);
  
          pNode->pNext = pNewNode;
          pNewNode->pNext = pPos;
 }
  
 void SListNodePopFront(SListNode **ppFrist)
 {
          assert(ppFirst != NULL);
          assert(*ppFirst != NULL);
  
         SListNode *pOldFirst = *ppFirst;
          *ppFirst = (*ppFirst)->pNext;
          free(pOldFirst);
 }
 
void SListNodePopBack(SListNode **ppFirst)
{
	assert(ppFirst != NULL);
	assert(*ppFirst != NULL);

	if((*ppFirst)->pNext == NULL){
		free(*ppFirst);
		*ppFirst = NULL;
		return;
	}
	SListNode *pNode = *ppFirst;
	while(pNode->pNext->pNext != NULL){
		pNode = pNode->pNext;
	}
	free(pNode->pNext);
	pNode->pNext = NULL;
}
 
 void SListNodeErase(SListNode **ppFirst,SListNode *pPos)
 {
         assert(ppFirst != NULL);
         assert(*ppFirst != NULL);
 
         if((*ppFirst) == pPos){
                SListNodePopFront(ppFirst);
                return;
         }
 
         SListNode *pCur = *ppFirst;
         while(pCur->pNext != pPos){
                pCur = pCur->pNext;
         }
         pCur->pNext = pPos->pNext;
         free(pPos);
 }
137 
 SListNode *Find(SListNode *pFirst,DataType data)
 {
         SListNode *pNode = pFirst;
         for(;pNode;pNode = pNode->pNext){
                if(pNode->data == data){
                         return pNode;
                 }
         }
         return NULL;
 }
 
 void SListNodeRemove(SListNode **ppFirst,DataType data)
 {
         SListNode *pFound = Find(*ppFirst,data);
 
         if(pFound != NULL){
                 SListNodeErase(ppFirst,pFound);
         }
 }
 
 void SListNodeRemoveAll(SListNode **pPFirst,DataType data)
 {
         SListNode *pNode = *ppFirst;
         SListNode *pNext;
 
         while(pNode->pNext){
                 if(pNode->pNext->data == data){
                         pNext = pNode->pNext;
                         pNode->pNext = pNode->pNext->pNext;
                }
                 else{
			 pNode = pNode->pNext;
                 }
         }
         if((*ppFirst)->data == data){
                SListNodePopFront(ppFirst);
        }
}

void Print(SListNode *pFirst)
{
	SListNode *pNode;
	for(pNode = pFirst;pNode;pNode = pNode->pNext){
		printf("%d -> ",pNode->data);
	}
	printf("\n");
}

void Test()
{
	SListNode *pFirst;
	
	SListInit(&oFirst);
	assert(pFirst != NULL);

	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,2);
	SListNodePushBack(&pFirst,1);
	SListNodePushFront(&pFirst,10);
	SListNodePushFront(&pFirst,11);

	Print(pFirst);

	SListNode *pFound = Find(pFirst,10);
	if(pFound == NULL){
		printf("没找到\n");
	}
	else{
		printf("%d\n",pFound->data);
		SListNodeInsert(&pFirst,pFound,100);
		SListNodeErase(&pFirst,pFound);
	}

	Print(pFirst);

	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,4);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,1);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,5);
	SListNodePushBack(&pFirst,3);
	SListNodePushBack(&pFirst,3);

	SListNodeRemoveAll(&pFirst,3);
	Print(pFirst);
}

int main( void )
{
    Test();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LSFAN0213/article/details/81236793