数据结构学习---表

一、什么是数据结构
1、数据结构的起源
1968年,美国高德纳教授,《计算机程序设计的艺术》的第一卷《基本算法》的出版,开创了数据结构和算法的先河
数据结构是一门研究数据之间的关系和操作的学科,而非计算方法
数据结构+算法=程序 沃斯,凭借这个观点,获得了图灵奖,这句话展示了程序的本质

2、数据结构的基本概念
数据: 所有能输入到计算机中,能被程序处理的描述客观事物的符号 (变量、数组)
数据元素: 组成数据的,有一定意义的基本单位,也称为节点(结构体变量)
数据项: 有独立含义的数据的最小单位,也叫做域 (结构体成员项)
数据结构: 相互之间存在一种或多种特定关系的数据元素的集合
算法: 数据结构所具备的功能,解决特定的问题的方法

3、研究数据结构的三个方面
数据的逻辑关系
数据的存储关系
数据结构的运算

二、逻辑结构与存储结构
数据的逻辑结构:
集合: 数据元素之间同属于一个集体,但是元素之间没有任何关系
线性结构: 数据元素之间存在一对一关系 (表)
树型结构: 数据元素之间存在一对多关系
图型结构: 数据元素之间存在多对多关系
数据的存储结构:
顺序结构:
数据元素存储在连续的内存中,用数据元素的相对位置来表示关系
优点:支持随机访问,访问效率极高、适合查找数据、不易产生内存碎片
缺点:空间利用率低、对内存的要求比较高,插入、删除不方便

    链式结构:
        数据元素存储在彼此独立的内存空间中,每个独立的元素也叫做节点,每个元素之间增加一个数据项用于存储其它元素的地址,以此来表示元素之间的关系    
        优点:插入、删除方便、空间利用率高、适合增删数据          
        缺点:不支持随机访问,只能从前往后逐个访问,容易产生内存碎片

    逻辑结构和存储结构的对应关系
        线性表   顺序 链式   
        树       链式 顺序
        图       顺序+链式
        每种逻辑机构采用什么物理存储结构并没有明确规定,通常根据实现的难易程度、空间要求、时间的要求,综合选择最合适的物理存储结构

三、数据结构和运算
1、建立数据结构 create
2、销毁数据结构 destory
3、清空数据结构 clean
4、数据结构排序 sort
5、插入元素 insert
6、删除元素 delete
7、访问元素 access
8、修改元素 modify
9、查询元素 query
10、遍历数据结构 show print ergodic

四、顺序表和链式表的实现
顺序表:
数据项:
储存元素的内存首地址
表的容量
元素的数量
运算:
创建、销毁、清空、插入、删除、访问、修改、查询、排序、遍历

    注意:
        1、不要越界
        2、要保持元素的连续性
    优点:支持随机访问,修改、访问、查询、排序效率比较高、大块的连续内存不容易产生内存碎片
    缺点:插入、删除元素时不方便、效率比较低,对内存的要求较高

顺序表

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#define TYPE int

//设计顺序表结构
typedef struct Array
{
	TYPE* ptr;   //存储元素的内存首地址
	size_t cal;  //size_t无符号int类型,表的容量
	size_t cnt; //已存储元素的数量

}Array;

// 创建
Array* create_array(size_t size)
{
	// 分配顺序表结构内存
	Array* arr = malloc(sizeof(Array));
	// 分配存储元素的首地址
	arr->ptr = malloc(sizeof(TYPE)*size);
	// 记录容量
	arr->cal = size;
	// 初始化元素的数量
	arr->cnt = 0;
	return arr;
}
// 销毁
void destory_array(Array* arr);
{
	free(arr->ptr);
	free(arr);
}
// 清空
void clean_array(Array* arr);
{
	arr->cnt = 0;
}
// 插入
bool insert_array(Array* arr,int index,TYPE val)
{
	//先判断表是否已满
	if(arr->cnt >= arr->cal) return false;//能省则省,不用写括号
	//判断下标是否能让表中的数据保持连续性
	if(index > arr->cnt)     return false;
// 内存拷贝,向后移动数据
/*
	for(int i=arr->cnt;i>index;i--)
	{
		arr->ptr[i] = arr->ptr[i-1];
	}
	*/
	memmove(arr->ptr+index+1,arr->ptr+index,(arr->cnt-index)*sizeof(TYPE));
	//void *memmove(void *dest, const void *src, size_t n);用->指向地址而不是遍历[i],函数从src中复制n个字符到dest中,并返回dest指针。
	arr->ptr[index] = val;
	arr->cnt++;
	return true;
}
// 删除
bool delete_array(Array* arr,int index)
{
	if(index >= arr->cnt) return false;
	memmove(arr->ptr+index,arr->ptr+index+1,(arr->cnt-index-1)*sizeof(TYPE));
	arr->cnt--;
	return true;
}
// 访问
bool access_array(Array* arr,int index,TYPE* val)
{
	if(index >= arr->cnt)  return false;
	*val = arr->ptr[index];
	return true;
}
// 修改
bool modify_array(Array* arr,int index,TYPE val)
{
	if(index >= arr->cnt) return false;
	arr->ptr[index] = val;
	return true;
}
// 查询
int query_array(Array* arr,TYPE val)
{
	for(int i=0;i<arr->cnt;i++)
	{
		if(val == arr->ptr[i]) return i;
	}
	return -1;
}
// 排序
void sort_array(Array* arr)
{
	int var;
	for(int i=0;i<arr->cnt-1;i++)
	{
		for(int j=i+1;j<arr->cnt;j++)
		{
			if(arr->ptr[j] < arr->ptr[i])
				var = arr->ptr[j];
				arr->ptr[j] = arr->ptr[i];
				arr->ptr[i] = var;
		}
	}
}
// 遍历
void show_array(Array* arr)
{
	for(int i=0;i<arr->cnt;i++)
	{
		printf("%d ",arr->ptr[i]);
	}
	printf("\n");
}

