数据结构2.0

2.1 线性表

线性表(List) 是由零个或多个数据元素的有限序列,允许在任意位置进行插入、删除和访问等操作。

线性表有两种物理结构——顺序存储结构链式存储结构

2.1.1 顺序存储结构

线性表的顺序存储结构是指,用一段地址连续的存储单元依次存储线性表的数据元素。

数组是一种使用顺序存储方式的数据结构。

 首先是,线性表的顺序结构的结构代码:

#include<stdio.h>
#define MAX 66 
//线性表所占存储空间的大小
typedef struct
{
    int a[N];//此处int可换为其他数据类型,这主要看线性表元素的类型,数组a[N]用来存储线性表元素
    int length;//线性表的当前长度
}List;

 接着是,访问,访问有两种方式:

第一种是根据元素访问,返回元素的位置:(代码接上)

int getElem(List L,int x)//x为要查找的元素
{
    int i=0;
    while(i<=L.length&&L.a[i]!=x)//遍历线性表
        i++;
    if(i>L.length)//如果i比线性表的长度还大,则说明线性表没有此元素,返回-1
        return -1;
    else //找到返回它的位置
        return i;
}

第二种是根据下标进行访问,返回元素本身:

int getElem(List L,int n)
{
    int s = L.a[n];
    return s;
}

然后是,增加: 

bool Insert(List L,int s,int i)
{
    if(L.length==MAX-1)
    {
        printf("表已满\n");
        return false;
    }
    if(i<1||i>L.length+2)
    {
        printf("位序不合理\n");
        return false;
    }
    for(int j=L.length;j>i;j--)
        L.a[j]=L.a[j-1];
    L.a[i]=s;
    return true;
}

最后是,删除:

bool Delete(List L,int i)
{
    if(i<1||i>=L.length)
    {
        printf("该数不存在!\n");
        return false;
    }
    for(int j=i+1;j<L.length;j++)
        L.a[j-1]=L.a[j];
    return true;
}

2.1.2 链式存储结构

线性表的链式存储结构是指,用一组任意的存储单元储线性表的数据元素,这组存储元素可以是连续的,也可以是不连续的。这就是说,这些元素可以存在着内存未被占用的任意位置。

链表也是使用链式存储方式实现的一种数据结构。

 2.1.2.1 单向链表

首先定义单链表节点的结构体:

#include<stdio.h>
struct Node
{
	int date;//数据元素 
	struct Node* next;//指向下一个节点的指针 
};

创建节点:

struct Node* CreateList(int date)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));//给节点赋予空间
	newNode->date = date; //数据域
	newNode->next = NULL; //指针域
} 
void BuildList(struct Node* head,int x)//x为元素
{
	//创建新节点 
	struct Node* newNode = CreateList(x);
	//找节点尾部 
	while(head->next!=NULL)
		head = head->next;
	//指向下一个节点 
	head->next = newNode;
}

接着是插入操作

void InsertNode(struct Node* head,int p,int x)//p为插入的位置,x为具体插入的值 
{
	struct Node* newNode = CreateNode(x);//新建一个节点 
	
	//last(链头的地址)---now(第一个数据地址)---...---NULL 
	struct Node* last = head;//last(上一个)地址为链头的地址 
	struct Node* now = head->next;//now(当前节点)地址为链头指向的地址 
	while(--p&&now!=NULL)//寻找要插入的位置(now)&&防止p大于链表的值 
	{
		now = now->next;//迭代now节点 
		last = last->next;//迭代last节点 
	}
	//插入操作 
	last->next = newNode;
	newNode->next = now;
} 

根据已知条件,删除方式分为两种:按位置删除和按元素删除

 第一种是按位置删除:

void EraseNode(struct Node* head,int p)//p为要删除的位置 
{
	//与插入操作类似 
	struct Node* last = head;
	struct Node* now = head->next;
	
	while(--p&&now!=NULL)
	{
		now = now->next;
		last = last->next;
	}
	//删除操作 
	last->next = now->next;
	now->next = NULL;//不要忘记!!! 
	free(now);//释放空间 
}

