双向链表
双向链表的删除
错误代码实例
int DelteDouList(LinkNode *pHead, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pHead->pNext;
if (pTmpNode == NULL)
{
return -1;
}
while(pTmpNode!=NULL)
{
if(pTmpNode->Data==TmpData)
{
pNextNode = pTmpNode->pNext;
pTmpNode->pPre->pNext = pTmpNode->pNext;
if(pTmpNode->pNext!=NULL)
{
pTmpNode->pNext->pPre = pTmpNode->pPre;
}
pTmpNode = pTmpNode->pNext;
free(pTmpNode->pPre);
}
else if(pTmpNode->Data!=TmpData)
{
pTmpNode = pTmpNode->pNext;
}
}
return 0;
}
在错误代码中,先将pTmpNode的前后节点相连(此时并没有和两个节点断开连接,如果按照节点连接的指向,依旧可以找到前后两个节点,因此在代码中选择将pTmpNode向后移动),随后让pTmpNode节点向后移动,错误之处在于,经过处理的pTmpNode的后一个节点的前一个已经变成了pTmpNode的前一个节点,在释放空间时,释放的已经不是我们所指定的空间。
正确代码
int DelteDouList(LinkNode *pHead, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
LinkNode *pNextNode = NULL;
pTmpNode = pHead->pNext;
if (pTmpNode == NULL)
{
return -1;
}
while(pTmpNode!=NULL)
{
if(pTmpNode->Data==TmpData)
{
pNextNode = pTmpNode->pNext;
pTmpNode->pPre->pNext = pTmpNode->pNext;
if(pTmpNode->pNext!=NULL)
{
pTmpNode->pNext->pPre = pTmpNode->pPre;
}
free(pTmpNode);
pTmpNode = pNextNode;
}
else if(pTmpNode->Data!=TmpData)
{
pTmpNode = pTmpNode->pNext;
}
}
return 0;
}
双向链表的销毁
int DestoryDouList(LinkNode **ppHead)
{
LinkNode *pTmpNode = NULL;
pTmpNode = (*ppHead)->pNext;
while (pTmpNode!= NULL)
{
free(pTmpNode->pPre);
pTmpNode = pTmpNode->pNext;
}
free(pTmpNode);
*ppHead = NULL;
return 0;
}
内核链表
内核链表是数据包含节点,因此在使用过程中可以由用户决定存放数据的类型
typedef struct student
{
struct list_head node;
char name[32];
char sex;
int age;
int score;
}data_t;
int main(void)
{
struct list_head head;
struct list_head *pTmpNode = NULL;
data_t *pData = NULL;
//初始化空白节点
INIT_LIST_HEAD(&head);
data_t tmpdata1 = {{NULL,NULL},"张三",'f',18,100};
list_add(&tmpdata1.node,&head);
data_t tmpdata2 = {{NULL,NULL},"李四",'f',18,88};
list_add(&tmpdata2.node,&head);
data_t tmpdata3 = {{NULL,NULL},"王二",'f',18,70};
list_add(&tmpdata3.node,&head);
data_t tmpdata4 = {{NULL,NULL},"张张",'m',16,100};
list_add(&tmpdata4.node,&head);
list_for_each(pTmpNode,&head)
{
pData = (data_t *)pTmpNode;
printf("%s ",pData->name);
printf("%c ",pData->sex);
printf("%d ",pData->age);
printf("%d ",pData->score);
printf("\n");
}
return 0;
}
栈
是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。(具有先进后出的特点)
栈顶:允许入栈出栈的一端称为栈顶
栈底:不允许入栈和出栈的一端称为栈底
入栈(压栈):将数据元素放入栈顶
出栈(弹栈):将数据元素从栈顶位置取出
分类
空增栈
空减栈
满增栈
满减栈
关于循序栈的函数(创建,遍历...)
#include "seqstack.h"
SeqStack *CreateSeqStack(int MaxLen)
{
//1.申请标签空间
SeqStack *pTmpSatck = NULL;
pTmpSatck = malloc(sizeof(SeqStack));
if (NULL == pTmpSatck)
{
return NULL;
}
//2.对每个成员赋初值
pTmpSatck->tLen = MaxLen;
pTmpSatck->Top =0;
//3.申请存放数据的空间
DataType *pTmpData = NULL;
pTmpData = malloc(sizeof(DataType)*MaxLen);
pTmpSatck->pData = pTmpData;
//4.返回标签地址
return pTmpSatck;
}
int IsFullSeqStack(SeqStack *pTmpStack)
{
if(pTmpStack->tLen == pTmpStack->Top)
{
return 1;
}
else if(pTmpStack->tLen>pTmpStack->Top)
{
return 0;
}
}
int IsEmptySeqStack(SeqStack *pTmpStack)
{
if(pTmpStack->Top == 0)
{
return 1;
}
else
return 0;
}
int EnterSeqStack(SeqStack *pTmpStack, DataType TmpData)
{
if (IsFullSeqStack(pTmpStack))
{
return -1;
}
pTmpStack->pData[(pTmpStack->Top) ] = TmpData;
(pTmpStack->Top)++;
return 0;
}
DataType PopSeqStack(SeqStack *pTmpStack)
{
if (IsEmptySeqStack(pTmpStack))
{
return -1;
}
(pTmpStack->Top)--;
return pTmpStack->pData[pTmpStack->Top];
}
int DestroySeqStack(SeqStack **ppTmpStack)
{
int i = 0;
free((*ppTmpStack)->pData);
free(*ppTmpStack);
*ppTmpStack = NULL;
}
内核链表与栈
//内核链表形(把存放数据的类型交给用户决定)
typedef struct data
{
struct list_head node;
int data;
}data_t;
int main(void)
{
struct list_head *ptmpnode = NULL;
data_t d[5] = {
{{NULL,NULL},1},
{{NULL,NULL},2},
{{NULL,NULL},3},
{{NULL,NULL},4},
{{NULL,NULL},5}
};
int i = 0;
//定义链表空节点,并初始化
struct list_head head;
INIT_LIST_HEAD(&head);
//将五个数据头插法插入链表中
for(i = 0;i < 5;i++)
{
list_add(&d[i].node, &head);
}
//只要链表不为空将第一个节点出栈
while (!list_empty(&head))
{
ptmpnode = head.next;
printf("%d ", list_entry(ptmpnode, data_t, node)->data);
list_del(head.next);
}
printf("\n");
return 0;
}
list_entry()函数
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
在该函数中能够直接得到结构体成员的值,并将其转化为自己想要的类型。
(unsigned long)&((type *)0)->member)):在该部分中主要为了获得结构体成员在结构体中的偏移量。
(char *)(ptr):是指该结构体的首地址的位置
两者的差值即为成员变量的首地址。
队列
队列也是一种线性表,其特殊性在于队列的基本操作是线性表的子集。队列按“先进先出”的规则进行操作,故称其为操作受限的线性表。