int main(int argc,const char* argv[])
{
	Array* arr = create_array(10);
	for(int i=0;i<5;i++)
	{
		insert_array(arr,0,i+1);
	}
	show_array(arr);

	//delete_array(arr,1);
	TYPE num = 0;
	//printf("%d\n",query_array(arr,6));
	sort_array(arr);
	show_array(arr);
}

链式表

    节点的数据项:
        数据域:    可以是各种类型的若干个数据项
        指针域:    指向下一个节点  
    由若干个节点通过指针域来一对一地连接在一起形成链式表

不带头节点


第一个节点的数据域中存储的是有效的数据
当添加、删除节点时,又可以会修改了头节点的指针,参数就需要使用二级指针
删除时需要获取上一个节点的指针,而头节点没有上一个指针,因此需要额外处理

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计链表的节点
typedef struct Node
{
	TYPE data;			//节点的数据域
	struct Node* next;	//节点的指针域
}Node;

//	创建节点
Node* create_node(TYPE data)
{
	//	分配节点的内存
	Node* node = malloc(sizeof(Node));
	//	初始化数据域 指针域
	node->data = data;
	node->next = NULL;

	return node;
}

//	添加 头添加
void add_list(Node** head,TYPE data)
{
	Node* node = create_node(data);
	node->next = *head;
	*head = node;
}

//	删除 按值删除
bool del_value_list(Node** head,TYPE data)
{
	//	删除头
	if((*head)->data == data)
	{
		Node* temp = *head;
		*head = temp->next;
		free(temp);
		return true;
	}
	//	删除非头
	for(Node* n=*head; NULL != n->next; n=n->next)
	{
		//	找到待删除节点的上一个节点
		if(n->next->data == data)
		{
			//	备份待删除节点
			Node* temp = n->next;
			n->next = n->next->next;
			free(temp);
			return true;
		}
	}
	return false;
}

//	按位置删除
bool del_index_list(Node** head,int index)
{
	if(0 == index)
	{
		Node* temp = *head;
		*head = temp->next;
		free(temp);
		return true;
	}

	Node* n = *head;
	while(0 < --index)
	{
		n = n->next;
		if(NULL == n->next) return false;
	}

	//n是待删除节点的上一个节点
	Node* temp = n->next;
	n->next = temp->next;
	free(temp);
	return true;
}


//	访问
bool access_list(Node* head,int index,TYPE* val)
{
	Node* n = head;
	for(int i=0; i<index; i++)
	{
		n = n->next;
		if(NULL == n)	ref(NULL == n->next) return false;turn false;
	}
	*val = n->data;
	return true;
}

//	排序
void sort_list(Node* head)
{
	for(Node* i = head; NULL != i->next; i=i->next)
	{
		for(Node* j=i->next; NULL != j; j=j->next)
		{
			if(j->data < i->data)
			{
				TYPE temp = i->data;
				i->data = j->data;
				j->data =temp;
			}
		}
	}
}

//	遍历链表
void show_list(Node* head)
{
	for(Node* n=head; NULL != n; n=n->next)
	{
		printf("%d ",n->data);	
	}
	printf("\n");
}