第二种是按元素删除:

void DeleteNode(struct Node* head,int x)
{
	struct Node* last = head;
	struct Node* now = head->next;
	
	while(now!=NULL)
	{
		if(now->data==x)
		{
			last->next = now->next;
			now->next=NULL;
			free(now);
			now = last->next;
		}
		else
		{
			now = now->next;
			last = last->next;
		}
	}
} 

接着是查找元素:

struct Node* FindNode(struct Node* head,int x)
{
	struct Node* now = head->next;
	while(now)
	{
		if(now->data==x)
		{
			return now;
		}
		now = now->next;
	}
	return NULL;
}

修改元素:

void ChangeNode(struct Node* head,int p,int x)
{
	struct Node* now = head->next;
	while(--p&&now!=NULL)
	{
		now = now->next;
	}
	if(!p)
		now->data = x;
	else
		return;
}

2.1.3 单链表结构与顺序结构的优缺点

[1].单链表结构:

优点:

  1. 动态性,不需要预先知道存储空间的大小,可以动态的减少和增加,节省内存。
  2. 插入和删除操作便捷,仅仅只需要指针的指向即可,时间复杂度为O(1),效率高。
  3. 灵活性,可处理不同的数据类型。

缺点:

  1. 访问效率低,需要从头遍历进行查找。
  2. 内存消耗较多,结构上,与顺序存储结构相比,一个节点需要数据域和额外的指针域。
  3. 无法逆向遍历,指针方向是确定的。

[2].顺序结构:

优点:

  1. 访问效率高,通过下标就可以访问,时间复杂度为O(1),效率高。
  2. 存储空间利用率高,连续的存储空间,且没有额外的指针域,对于大型数据集合存储效率较高。
  3. 可逆向遍历。

缺点:

  1. 固定大小,需要预先确定数据大小。
  2. 插入和删除操作繁琐,插入和删除均要移动多个元素,效率低。
  3. 灵活性差,对于底层数据结构的操作有一定的限制,不太适合频繁的动态修改操作。

[3]. 有以上可知,当线性表需要频繁查找,插入和删除操作较少时,则使用顺序存储;当线性表的大小未知,或变化较大时,最好使用链表存储。

2.2 堆栈

堆栈的别称又叫栈,栈(Stack)是限定仅在栈顶(Top)进行插入和删除操作的线性表,也是线性表的特殊形式。因此它也有顺序链式两种存储结构,它的主要操作有两种,入栈(Push)出栈(Pop)。其主要特点是后进先出

2.2.1 堆栈的顺序存储实现形式

首先是顺序存储结构的入栈(Push)与出栈(Pop)。

首先定义栈的结构

#include<stdio.h>
#define MAX 100
struct SqStack
{
	int data[MAX];//data数组存放栈的数据 
	int top;//栈顶的位置 
};

然后是入栈操作:

bool Push(SqStack S,int x)//x为新的栈顶元素 
{
	if(S.top==MAX-1)//判断是否栈满 
		return false;//若栈满则不能进栈
	S.top++;
	S.data[S.top-1] = x;
	return true; 
}

 最后是出栈操作:

int Pop(SqStack S)
{
	if(S.top==-1)//判断是否为空
		return -1;
	S.top--;
	ShowSqStack(S);
	return S.data[S.top]; //返回出栈元素
} 

2.2.2 堆栈的链式存储实现形式(又作链栈)

接着是链式存储结构的出栈(Push)与入栈(Pop)。

同样的,首先需要定义一个链栈的结构

#include<stdio.h>
#include<stdlib.h>
struct StackNode
{
	int data;//数据
	struct StackNode* next;//下个节点的地址
};

然后创建一个空的链栈:

struct StackNode* CreateNode()
{
	return NULL; 
}

接着是入栈(Push)操作:

