链表、栈、队列的实现

带头双向循环链表

链表是一种数据结构,包括单链表、双链表、带哨兵位的单链表等等。这里实现的是链表数据结构中的最优形式:带头双向循环链表。

头文件声明

typedef int DataType;

typedef struct ListNode
{
	struct ListNode* prev;
	DataType data;
	struct ListNode* next;
}LTNode;

LTNode* ListInit();
LTNode* BuyLTNode(DataType x);
void ListPrint(LTNode* phead);
bool ListIsEmpty(LTNode* phead);
//链表的尾插头插
void ListPushBack(LTNode* phead, DataType x);
void ListPushFront(LTNode* phead, DataType x);
//链表的尾删头删
void ListPopBack(LTNode* phead);
void ListPopFront(LTNode* phead);
//查找
LTNode* ListFind(LTNode* phead, DataType x);
//链表在pos位置之前插入
void ListInsert(LTNode* pos, DataType x);
//链表删除在pos位置的节点
void ListErase(LTNode* pos);

链表添加一个节点并对节点赋值

LTNode* BuyLTNode(DataType x)
{
	LTNode* tmp = (LTNode*)malloc(sizeof(LTNode));
	if (tmp == NULL)
	{
		perror("malloc");
		exit(-1); 
	}
	tmp->data = x;
	tmp->prev = NULL;
	tmp->next = NULL;

	return tmp;
}

链表初始化,哨兵节点的data赋值-1

LTNode* ListInit()
{
	LTNode* phead = BuyLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

判断链表是否为空,是则返回true,不是则返回false

bool ListIsEmpty(LTNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

链表的增删查改

链表的尾头插

void ListPushBack(LTNode* phead, DataType x)
{
	assert(phead);

	LTNode* newnode = BuyLTNode(x);
	LTNode* prev = phead->prev;

	phead->prev = newnode;
	newnode->next = phead;
	newnode->prev = prev;
	prev->next = newnode;

	/*phead->prev->next = newnode;
	newnode->prev = phead->prev;
	phead->prev = newnode;
	newnode->next = phead;*/

	/*ListInsert(phead, x);*/
}

链表的头插

void ListPushFront(LTNode* phead, DataType x)
{
	assert(phead);

	LTNode* newnode = BuyLTNode(x);
	LTNode* next = phead->next;

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;

	/*phead->next->prev = newnode;
	newnode->next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;*/

	/*ListInsert(phead->next, x);*/
}

链表的尾删

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(!ListIsEmpty(phead));

	LTNode* prev = phead->prev;

	phead->prev = prev->prev;
	prev->prev->next = phead;
	free(prev);

	/*phead->prev = phead->prev->prev;
	free(phead->prev->next);
	phead->prev->next = phead;*/

	/*ListErase(phead->prev);*/
}

链表的头删

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(!ListIsEmpty(phead));

	LTNode* next = phead->next;

	phead->next = next->next;
	next->next->prev = phead;
	free(next);

	/*phead->next = phead->next->next;
	free(phead->next->prev);
	phead->next->prev = phead;*/

	/*ListErase(phead->next);*/
}

查找链表元素,找到则返回节点地址,找不到则返回NULL

LTNode* ListFind(LTNode* phead, DataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	//找不到返回NULL
	return NULL;
}

链表在pos位置之前插入

void ListInsert(LTNode* pos, DataType x)
{
	assert(pos);

	LTNode* newnode = BuyLTNode(x);
	LTNode* prev = pos->prev;

	prev->next = newnode;
	newnode->prev = prev;
	pos->prev = newnode;
	newnode->next = pos;

	/*pos->prev->next = newnode;
	newnode->prev = pos->prev;
	pos->prev = newnode;
	newnode->next = pos;*/
}

链表删除在pos位置的节点

void ListErase(LTNode* pos)
{
	assert(pos);
	assert(!ListIsEmpty(pos));

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);

	/*pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);*/
}

特别地,带头双向循环链表只要实现了链表在pos位置之前插入和链表删除在pos位置的节点这两个功能,就可以实现几乎全部功能,因为头插尾插、头删尾删都可以复用这两个功能。

栈的实现

栈是一种数据结构,其特点是先进后出

头文件声明

typedef int DataType;

typedef struct Stack
{
	DataType* arr;
	int Top;
	int Capacity;
}Stack;

//支持动态增长的栈
//初始化栈
Stack* StackInit();
//检查栈是否为空,如果为空则返回非零,如果不为空则返回0
bool IsEmpty(Stack* ps);
//入栈
void StackPush(Stack* ps, DataType x);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
DataType StackTop(Stack* ps);
//获取栈中有效元素个数
int StackSize(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);