int main(int argc,const char* argv[])
{
	Node* head = NULL;

	for(int i=0; i<10; i++)
	{
		add_list(&head,i+1);	
	}
	show_list(head);

//	del_value_list(&head,8);
	TYPE num = -10;
	access_list(head,0,&num);
	printf("num=%d\n",num);
	sort_list(head);
	show_list(head);

	del_index_list(&head,9);
	show_list(head);



	/*理解链表的本质
	Node* n1 = create_node(1);	
	Node* n2 = create_node(2);	
	Node* n3 = create_node(3);	
	Node* n4 = create_node(4);	
	Node* n5 = create_node(5);	

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = n5;
	for(Node* n=n1; NULL != n; n=n->next)
	{
		printf("%d ",n->data);	
	}
	*/

}

带头节点


第一个节点不使用,仅仅是为了用它来指向第一个数据域有效的节点
进行插入、删除操作时会比不带头节点的链表要方便
注意:其它的操作要从第二个节点开始

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计链表的节点
typedef struct Node
{
	TYPE data;			//节点的数据域
	struct Node* next;	//节点的指针域
}Node;

//	创建节点
Node* create_node(TYPE data)
{
	//	分配节点的内存
	Node* node = malloc(sizeof(Node));
	//	初始化数据域 指针域
	node->data = data;
	node->next = NULL;
	return node;
}

//	头添加
void add_head_list(Node* head,TYPE val)
{
	Node* node = create_node(val);
	node->next = head->next;
	head->next = node;
}
//	尾添加
void add_tail_list(Node* head,TYPE val)
{
	Node* node = create_node(val);
	//	找到最后一个节点
	Node* n =head;
	while(NULL != n->next) n=n->next;
	//	最后一个节点指向新的节点
	n->next = node;
}

//	按位置删除
bool del_index_list(Node* head,int index)
{
	//	找到待删除节点的上一个节点
	Node* n = head;
	for(int i=0; i<index; i++)
	{
		n = n->next;
		if(NULL == n->next) return false;
	}
	//	备份待删除节点
	Node* temp = n->next;
	n->next = temp->next;
	free(temp);
	return true;
}

//	按值删除
bool del_value_list(Node* head,TYPE val)
{
	//n 是待删除节点的上一个节点
	Node* n = head;
	while(NULL != n->next)
	{
		if(val == n->next->data)	
		{
			Node* temp = n->next;
			n->next = temp->next;
			free(temp);
			return true;
		}
		n = n->next;
	}
	return false;
}

//	插入
bool insert_list(Node* head,int index,TYPE val)
{
	Node* n = head;
	for(int i=0; i<index; i++)
	{
		n = n->next;
		if(NULL == n->next) return false;
	}
	// n是待插入节点的上一个节点
	Node* node = create_node(val);
	node->next = n->next;
	n->next = node;
	return true;
}

//	修改
bool modify_list(Node* head,TYPE old,TYPE new)
{
	for(Node* n = head->next; NULL != n; n=n->next)
	{
		if(old == n->data)
		{
			n->data = new;
			return true;
		}
	}
	return false;
}

//	访问
bool access_list(Node* head,int index,TYPE* val)
{
	// n是index下标位置的当前节点
	Node* n = head->next;
	for(int i=0; i<index; i++)
	{
		n = n->next;
		if(NULL == n) return false;
	}
	*val = n->data;
	return true;
}

//	排序
void sort_list(Node* head)
{
	for(Node* i = head->next; NULL !=i->next; i=i->next)
	{
		for(Node* j=i->next; NULL != j; j=j->next)
		{
			if(i->data < j->data)
			{
				TYPE temp = i->data;
				i->data = j->data;
				j->data = temp;
			}
		}
	}
}

//	查询
int query_list(Node* head,TYPE val)
{
	Node* n = head->next;
	for(int i=0; NULL != n; i++)
	{
		if(val == n->data)	return i;
		n = n->next;
	}
	return -1;
}

//	遍历
void show_list(Node* head)
{
	for(Node* n = head->next; NULL != n; n=n->next)
	{
		printf("%d ",n->data);
	}
	printf("\n");
}

int main()
{
	// 创建了带头节点的空链表
	Node* head = create_node(0);
	for(int i=0; i<10; i++)
	{
		add_tail_list(head,rand()%100);	
	}
	show_list(head);
//	del_value_list(head,100);
//	insert_list(head,1,100);
	modify_list(head,21,99);
	int num = -100;
	access_list(head,9,&num);
	printf("num=%d\n",num);
	sort_list(head);
	printf("index=%d\n",query_list(head,92));
	show_list(head);
}

数组和链表的区别、优缺点?

数组 一、数组的特点
1.在内存中,数组是一块连续的区域
2.数组需要预留空间

在使用前需要提前申请所占内存的大小,这样不知道需要多大的空间,就预先申请可能会浪费内存空间,即数组空间利用率低
ps:数组的空间在编译阶段就需要进行确定,所以需要提前给出数组空间的大小(在运行阶段是不允许改变的)

