数据结构链表总结一
最近一直在学数据结构,刚开始学习的,觉得链表还是比较难的,今天将自己写的代码贴上来
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
struct List
{
data_t data; //typedef int data_t;
struct List *pNext;
};
List *CreateList()
{
List *pHeader = NULL;
pHeader = (List *) malloc (sizeof(struct List));
if (NULL != pHeader)
{
memset(pHeader, 0, sizeof(struct List));
}
return pHeader;
}
int DestoryList(List *pList)
{
if (NULL == pList)
{
return LIST_ERROR;
}
while(pList->pNext)
{
List *p = pList->pNext;
if (NULL != p)
{
pList->pNext = p->pNext;
free(p);
p = NULL;
}
}
free(pList);
return LIST_OK;
}
int InsertList(List *pList, data_t tData, int iOffset)
{
if (NULL == pList)
{
return LIST_ERROR;
}
List *pData = (List *) malloc (sizeof(List));
if (NULL == pData)
{
return LIST_ERROR;
}
memset(pData, 0, sizeof(struct List));
pData->data = tData;
pData->pNext = NULL;
//insert
/*********************************************************************************/
if(NULL == pList->pNext)
{
pList->data = (data_t)pData; //data_t为四字节,可以存储四字节地址
}
/*********************************************************************************/
List *pTmp = pList;
switch(iOffset)
{
case HEADER : //在第一次插入数据的时候,尾结点的地址已保存到了头节点
{ //后面的插入的数据不会改变尾结点的地址
pData->pNext = pList->pNext;
pList->pNext = pData;
}
break;
case TAIL :
{
/*while(pTmp->pNext) //方式1
{
pTmp = pTmp->pNext;
}
pTmp->pNext = pData;*/
/****************************************************************************************/
List *pTmp1 = (List *)pList->data; //直接通过头结点的data域,强制转换得到尾地址
pTmp1->pNext = pData;
pList->data = (data_t)pData;
/****************************************************************************************/
}
break;
default:
{
if (iOffset > 0)
{
while(iOffset--)
{
if (NULL == pTmp->pNext) //防止索引超出范围
{
return LIST_ERROR;
}
pTmp = pTmp->pNext;
}
/****************************************************************************************/
if(NULL == pTmp->pNext) //索引正好指向最后一个结点,新节点成为尾结点
{
pList->data = (data_t)pData;
}
/****************************************************************************************/
pData->pNext = pTmp->pNext;
pTmp->pNext = pData;
}
}
}
return LIST_OK;
}
int DeleteFromList(List *pList, data_t *pData, int iOffset)
{
if (NULL == pList || iOffset < 0)
{
return LIST_ERROR;
}
if (HEADER == iOffset)
{
List *pTmp = pList->pNext;
*pData = pTmp->data;
pList->pNext = pTmp->pNext;
if (NULL != pTmp)
{
free(pTmp);
}
pTmp = NULL;
/****************************************************************************/
if(NULL == pList->pNext) //删除最后一个节点后,尾地址为NULL
{
pList->data = 0;
}
/****************************************************************************/
return LIST_OK;
}
List *pTmp1 = pList;
if (iOffset > 0)
{
while(iOffset--)
{
if (NULL == pTmp1->pNext)
{
return LIST_ERROR;
}
pTmp1 = pTmp1->pNext;
}
List *pDel = pTmp1->pNext;
if (NULL != pDel)
{
pTmp1->pNext = pDel->pNext;
*pData = pDel->data;
free(pDel);
pDel = NULL;
if(NULL == pTmp1->pNext) //删除的正好是最后一个结点,则更新尾结点地址
{
pList->data = (data_t)pTmp1;
}
}
else
{
return LIST_ERROR;
}
}
return LIST_OK;
}
int UpdateData(List *pList, data_t tOldData, data_t tNewData)
{
if (NULL == pList)
{
return LIST_ERROR;
}
List *pTmp = pList;
while(pTmp->pNext)
{
List *p = pTmp->pNext;
if (p->data == tOldData)
{
p->data = tNewData;
return LIST_OK;
}
pTmp = p;
}
return LIST_ERROR;
}
int SearchData(List *pList, data_t tData)
{
if (NULL == pList)
{
return LIST_ERROR;
}
List *pTmp = pList;
while(pTmp->pNext)
{
List *p = pTmp->pNext;
if (p->data == tData)
{
return LIST_OK;
}
pTmp = p;
}
return LIST_ERROR;
}
int ShowList(List *pList)
{
if (NULL == pList)
{
return LIST_ERROR;
}
List *pTmp = pList;
while(pTmp->pNext)
{
printf("%d ", pTmp->pNext->data);
pTmp = pTmp->pNext;
}
printf("\r\n");
return LIST_OK;
}
链表并不像数组那样,可以由下标随机访问数组中的元素,他有自己的好处,他一般从堆内存中分配,而且不需要连续的存储空间,要访问链表中的结点必须通过指针去访问,插入和删除是链表操作中较复杂的操作,一般要在链表中插入或删除某个元素,可以给函数传递一个相对链表第一个有效结点的偏移量,用这个偏移量来确定循环次数,如果是单向链表,必须要找到被删除结点的前一个结点的位置(地址),才能正确删除,当然如果在头部和尾部,情况会特殊一些,进一步的,如果在尾部操作,一种方法是通过每次通过while循环找到最后一个元素的位置,当然这种做法效率并不高,可以在链表头结点中保存尾结点的地址