数据结构总结1

数据结构,无非是对数据进行包装和管理。即实现增删查改等基本功能,因此我们只需了解其中内在逻辑,如何设计具体的代码,可依据个人的喜好使逻辑自洽。

1.顺序表

1.1关键点

顺序表一般说来是针对物理空间上连续的数据进行管理,比如数组。
因此通过数组下标操作可基本实现增删查改。
值得注意
①由于空间的弹性,空间不足我们需要开辟新的空间
②前插前删,需要整体挪动数据

typedef int Seqdate;
typedef struct s
{
	Seqdate* pr;//储存数据的地址
	int size;//数据的个数
	int capacity;//申请的空间
}sl;

void Seqinit(sl* ptr);//初始化
void SeqDestory(sl* ptr);//顺序表销毁
void Seqinsertfront(sl* ptr, Seqdate m);//头插
void Seqinsertback(sl* ptr, Seqdate m);//尾插
void Seqpopfront(sl* ptr);//头删
void Seqpopback(sl* ptr);//尾删
void Seqcheckcapacity(sl* ptr);//每次插入时都要检查空间大小
void Print(sl* ptr);//打印
int SeqListFind(sl* ptr, Seqdate x);//查找数据,返回下标
void SeqListInsert(sl* ptr, int pos, Seqdate x);//特定位置前插//后插
void SeqListErase(sl* ptr, int pos);//删除特定位置
1.2代码实现
void SeqDestory(sl* ptr)
{
	if (ptr->pr != NULL)
	{
		free(ptr->pr);
		ptr->pr = NULL;
		ptr->capacity = 0;
		ptr->size = 0;
	}
	return;
}

void Seqinit(sl* ptr)
{
	ptr->pr = NULL;
	ptr->size = 0;
	ptr->capacity = 0;
	return;
}



void Seqcheckcapacity(sl* ptr)
{
	if (ptr->capacity == ptr->size)
	{
		int newcapacity = ptr->capacity == 0 ? 4 : 2 * ptr->capacity;
		Seqdate* tem = (Seqdate*)realloc(ptr->pr, newcapacity * sizeof(Seqdate));
		assert(tem);
		ptr->pr = tem;
		tem = NULL;
		ptr->capacity = newcapacity;
	}
	assert(ptr->pr);
}

void Seqinsertfront(sl* ptr, Seqdate m)
{
	Seqcheckcapacity(ptr);
	int last = ptr->size - 1;
	while (last >= 0)
	{
		*(ptr->pr + last + 1) = *(ptr->pr + last);
		last--;
	}
	ptr->pr[0] = m;
	ptr->size++;
	return;
}

void Seqinsertback(sl* ptr, Seqdate m)
{
	Seqcheckcapacity(ptr);
	ptr->pr[ptr->size] = m;
	ptr->size++;
	return;
}

void Seqpopback(sl* ptr)
{
	Seqcheckcapacity(ptr);
	if (ptr->size > 0)
	{
		ptr->size--;
	}
	return;
}
void Seqpopfront(sl* ptr)
{
	Seqcheckcapacity(ptr);
	int begin = 0;
	while (begin < ptr->size - 1)
	{
		ptr->pr[begin] = ptr->pr[begin + 1];
		begin++;
	}
	ptr->size--;
	return;
}
void Print(sl* ptr)
{
	for (int i = 0;i < ptr->size;i++)
	{
		printf("%d ", ptr->pr[i]);
	}
	printf("\n");
	return;
}
int SeqListFind(sl* ptr, Seqdate x)
{
	int pos = 0;
	while (pos < ptr->size)
	{
		if (ptr->pr[pos] == x)
			return pos;
		pos++;
	}
	return -1;

}
void SeqListInsert(sl* ptr, int pos, Seqdate x)
{
	assert(ptr);
	assert(pos >= 0);
	Seqcheckcapacity(ptr);
	int right = ptr->size - 1;
	while (right >= pos)
	{
		ptr->pr[right + 1] = ptr->pr[right];
		right--;
	}
	ptr->pr[pos] = x;
	ptr->size++;
}
void SeqListErase(sl* ptr, int pos)
{
	assert(ptr);
	assert(pos >= 0);
	int left = pos + 1;
	while (left <= ptr->size - 1)
	{
		ptr->pr[left-1] = ptr->pr[left];
		left++;
	}
	ptr->size--;

}

2.链表

2.1无头不循环单链表