3.在数组起始位置处,插入数据和删除数据效率低。

插入数据时,待插入位置的的元素和它后面的所有元素都需要向后搬移 删除数据时,待删除位置后面的所有元素都需要向前搬移

4.随机访问效率很高

因为数组的内存是连续的,想要访问那个元素,直接从数组的首地址处向后偏移就可以访问到了

5.数组开辟的空间,在不够使用的时候需要扩容,扩容的话,就会涉及到需要把旧数组中的所有元素向新数组中搬移
6.数组的空间是从栈分配的

二、数组的优点
随机访问性强,查找速度快

三、数组的缺点
1.头插和头删的效率低,时间复杂度为O(N)
2.空间利用率不高
3.内存空间要求高,必须有足够的连续的内存空间
4.数组空间的大小固定,不能动态拓展

链表 一、链表的特点
1.在内存中,元素的空间可以在任意地方,空间是分散的,不需要连续
2.链表中的元素都会两个属性,一个是元素的值,另一个是指针,此指针标记了下一个元素的地址

每一个数据都会保存下一个数据的内存的地址,通过此地址可以找到下一个数据

3.查找数据时效率低,时间复杂度为O(N)

因为链表的空间是分散的,所以不具有随机访问性,如要需要访问某个位置的数据,需要从第一个数据开始找起,依次往后遍历,直到找到待查询的位置

4.空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,故空间的利用率较高
5.任意位置插入元素和删除元素效率较高
6.链表的空间是从堆中分配的

二、链表的优点
1.任意位置插入元素和删除元素的速度快
2.内存利用率高,不会浪费内存
3.链表的空间大小不固定,可以动态拓展
综上:

对于想要快速访问数据,不经常有插入和删除元素的时候,选择数组
对于需要经常的插入和删除元素,而对访问元素时的效率没有很高要求的话,选择链表
———————————————— 版权声明:本文为CSDN博主「dangzhangjing97」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dangzhangjing97/article/details/81699050

功能受限的表

: 对表结构加以限制,形成特殊的表结构

:只有一个进出口,先进后出,FILO

顺序栈


数据项:
存储元素的内存首地址
栈的容量
栈顶位置
运算:
创建、销毁、入栈、出栈、栈满、栈空、栈顶
有四种顺序栈:
top: 初值: 0 入栈 top++ 空增栈
top:初值:-1 top++ 入栈 满增栈
top:初值:cal-1 入栈 top-- 空减栈
top:初值:cal top-- 入栈 满减栈

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计顺序栈结构
typedef struct ArrayStack
{
	TYPE* ptr;	//	存储元素的内存首地址
	size_t cal;	//	栈的容量
	size_t top;	//	栈顶位置	指向接下去要入栈的位置下标
}ArrayStack;

//	创建
ArrayStack* create_array_stack(size_t cal)
{
	//	为顺序栈结构分配内存
	ArrayStack* stack = malloc(sizeof(ArrayStack));	
	//	为存储元素分配内存
	stack->ptr = malloc(cal*sizeof(TYPE));
	//	初始化栈结构的容量
	stack->cal = cal;
	//	初始化栈顶位置
	stack->top = 0;
	return stack;
}

//	销毁
void destory_array_stack(ArrayStack* stack)
{
	free(stack->ptr);
	free(stack);
}

//	栈空
bool empty_array_stack(ArrayStack* stack)
{
	return !stack->top;	
}

//	栈满
bool full_array_stack(ArrayStack* stack)
{
	return stack->top >= stack->cal;	
}

//	入栈
bool push_array_stack(ArrayStack* stack,TYPE val)
{
	if(full_array_stack(stack)) return false;
	stack->ptr[stack->top++] = val;
	return true;
}

//	出栈
bool pop_array_stack(ArrayStack* stack)
{
	if(empty_array_stack(stack)) return false;
	stack->top--;
	return true;

}

//	察看栈顶
bool top_array_stack(ArrayStack* stack,TYPE* val)
{
	if(empty_array_stack(stack))	return false;
	*val = stack->ptr[stack->top-1];
	return true;
}

bool is_pop_stack(int* a,int* b,size_t len)
{
	//创建栈
	ArrayStack* stack = create_array_stack(len);
	//按a顺序入栈
	for(int i=0,j=0; i<len; i++)
	{
		push_array_stack(stack,a[i]);
		//按b顺序出栈
		TYPE val = 0;
		while(top_array_stack(stack,&val) && val == b[j])
		{
			pop_array_stack(stack);
			j++;
		}
	}
	bool flag = empty_array_stack(stack);
	destory_array_stack(stack);
	return flag;

}