void Push(struct StackNode* Stack,int data)
{
	struct StackNode* newNode = (struct StackNode*)malloc(sizeof(StackNode));//为每一个新节点分配空间 
	newNode->data = data;
	newNode->next = Stack->next;//将新节点放在栈顶(新节点下一个地址指向Stack,说明新节点在Stack前面) 
	
	Stack->next = newNode;//迭代栈指针 
}

 由于出栈之前需要判断是否为空栈,所以先写一个函数判断:

bool isEmpty(struct StackNode* Stack)
{
	return Stack->next==NULL;
}

 接下来是出栈:

int Pop(struct StackNode* Stack)
{
	if(isEmpty(Stack))
	{
		printf("栈空!不能做出栈操作\n");
		return -1;
	}
	struct StackNode* top = Stack->next;
	int popdata = top->data;
	Stack->next = top->next;//不要忘记!!! 直接指向下一个节点
	free(top);//释放空间 
	return popdata;
} 

 2.2.3 堆栈应用(表达式求值)

表达式一般由运算数和运算符号组成。

中缀表达式,即运算符号位于两个运算数之间的表达式。 这个就是我们平常写的算术表达式,例如:1+2*3.

后缀表达式,即运算符号位于两个运算数之后的表达式。这个表达式更方便计算,例如:123*+。(乘号“ * ”的优先级比加号“ + ”的优先级高,因此先算乘法)

后缀表达式的计算原则:碰到运算数就记住,遇到运算符号就与记住的最近的两个运算数做对应的计算

中缀表达式要转后缀表达式,需要考虑运算符号的优先级。从左到右遍历中缀表达式的每个数字和符号,如果是数字就输出,如果是符号就判断它与栈顶符号的优先级,优先级高就输出

(数字输出,运算符号就放进栈中)

2.3 队列

队列(Queue)是只允许在一端进行插入操作,另一端进行删除操作的线性表,也是线性表的特殊形式。主要是在列头(front)删除,在列尾(rear)增加。主要操作也就是入队出队

2.3.1 队列的顺序存储实现形式

#include<stdio.h>
#include<stdlib.h>
#define MAX 100
struct Queue
{
	int data[MAX];//存数据 
	int front;//头指针
	int rear;//尾指针 
}; 
struct Queue* CreateQueue()//创建空列队 
{
	struct Queue* queue = (struct Queue*)malloc(sizeof(Queue));
	queue->front = 0;
	queue->rear = 0;
	return queue;
}
bool isFull(struct Queue* q)//判断队满 
{
	return(q->rear==MAX-1);
}
void enQueue(struct Queue* q,int data)
{
	if(isFull(q))
		printf("队列满,不可再入队\n");
	else
	{
		q->data[q->rear] = data;
		q->rear++;
	}
}
bool isEmpty(struct Queue* q)
{
	return(q->rear==0);
}
int deQueue(struct Queue* q)
{
	if(isEmpty(q))
	{
		printf("队列为空,不可删除\n");
		return -1;
	}
	int dedata = q->data[q->front];
	q->front++;
	return dedata;
}
int main()
{
	struct Queue* q = CreateQueue();
	for(int i=0;i<5;i++)
	{
		q->data[i]=i;
	}
	q->rear = 5;
	enQueue(q,6);
	for(int i=0;i<q->rear;i++)
	{
		printf("列队元素为%d\n",q->data[i]);
	}
	printf("出队元素为%d\n",deQueue(q));
	printf("头指针为%d,尾指针为%d\n",q->front,q->rear); 	
} 

2.3.2 队列的链式存储实现形式