2.1.1关键点

在这里插入图片描述
顺序表物理空间上的连续性决定了它只需一个结构体、一个指针来维护
而链表物理空间上的不连续决定它需要多个结构体、多个指针来维护。
因此链表维护的时间复杂度较高,但空间不受限制。

typedef int SLNodeDate;
typedef struct SLS
{
	SLNodeDate val;
	struct SLS* next;//指向下一个节点
} SLN;
SLN* SLNcteatnode(SLNodeDate x);//创建节点并初始化
void SLNpushfront(SLN** pphead, SLNodeDate x);//头插
void SLNpushback(SLN** pphead, SLNodeDate x);//尾插
void SLNpopfront(SLN** pphead);//头删
void SLNpopback(SLN** pphead);//尾删
SLN* SLNfind(SLN* phead, SLNodeDate x);//查找
void SLNInsert(SLN** phead, SLN* pos, SLNodeDate x);//目标位置前插或后插
void SLNerase(SLN** pphead, SLN* pos);//删除目标位置
void SLNPrint(SLN* phead);//打印链表
void SLNDestory(SLN** phead);//链表销毁

值得注意
①修改变量,函数传变量的地址,即一级指针;
修改指针,传指针的地址,即二级指针
②增删都有可能涉及头指针的修改,故传二级指针
③链表销毁,避免出现野指针,也需要传二级指针
④循环找尾节点

2.1.2代码实现
SLN* SLNcteatnode(SLNodeDate x)
{
	SLN* str = (SLN*)malloc(sizeof(SLN));
	if (str == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	str->val = x;
	str->next = NULL;
	return str;
}
void SLNpushfront(SLN** pphead, SLNodeDate x)
{
	assert(pphead);
	SLN* newnode=SLNcteatnode(x);
	assert(newnode);
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLNpushback(SLN** pphead, SLNodeDate x)
{
	assert(pphead);
	SLN* newnode=SLNcteatnode(x);
	SLN* cur = *(pphead);
	if (cur == NULL)
		*pphead = newnode;
	else
	{
		while (cur->next!=NULL)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
}
void SLNpopfront(SLN** pphead)
{
	assert(*pphead);
	SLN* cur = *pphead;
	*pphead = (*pphead)->next;
	free(cur);
}

void SLNpopback(SLN** pphead)
{
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLN* cur = *pphead;
		SLN* prev = NULL;
		while (cur->next!=NULL)
		{
			prev = cur;
			cur = cur->next;
		}
		free(cur);
		prev->next = NULL;
	}
}
SLN* SLNfind(SLN* phead, SLNodeDate x)
{
	assert(phead);
	SLN* pos = phead;
	while (pos != NULL)
	{
		if (pos->val == x)
		{
			return pos;
		}
		pos = pos->next;
	}
	return NULL;
}
void SLNInsert(SLN** phead, SLN* pos, SLNodeDate x)
{
	assert(phead);
	assert(*phead);
	assert(pos);
	SLN* cur = *phead;
	SLN* newnode = SLNcteatnode(x);
	if (cur == pos)
	{
		newnode->next = cur;
		*phead = newnode;
	}
	else
	{
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		newnode->next = pos;
		cur->next = newnode;
	}
}
void SLNerase(SLN** pphead, SLN* pos)
{
	assert(pphead);
	assert(*pphead);
	if (*pphead == pos)
	{
		free(pos);
		*pphead = NULL;
	}
	else
	{
		SLN* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
void SLNPrint(SLN* phead)
{
	SLN* cur = phead;
	while (cur)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}
void SLNDestory(SLN** phead)
{
	SLN* next = (*phead)->next;
	SLN* cur = *phead;
	while (cur != NULL)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

2.2有头循环双向链表

2.2.1关键点

在这里插入图片描述
双向链表每个节点需要两个指针维护
用空间换取时间使得数据访问十分方便

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
2.2.2代码实现
ListNode* ListCreate()
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	assert(newnode);
	newnode->next = newnode;
	newnode->prev = newnode;
	return newnode;
}
void ListDestory(ListNode* pHead)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	ListNode* next = cur->next;
	while (cur != pHead)
	{
		free(cur);
		cur = next;
		next = cur->next;
	}
	free(cur);
}
void ListPrint(ListNode* pHead)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	printf("ڱλ<->");
	while (cur != pHead)
	{
		printf("%d<->", cur->_data);
		cur = cur->next;
	}
	printf("\n");
}
void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* cur = ListCreate();
	cur->_data = x;
	pHead->prev->next = cur;
	cur->prev = pHead->prev;
	pHead->prev = cur;
	cur->next = pHead;
}
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	if (pHead->next == pHead)
		return;
	ListNode* last = pHead->prev;
	pHead->prev->prev->next = pHead;
	pHead->prev = pHead->prev->prev;
	free(last);
}