int main(int argc,const char* argv[])
{
	int a[5] = {1,2,3,4,5};
	int b[5] = {3,1,2,4,5};
	printf("%s\n",is_pop_stack(a,b,5)?"是出栈顺序":"不是出栈顺序");
	/*
	ArrayStack* stack = create_array_stack(10);
	TYPE top = 0;
	for(int i=0; i<10; i++)
	{
		push_array_stack(stack,i+1);	
		top_array_stack(stack,&top) && printf("top:%d\n",top);
	}
	printf("---------------\n");
	for(int i=0; i<10; i++)
	{
		top_array_stack(stack,&top) && printf("top:%d\n",top);
		pop_array_stack(stack);
	}
	*/

}

链式栈

(链式栈不会栈满)
数据项:
栈顶节点
节点的数量
运算:
创建、销毁、入栈、出栈、栈空、栈顶

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计链式节点
typedef struct Node
{
	TYPE data;
	struct Node* next;
}Node;

//	创建节点
Node* create_node(TYPE data)
{
	Node* node = malloc(sizeof(Node));
	node->data = data;
	node->next = NULL;
	return node;
}

// 设计链式栈结构
typedef struct ListStack
{
	Node* top;	//指向栈顶节点
	size_t cnt;	//栈中的节点个数
}ListStack;

//	创建栈结构
ListStack* create_list_stack(void)
{
	ListStack* stack = malloc(sizeof(ListStack));
	stack->top = NULL;
	stack->cnt = 0;
	return stack;
}

//	栈空
bool empty_list_stack(ListStack* stack)
{
	return NULL == stack->top;	
}

//	入栈
void push_list_stack(ListStack* stack,TYPE val)
{
	Node* node = create_node(val);
	node->next = stack->top;
	stack->top = node;
	stack->cnt++;
}

//	出栈
bool pop_list_stack(ListStack* stack)
{
	if(empty_list_stack(stack)) return false;
	Node* temp = stack->top;
	stack->top = temp->next;
	free(temp);
	stack->cnt--;
	return true;
}

//	栈顶 通过判断栈非空确保栈中有数据,来访问栈顶,由调用者判断
TYPE top_list_stack(ListStack* stack)
{
	return stack->top->data;	
}

//	节点数 不应该在外面访问数据结构的成员(打破了封装性)
size_t size_list_stack(ListStack* stack)
{
	return stack->cnt;	
}

//	销毁
void destory_list_stack(ListStack* stack)
{
	while(pop_list_stack(stack));
	free(stack);
}

int main(int argc,const char* argv[])
{
	ListStack* stack = create_list_stack();
	for(int i=0; i<10; i++)
	{
		push_list_stack(stack,i+1);
		!empty_list_stack(stack) &&
			printf("%d  ",top_list_stack(stack));
		printf("---------%d\n",size_list_stack(stack));
	}
	printf("==============\n");
	for(int i=0; i<10; i++)
	{
		!empty_list_stack(stack) &&
			printf("%d  ",top_list_stack(stack));
		printf("---------%d\n",size_list_stack(stack));
		pop_list_stack(stack);
	}
}

    栈的应用:
        1、函数的调用(栈内存)
        2、生产者消费者模型,栈作为仓库
        3、表达式的解析(中缀表达式转后缀表达式)
    常见的面试笔试题:
        某序列为入栈序列,判断正确的出栈序列(先进后出)
        1 2 3 4 5
        3 1 2 4 5   NO
    练习:
        实现一个函数,判断b序列是否是a序列的出栈顺序
        int a[5] = {1,2,3,4,5};
        int b[5] = {3,2,1,4,5};
        is_pop_stack(a,b,5);
        bool is_pop_stack(int* a,int* b,size_t len)
        {
            //  创建一个空栈
            //  按a的顺序一个一个入栈
            //  a每入栈一个数据,按b顺序出栈
            //  最后查看是否栈空,是则b是a的出栈顺序
        }
        
        问题:
            两个顺序栈如何使用才能使空间利用率最大化?

队列

    一个端口进,另一个端口出,先进先出 FIFO
    队列是不能遍历的

顺序队列

        数据项:
            存储元素的内存首地址
            容量
            队头    出
            队尾    入  即将入队的位置
        运算:
            创建、销毁、入队、出队、队空、队满、队头、队尾、元素数量
        
        顺序队列是由一维数组+队头位置front+队尾位置rear组成,入队时rear+1,出队时front+1,为了让队列能够反复使用,我们要把一维数组想象成一个环,因此front、rear加1后都要对容量cal求余
            front = (front+1)%cal
            rear = (rear+1)%cal

            如何判断队空:front == rear
            如何判断队满:front == (rear+1)%cal
             如此判断队满的方式的代价就是有一个空位置不能使用,或者可以添加一个数据项标记队列为空或者满
            第一个版本常考,第二个版本只是为了实现方便,但是是非常规的

        计算顺序队列中元素数量:
            (rear-front+cal)%cal