#include<stdio.h>
#include<stdlib.h>
//队列节点结构体 
struct QNode
{
	int data;
	struct QNode* next;
};
//队列结构体 
struct Queue
{
	struct QNode* front;//头指针 
	struct QNode* rear;//尾指针 
};
//创建空队列 
struct Queue* CreateQueue()
{
	//给队列和节点分配空间 
	struct Queue* queue = (struct Queue*)malloc(sizeof(struct Queue));
	struct QNode* node = (struct QNode*)malloc(sizeof(struct QNode));
	node->next = NULL;
	queue->front = node;//头指针与尾指针都指向头节点 
	queue->rear = node;
	return queue;
}
bool isEmpty(struct Queue* q)
{
	return(q->front->next==NULL);//判断第一个节点是否为空 
}
//出队操作,删除第一个节点并返回节点数据 
int deQueue(struct Queue* q)
{
	if(isEmpty(q))
	{
		printf("队列为空,不可删除\n");
		return -1;
	}
	//注意!!!q->front->next才是第一个节点 
	struct QNode* frontCell = q->front->next;//保存第一个节点 
	int frontdata =  frontCell->data;//保存第一个节点的数据
	
	q->front = frontCell->next;//将头指针指向下一个节点
	free(frontCell);//释放空间 

	return frontdata; 
}
//入队操作,在队尾增加节点 
void enQueue(struct Queue* q,int data)
{
	//创建新节点 
	struct QNode* newNode = (struct QNode*)malloc(sizeof(QNode));//给节点分配空间 
	newNode->data = data;
	newNode->next = NULL;
	
	struct QNode* lastNode = q->rear;
	lastNode->next = newNode;//将尾节点的指针指向新节点 
	
	q->rear = newNode;//将尾指针指向新节点 
}
int main() {
    struct Queue* q = CreateQueue();//创建一个空队列 
	//初始化队列 
    for (int i = 0; i < 5; i++) {
        enQueue(q, i);
    }
    enQueue(q, 6);
	//通过节点来遍历队列 
    struct QNode* n = q->front->next;//第一个节点 
    while (n != NULL) {
        printf("队列元素为%d\n", n->data);
        n = n->next;
    }
    printf("出队元素为%d\n",deQueue(q));//出队操作 
	printf("头指针为%d,尾指针为%d\n",q->front->data,q->rear->data); 

    return 0;
}

2.3.3 循环队列

队列一般的顺序存储结构会发生假溢出(尾指针已经到了最后,头指针并不是从0开始,数组data并没有满,但是已经无法增加元素的情况),为了解决这一问题,引出一个循环队列循环队列是头尾相接的顺序存储结构。

循环队列在实现时,与一般队列的顺序存储结构不同的是,判断队满和队空的条件不同。

实现代码如下:

#include<stdio.h>
#include<stdlib.h>
#define MAX 100
struct Queue
{
	int data[MAX];//存数据 
	int front;//头指针
	int rear;//尾指针 
}; 
struct Queue* CreateQueue()//创建空列队 
{
	struct Queue* queue = (struct Queue*)malloc(sizeof(Queue));
	queue->front = 0;
	queue->rear = 0;
	return queue;
}
bool isFull(struct Queue* q)//判断队满 
{
	return((q->rear+1)%MAX==q->front);
}
void enQueue(struct Queue* q,int data)
{
	if(isFull(q))
		printf("队列满,不可再入队\n");
	else
	{
		q->data[q->rear] = data;
		q->rear = (q->rear+1)%MAX;
	}
}
bool isEmpty(struct Queue* q)
{
	return(q->front==q->rear);
}
int deQueue(struct Queue* q)
{
	if(isEmpty(q))
	{
		printf("队列为空,不可删除\n");
		return -1;
	}
	int dedata = q->data[q->front];
	q->front = (q->front+1)%MAX;
	return dedata;
}
int main()
{
	struct Queue* q = CreateQueue();
	for(int i=0;i<5;i++)
	{
		q->data[i]=i;
	}
	q->rear = 5;
	enQueue(q,6);
	for(int i=0;i<q->rear;i++)
	{
		printf("列队元素为%d\n",q->data[i]);
	}
	printf("出队元素为%d\n",deQueue(q));
	printf("头指针为%d,尾指针为%d\n",q->front,q->rear); 	
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值