void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur=ListCreate();
	assert(cur);
	cur->_data = x;
	cur->next = pHead->next;
	pHead->next->prev = cur;
	pHead->next = cur;
	cur->prev = pHead;
}
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	if (pHead->next == pHead)
		return;
	ListNode* cur = pHead->next;
	pHead->next->next->prev = pHead;
	pHead->next = pHead->next->next;
	free(cur);
}
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
		if (cur->_data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* cur = ListCreate();
	cur->_data = x;
	pos->prev->next = cur;
	cur->prev = pos->prev;
	cur->next = pos;
	pos->prev = cur;
}
void ListErase(ListNode* pos)
{
	assert(pos);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);                 
}

3.栈和队列

栈和队列,是基于上述两种数据结构的,既有数组栈也有链表栈,队列也一样
但栈必须符合先入后出,队列符合先进先出

3.1栈

3.1.1关键点

既然是缩减版,那只需符合先进后出的逻辑即可
数组栈:

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶,也可以理解为size
	int _capacity;  // 容量 
}Stack;
// 初始化栈 
void StackInit(Stack* ps); 
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);
3.1.2代码实现
void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	//栈顶指向元素下一个
	ps->_top = 0;
	//栈顶指向栈顶元素
	//ps->top=-1;

}
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->_capacity == ps->_top)
	{
		//判断空间大小
		int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
		STDataType* ptr = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
		assert(ptr);
		ps->_a = ptr;
		ps->_capacity = newcapacity;
	}
	//栈顶指向下一位
	ps->_a[ps->_top] =x;
	//栈顶指向顶元素
	//ps->_a[ps->_top+1]=x;
	ps->_top++;
}
void StackPop(Stack* ps)
{
	assert(ps);
	//判断栈是否为空
	if (ps->_top == 0)
		return;
	ps->_top--;
}
STDataType StackTop(Stack* ps)
{
	assert(ps);
	//断言不为空
	assert(ps->_top > 0);
	return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}

int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top == 0;
}
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = 0;
}

3.2队列

3.2.1关键点

链表队列:
为了时间上的方便,我们可以把头尾指针以及数据个数放在一个结构体里维护

typedef int Qdate;
typedef struct STL
{
	struct STL* next;
	Qdate val;
}ST;
typedef struct Queue
{
	ST* phead;
	ST* ptail;
	int size;
}QT;

void QueueInit(QT* ptr);//初始化
void QueuePush(QT* ptr, Qdate x);//尾插
bool QueueEmpty(QT* ptr);//判断是否空
void QueuePop(QT* ptr);//头删
int QueueSize(QT* ptr);//队列大小
void QueueDestroy(QT* ptr);//队列销毁
Qdate QueueFront(QT* ptr);//获取头部数据
Qdate QueueBack(QT* ptr);//获取尾部数据
3.2.2代码实现
void QueueInit(QT* ptr)
{
	assert(ptr);
	ptr->phead = ptr->ptail = NULL;
	ptr->size = 0;
}
void QueuePush(QT* ptr, Qdate x)
{
	assert(ptr);
	ST* newnode = (ST*)malloc(sizeof(ST));
	assert(newnode);
	newnode->val = x;
	newnode->next = NULL;
	if (QueueEmpty(ptr))
	{
		ptr->phead = ptr->ptail = newnode;
	}
	else
	{
		ptr->ptail->next = newnode;
		ptr->ptail = newnode;
	}
	ptr->size++;
}
bool QueueEmpty(QT* ptr)
{
	assert(ptr);
	return ptr->size == 0;
}
void QueuePop(QT* ptr)
{
	assert(ptr);
	assert(!QueueEmpty(ptr));
	ST* cur = ptr->phead;
	ptr->phead = ptr->phead->next;
	free(cur);
	if (ptr->phead == NULL)
		ptr->ptail = NULL;
	ptr->size--;
}
int QueueSize(QT* ptr)
{
	assert(ptr);
	return ptr->size;
}
void QueueDestroy(QT* ptr)
{
	assert(ptr);
	ST* cur = ptr->phead;
	while (cur)
	{
		ST* next = cur->next;
		free(cur);
		cur = next;
	}
	ptr->phead = ptr->ptail = NULL;
	ptr->size = 0;
}
Qdate QueueFront(QT* ptr)
{
	assert(ptr);
	assert(ptr->size > 0);
	return ptr->phead->val;
}
Qdate QueueBack(QT* ptr)
{
	assert(ptr);
	assert(ptr->size > 0);
	return ptr->ptail->val;
}
3.3模拟实现