第一个版本 一个空位置不能使用

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计顺序队列结构
typedef struct ArrayQueue
{
	TYPE* ptr;	//存储元素的首地址
	size_t cal;	//容量
	size_t front;	//队头
	size_t rear;	//队尾
}ArrayQueue;

//	创建 多申请一个元素位置来解决判断队满的问题
ArrayQueue* create_array_queue(size_t cal)
{
	ArrayQueue* queue = malloc(sizeof(ArrayQueue));
	queue->ptr = malloc((cal+1)*sizeof(TYPE));
	queue->cal = cal+1;
	queue->front = 0;
	queue->rear = 0;
	return queue;
}
//	销毁
void destory_array_queue(ArrayQueue* queue)
{
	free(queue->ptr);
	free(queue);
}

//	队空
bool empty_array_queue(ArrayQueue* queue)
{
	return queue->front == queue->rear;	
}

//	队满
bool full_array_queue(ArrayQueue* queue)
{
	return (queue->rear+1)%queue->cal == queue->front;
}

//	入队
bool push_array_queue(ArrayQueue* queue,TYPE val)
{
	if(full_array_queue(queue))	return false;
	queue->ptr[queue->rear] = val;
	queue->rear = (queue->rear+1)%queue->cal;
	return true;
}

//	出队
bool pop_array_queue(ArrayQueue* queue)
{
	if(empty_array_queue(queue)) return false;
	queue->front = (queue->front+1)%queue->cal;
	return true;
}

//	队头
TYPE head_array_queue(ArrayQueue* queue)
{
	return queue->ptr[queue->front];	
}

//	队尾
TYPE tail_array_queue(ArrayQueue* queue)
{
	return queue->ptr[(queue->rear-1+queue->cal)%queue->cal];
}

//	数量
size_t size_array_queue(ArrayQueue* queue)
{
	return (queue->rear - queue->front + queue->cal)%queue->cal;	
}

int main(int argc,const char* argv[])
{
	ArrayQueue* queue = create_array_queue(10);
	for(int i=0; i<10; i++)
	{
		push_array_queue(queue,i);
		printf("tail:%d ",tail_array_queue(queue));
		printf("size:%d\n",size_array_queue(queue));
	}
	printf("---------------------\n");
	while(!empty_array_queue(queue))
	{
		printf("head:%d ",head_array_queue(queue));
		printf("size:%d\n",size_array_queue(queue));
		pop_array_queue(queue);
	}
}

第二个版本 加一个数据项用来标记空或满

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计顺序队列
typedef struct ArrayQueue
{
	TYPE* ptr;	//	内存首地址
	size_t cal;	//	队列容量
	size_t cnt;	//	元素个数
	size_t front;	//	队头
	size_t rear;	//	队尾
}ArrayQueue;

//	创建
ArrayQueue* create_array_queue(size_t cal)
{
	ArrayQueue* queue = malloc(sizeof(ArrayQueue));
	queue->ptr = malloc(sizeof(TYPE)*cal);
	queue->cal = cal;
	queue->cnt = 0;
	queue->front = 0;
	queue->rear = -1;	//队尾指向最后一个元素的位置
}

//	销毁
void destory_array_queue(ArrayQueue* queue)
{
	free(queue->ptr);
	free(queue);
}

//	队空
bool empty_array_queue(ArrayQueue* queue)
{
	return 0 == queue->cnt;	
}	

//	队满
bool full_array_queue(ArrayQueue* queue)
{
	return queue->cal == queue->cnt;	
}

//	入队
bool push_array_queue(ArrayQueue* queue,TYPE val)
{
	if(full_array_queue(queue)) return false;
	queue->rear = (queue->rear+1)%queue->cal;
	queue->ptr[queue->rear] = val;
	queue->cnt++;
	return true;
}

//	出队
bool pop_array_queue(ArrayQueue* queue)
{
	if(empty_array_queue(queue)) return false;
	queue->front = (queue->front+1)%queue->cal;
	queue->cnt--;
	return true;
}

//	队头
TYPE head_array_queue(ArrayQueue* queue)
{
	return queue->ptr[queue->front];	
}

//	队尾
TYPE tail_array_queue(ArrayQueue* queue)
{
	return queue->ptr[queue->rear];	
}

//	数量
size_t size_array_queue(ArrayQueue* queue)
{
	return queue->cnt;	
}