初始化栈

Stack* StackInit()
{
	Stack* s = (Stack*)malloc(sizeof(Stack));
	if (s == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	s->arr = NULL;
	s->Capacity = 0;
	s->Top = -1;
	return s;
}

检查栈是否为空,如果为空则返回true,如果不为空则返回false

bool IsEmpty(Stack* ps)
{
	assert(ps);
	return ps->Top == -1;
}

栈的增删查改
入栈
当栈为空时Top等于-1,数组空间不够了就进行2倍扩容。特别注意,有些教科书上将栈的空间定义为Maxsize,不支持扩容,当栈空间满了的时候再入栈就报错。

void StackPush(Stack* ps, DataType x)
{
	assert(ps);

	if (ps->Top == ps->Capacity - 1)
	{
		int newcpty = 0;
		(ps->arr == NULL) ? (newcpty = 4) : (newcpty = 2 * ps->Capacity);
		ps->arr = (DataType*)realloc(ps->arr, newcpty * sizeof(DataType));
		if (ps->arr == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->Capacity = newcpty;
	}
	ps->arr[++ps->Top] = x;
}

出栈

void StackPop(Stack* ps)
{
	assert(ps);
	assert(!IsEmpty(ps));

	ps->Top--;
}

获取栈顶元素

DataType StackTop(Stack* ps)
{
	assert(ps);

	return ps->arr[ps->Top--];
}

获取栈中有效元素个数

int StackSize(Stack* ps)
{
	assert(ps);

	return ps->Top + 1;
}

销毁栈

void StackDestroy(Stack* ps)
{
	assert(ps);

	if (ps->arr != NULL)
	{
		free(ps->arr);
	}
	free(ps);
}

以上的功能实现起来都是十分简单的,但是如此简单的代码就能实现十分强大的功能。

队列的实现

队列是一种数据结构,其特点是先进先出

头文件声明

typedef int DataType;

typedef struct QueueNode
{
	DataType data;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

//初始化队列
Queue* QueueInit();
//检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool IsEmtpy(Queue* pq);
//队尾入队列
void QueuePush(Queue* pq, DataType x);
//队头出队列
void QueuePop(Queue* pq);
//获取队列头部元素
DataType QueueFront(Queue* pq);
//获取队列尾部元素
DataType QueueBack(Queue* pq);
//获取队列中有效元素个数
int QueueSize(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);

初始化队列

Queue* QueueInit()
{
	Queue* pq = (Queue*)malloc(sizeof(Queue));
	if (pq == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	pq->head = NULL;
	pq->tail = NULL;
	return pq;
}

检测队列是否为空,如果为空返回true,如果非空返回false

bool IsEmtpy(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

队列的增删查改
队尾入队列

void QueuePush(Queue* pq, DataType x)
{
	assert(pq);

	QueueNode* tmp = (QueueNode*)malloc(sizeof(QueueNode));
	if (tmp == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	tmp->data = x;
	tmp->next = NULL;
	if (pq->tail == NULL)
	{
		pq->tail = tmp;
		pq->head = tmp;
	}
	else
	{
		pq->tail->next = tmp;
		pq->tail = pq->tail->next;
	}
}

队头出队列

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!IsEmtpy(pq));

	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = NULL;
		pq->tail = NULL;
	}
	else
	{
		QueueNode* prev = pq->head;
		pq->head = pq->head->next;
		free(prev);
	}
}

获取队列头部元素

DataType QueueFront(Queue* pq)
{
	assert(pq);

	return pq->head->data;
}

获取队列尾部元素

DataType QueueBack(Queue* pq)
{
	assert(pq);

	return pq->tail->data;
}

获取队列中有效元素个数
在实现获取队列有效元素的个数这个功能时,采用了具有O(n)时间复杂度的算法,这不是一个好的实现方式,特别是在需要频繁调用这个功能的时候,就会有大量的消耗。最好是能够在保证函数接口一种的情况下对这个功能进行优化。

int QueueSize(Queue* pq)
{
	assert(pq);

	int sz = 0;
	QueueNode* cur = pq->head;
	while (cur != NULL)
	{
		sz++;
		cur = cur->next;
	}
	return sz;
}

销毁队列

void QueueDestroy(Queue* pq)
{
	assert(pq);

	while (pq->head != NULL)
	{
		QueuePop(pq);
	}
	free(pq);
}

写这篇文章的初衷就是将这些数据结构的实现记录下来,作为一个模板。感谢阅读。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值