有意思的是二者的相互实现
用栈模拟队列
增加数据,栈和队列同样尾插;
删除数据,栈尾删,队列头删。
设置两个栈,一个入,一个出,出的不够用,将入的反方向叠罗汉给出,出的尾删,即入的头删
用队列模拟栈
一样,两个队列,非空队列用来导数据,空队列用来出数据

3.4循环队列

在这里插入图片描述
循环队列就是限制大小,链接首尾,将空间循环利用
数组队列,虽然空间上不循环,但可以通过%运算,使其限制在指定容量中
循环双向链表,虽然空间上循环,但实现过程复杂。
数组队列
①%运算决定了开辟k+1空间,余数在0-k之间
②front==back,则队列为空
③(back+1) % (k+1) ==front ,则队列满

typedef struct {
    int* a;
    int front;//指向头
    int back;//指向尾的下一位
    int k;//容量
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* ptr=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    ptr->a=(int*)malloc(sizeof(int)*(k+1));
    ptr->front=0;
    ptr->back=0;
    ptr->k=k;
    return ptr;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->front==obj->back)
        return true;
    return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if((obj->back+1)%(obj->k+1)==obj->front)
        return true;
    return false;
    
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入数据
    if(myCircularQueueIsFull(obj))
        return false;
    obj->a[obj->back]=value;
    obj->back=(obj->back+1)%(obj->k+1);
    return true;
    
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除数据
    if(myCircularQueueIsEmpty(obj))
        return false;
    obj->front=(obj->front+1)%(obj->k+1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {//取front
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->a[obj->front];
    
}

int myCircularQueueRear(MyCircularQueue* obj) {//取back
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->a[(obj->back+obj->k)%(obj->k+1)];
    
}



void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
    
}

链表队列
①size== 0,则队列为空
②size== k,则队列满
③怎么判断头插还是尾插比较关键,故直接采用循环链表,创建k个节点,用phead、ptail指向,控制头尾指针实现数据管理

typedef struct Node
{
    struct Node* next;
    int val;
}node;

typedef struct {
    struct Node* phead;//指向头有效节点
    struct Node* ptail;//指向尾有效节点
    int k;//队列最大值
    int size;//记录数据个数
} MyCircularQueue;
node* Mycreatenode(int x)
{
    node* ptr=(node*)malloc(sizeof(node));
    ptr->val=x;
    ptr->next=NULL;
    return ptr;
}


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* ptr=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    ptr->phead=Mycreatenode(0);
    int i=0;
    node* cur=ptr->phead;
    while(i<k-1)//再创建k-1个节点
    {
        cur->next=Mycreatenode(0);
        cur=cur->next;
        i++;
    }
    cur->next=ptr->phead;//形成循环
    ptr->ptail=ptr->phead;
    ptr->k=k;
    ptr->size=0;
    return ptr;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->size==0)
        return true;
    return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if(obj->size==obj->k)
        return true;
    return false;
    
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
        return false;
    //空的情况特殊,删完后,phead后移了,但ptail没动
    if(myCircularQueueIsEmpty(obj))
    {
        obj->ptail=obj->phead;
        obj->phead->val=value;
    }
    else
    {
    obj->ptail->next->val=value;
    obj->ptail=obj->ptail->next;
    }
    obj->size++;
    return true;
    
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return false;
    obj->phead=obj->phead->next;
    obj->size--;
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->phead->val;
    
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->ptail->val;
    
}



void myCircularQueueFree(MyCircularQueue* obj) {
    int i=0;
    while(i<obj->k)
    {
        node* next=obj->phead->next;
        free(obj->phead);
        obj->phead=next;
        i++;
    }
    obj->phead=NULL;
    obj->ptail=NULL;
    obj->size=0;
    obj->k=0;
}
  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值