int main(int argc,const char* argv[])
{
	ArrayQueue* queue = create_array_queue(10);
	for(int i=0; i<10; i++)
	{
		push_array_queue(queue,i);
		printf("tail: %d ",tail_array_queue(queue));
		printf("cnt: %d\n",size_array_queue(queue));
	}
	printf("------------------\n");
	while(!empty_array_queue(queue))
	{
		printf("head: %d ",head_array_queue(queue));
		printf("cnt: %d\n",size_array_queue(queue));
		pop_array_queue(queue);
	}
}

链式队列:

    由若干个节点组成的队列
    数据项:
        队头指针
        队尾指针
        节点数量
    运算:
        创建、销毁、队空、入队、出队、队头、队尾、数量
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int
//	设计节点
typedef struct Node
{
	TYPE data;
	struct Node* next;
}Node;

//	创建节点
Node* create_node(TYPE val)
{
	Node* node = malloc(sizeof(Node));
	node->data = val;
	node->next = NULL;
	return node;
}

//	设计链式队列结构
typedef struct ListQueue
{
	Node* head;	//	指向队头节点的指针
	Node* tail;	//	指向队尾节点的指针
	size_t size;//	节点个数
}ListQueue;

//	创建队列结构
ListQueue* create_list_queue(void)
{
	ListQueue* queue = malloc(sizeof(ListQueue));
	queue->head = NULL;
	queue->tail = NULL;
	queue->size = 0;
	return queue;
}


//	队空
bool empty_list_queue(ListQueue* queue)
{
	return !queue->size;	
}

//	入队
void push_list_queue(ListQueue* queue,TYPE val)
{
	Node* node = create_node(val);	
	if(empty_list_queue(queue))
	{
		queue->head = node;
		queue->tail = node;
	}
	else
	{
		queue->tail->next = node;	
		queue->tail = node;
	}
	queue->size++;
}

//	出队
bool pop_list_queue(ListQueue* queue)
{
	if(empty_list_queue(queue)) return false;
	Node* temp = queue->head;
	queue->head = temp->next;
	queue->size--;
	free(temp);
	empty_list_queue(queue) && (queue->tail = NULL);//当为空时,尾指针指向空,避免野指针
	return true;
}

//	队头
TYPE head_list_queue(ListQueue* queue)
{
	return queue->head->data;	
}

//	队尾
TYPE tail_list_queue(ListQueue* queue)
{
	return queue->tail->data;	
}

//	数量
size_t size_list_queue(ListQueue* queue)
{
	return queue->size;	
}

//	销毁
void destory_list_queue(ListQueue* queue)
{
	while(pop_list_queue(queue));
	free(queue);
}

int main(int argc,const char* argv[])
{
	ListQueue* queue = create_list_queue();
	for(int i=0; i<20; i++)
	{
		push_list_queue(queue,i);
		printf("tail:%d ",tail_list_queue(queue));
		printf("size:%d\n",size_list_queue(queue));
	}
	printf("----------------\n");
	while(!empty_list_queue(queue))
	{
		printf("head:%d ",head_list_queue(queue));
		printf("size:%d\n",size_list_queue(queue));
		pop_list_queue(queue);
	}
	destory_list_queue(queue);
}

队列的应用:
    1、消息的排队
    2、树的层序遍历
    3、图的广度优先遍历
    4、封装线程池、数据池
    
常见的队列笔试面试题
    如何使用两个栈来模拟一个队列的功能
        栈1不满,都可以入队
        栈1满,栈2空,可以入队,前提是从栈1到栈2一个不留
        栈2不空栈,栈1一定不能到栈2
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TYPE int

//	设计顺序栈结构
typedef struct ArrayStack
{
	TYPE* ptr;	//	存储元素的内存首地址
	size_t cal;	//	栈的容量
	size_t top;	//	栈顶位置	指向接下去要入栈的位置下标
}ArrayStack;

//	创建
ArrayStack* create_array_stack(size_t cal)
{
	//	为顺序栈结构分配内存
	ArrayStack* stack = malloc(sizeof(ArrayStack));	
	//	为存储元素分配内存
	stack->ptr = malloc(cal*sizeof(TYPE));
	//	初始化栈结构的容量
	stack->cal = cal;
	//	初始化栈顶位置
	stack->top = 0;
	return stack;
}

//	销毁
void destory_array_stack(ArrayStack* stack)
{
	free(stack->ptr);
	free(stack);
}

//	栈空
bool empty_array_stack(ArrayStack* stack)
{
	return !stack->top;	
}

//	栈满
bool full_array_stack(ArrayStack* stack)
{
	return stack->top >= stack->cal;	
}

//	入栈
bool push_array_stack(ArrayStack* stack,TYPE val)
{
	if(full_array_stack(stack)) return false;
	stack->ptr[stack->top++] = val;
	return true;
}

//	出栈
bool pop_array_stack(ArrayStack* stack)
{
	if(empty_array_stack(stack)) return false;
	stack->top--;
	return true;

}

//	察看栈顶
bool top_array_stack(ArrayStack* stack,TYPE* val)
{
	if(empty_array_stack(stack))	return false;
	*val = stack->ptr[stack->top-1];
	return true;
}

bool is_pop_stack(int* a,int* b,size_t len)
{
	//创建栈
	ArrayStack* stack = create_array_stack(len);
	//按a顺序入栈
	for(int i=0,j=0; i<len; i++)
	{
		push_array_stack(stack,a[i]);
		//按b顺序出栈
		TYPE val = 0;
		while(top_array_stack(stack,&val) && val == b[j])
		{
			pop_array_stack(stack);
			j++;
		}
	}
	bool flag = empty_array_stack(stack);
	destory_array_stack(stack);
	return flag;

}

//	使用两个栈来模拟队列
//	设计队列
typedef struct Queue
{
	ArrayStack* s1;
	ArrayStack* s2;
}Queue;

//	创建队列
Queue* create_queue(size_t cal)
{
	Queue* queue = malloc(sizeof(Queue));
	queue->s1 = create_array_stack(cal);
	queue->s2 = create_array_stack(cal);
	return queue;
}

//	销毁队列
void destory_queue(Queue* queue);

//	队空
bool empty_queue(Queue* queue)
{
	return empty_array_stack(queue->s1) && empty_array_stack(queue->s2);	
}

//	队满
bool full_queue(Queue* queue)
{
	return full_array_stack(queue->s1) && full_array_stack(queue->s2);	
}

//	入队
bool push_queue(Queue* queue,TYPE val)
{
	if(full_array_stack(queue->s1))
	{
		//	栈1满,而栈2非空,则无法入队
		if(!empty_array_stack(queue->s2)) return false;
		//	栈1满,但栈2空,可以入队
		while(!empty_array_stack(queue->s1))
		{
			//	把栈1一个不留地入到栈2中
			TYPE top;
			top_array_stack(queue->s1,&top);
			push_array_stack(queue->s2,top);
			pop_array_stack(queue->s1);
		}
	}
	push_array_stack(queue->s1,val);
	//测试队尾	
	TYPE top;
	top_array_stack(queue->s1,&top);
	printf("tail:%d\n",top);
	//
	return true;
}

//	出队
bool pop_queue(Queue* queue)
{
	if(empty_array_stack(queue->s2))
	{
		if(empty_array_stack(queue->s1)) return false;
		//把栈1入到栈2中
		while(!empty_array_stack(queue->s1))
		{
			TYPE top = 0;
			top_array_stack(queue->s1,&top);
			push_array_stack(queue->s2,top);
			pop_array_stack(queue->s1);
		}
	}
	//测试队头
	TYPE top = 0;
	top_array_stack(queue->s2,&top);
	printf("head:%d\n",top);
	//
	pop_array_stack(queue->s2);	
	return true;
}

int main(int argc,const char* argv[])
{

	Queue* queue = create_queue(10);
	for(int i=0; i<10; i++)
	{
		push_queue(queue,i);	
	}
	printf("-------------\n");
	while(!empty_queue(queue))
	{
		pop_queue(queue);	
	}
}

复习:

1、什么是数据结构
    是专门研究数据之间关系和操作的学科,而非计算方法
    数据结构+算法=程序
2、逻辑结构和物理结构
    逻辑结构: 
        集合:  除了同属于一个集合外,数据之间没有任何关系
        表:    数据之间存在一对一的关系
        树:    数据之间存在一对多的关系
        图:    数据之间存在多对多的关系
    物理结构:
        顺序结构:数据存储在连续的内存中,使用数据的相对位置来表示数据之间的关系
        链式结构:数据分散存储在内存中的任何位置,数据项有一块指针域用来表示数据之间的关系

    逻辑结构和物理结构的对应关系:
        表:    顺序 链式
        树:    链式 顺序
        图:    顺序+链式

3、数据结构和运算
    创建、销毁、清空、添加、删除、查询、修改、排序、访问、遍历

4、表结构
    顺序表:    数组    
    链式表:    链表
    常见的面试题: 数组和链表的区别、优缺点?

5、功能受限的表结构
    栈:    只有一个出入口,先进后出,FILO
        有四种顺序栈:
            top:初值:  0     入栈  top++   空增栈
            top:初值:-1     top++ 入栈    满增栈
            top:初值:cal-1   入栈 top--   空减栈
            top:初值:cal     top-- 入栈   满减栈

    队列:  有两个出入口,一个只能进,一个只能出,先进先出
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值