数据结构:
定义:
数据结构是计算机组织,存储数据的方式。数据结构是指相互之间存在一种或多种特定关系的元素集合,大部分的数据结构的实现都需要借助C语言中的指针和结构体类型。
几种常见的数据结构:
1,线性数据结构:元素之间一般存在一对一关系,是最常用的一种数据结构,比如:数组,栈,队列和线性表。
2,树形数据结构:结点间具有层次关系,每一层的结点能且只能和上一层的一个结点相关,但同时可以和下一层的多个结点相关,称为一对多关系,比如:树,堆
3,图形结构:允许多个结点之间相关,被称为多对多关系,分为有向图和多向图。
下面对这几种数据结构做简单介绍:
数组:存放着一组相同类型的数据,需要预先指定数组的长度,有一维,二维,多维数组。
链表:是C语言中一种应用广泛的数据结构,它采用动态分配内存的方式实现,用一组任意的存储单元存放数据元素,一般为每个数据元素增加指针域,用来指向后继元素。
数组和链表的区别:
从逻辑结构来看:数组必须事先定义固定的长度,不能适应数据动态的增减的情况;链表进行动态的内存分配,可以适应数据动态的增减情况,且可以方便的插入删除数据项(数组中插入删除数据项时,需要移动其他数据项)
从内存存储来看:(静态)数组从栈中分配内存(用NEW分配在堆中),对于程序员方便快捷,但自由度小;链表在堆中分配内存,自由度大但申请管理比较麻烦
从访问方式来看:数组在内存中是连续存储的,因此可以利用下标索引进行随机访问;链表是链式存储结构,在访问数据的时候只能通过线性的方式由前到后顺序访问,所以访问效率比数组低。
栈、队列、线性表:可采取顺序存储和链式存储的方法进行存储。
顺序存储:借助数据元素在存储空间中的相对位置来表示元素之间的逻辑关系
链式存储:借助表示数据元素存储地址的指针表示元素之间的逻辑关系
栈:只允许在序列末端进行操作,栈的操作只能在栈顶进行,一般栈又被称为后进先出或先进后出的线性结构。
顺序栈:采用顺序存储结构的栈称为顺序栈,即需要用一片地址连续的空间来存储栈的元素,顺序栈的类型定义如下:
typedef struct{
ElemType *elem; //存储空间的基址
int top; //栈顶元素的下一个元素,简称栈顶位标
int size; //当前分配的存储容量
int increment; //扩容时增加的存储容量
}SqStack;
链栈:采用链式存储结构的栈称为链栈:
typedef struct LsNode{
ElemType data; //数据域
struct LSNode *next; //指针域
}LSNode, *LsStack; //结点和链栈类型
链栈使用C语言的实现如下所示:
/*
The realization of the stack.
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//Define the node struct.
typedef struct Node
{
int data; //Data.
struct Node *pNext; //Pointer the next node.
}NODE, *PNODE; //NODE is equivalent to Node,PNODE is equivalent to struct Node *.
//定义栈的结构体
typedef struct Stack
{
PNODE pTop; //栈顶结点
PNODE pBottom; //栈底结点
}STACK, *PSTACK;
//Function declarations
void initStack (PSTACK pStack); //初始化栈函数
void pushStack (PSTACK pStack, int val); //入栈函数
int popStack (PSTACK pStack, int *pVal); //出栈函数
void traverseStack (PSTACK pStack); //遍历栈的函数
int isEmpty (PSTACK pStack); //判断栈是否为空的函数
void clearStack (PSTACK pStack); //清空栈的函数
int main (void)
{
STACK stack; //定义一个栈变量
int val; //用来保存出栈的内容
initStack (&stack); //调用初始化栈函数
pushStack (&stack, 10); //调用入栈函数
pushStack (&stack, 20); //调用入栈函数
pushStack (&stack, 30); //调用入栈函数
pushStack (&stack, 40); //调用入栈函数
pushStack (&stack, 50); //调用入栈函数
traverseStack (&stack); //调用遍历栈的函数
//调用出栈的函数
if (popStack (&stack, &val))
printf ("出栈失败!\n");
else
printf ("出栈成功,出栈元素值为:%d.\n", val);
//调用清空栈的函数
clearStack (&stack);
traverseStack (&stack);
return 0;
}
void initStack (PSTACK pStack)
{
pStack->pTop = (PNODE)malloc(sizeof(NODE)); //创建一个空结点,让 pTOP 指向它
if (NULL != pStack->pTop)
{
pStack->pBottom = pStack->pTop; //将 pBottom 也指向空结点
pStack->pTop->pNext = NULL; //清空空结点的指针欲
}
else //如果分配内存失败
{
printf ("内存分配失败,程序退出!\n");
exit (-1);
}
return ;
}
void pushStack (PSTACK pStack, int val)
{
PNODE pNew = (PNODE)malloc (sizeof (NODE)); //动态创建一个新结点
pNew->data = val; //设置新结点的数据域的值
pNew->pNext = pStack->pTop; //将新结点的指针域指向之前的栈顶结点
pStack->pTop = pNew; //pTop 指向新的结点
return ;
}
int popStack (PSTACK pStack, int *pVal)
{
if (isEmpty (pStack))
return -1;
else
{
//先保存栈顶元素的地址,然后将 pTop 指向下一元素,最后释放之前栈顶元素的内存
PNODE tmpNode = pStack->pTop;
*pVal = pStack->pTop->data;
pStack->pTop = pStack->pTop->pNext;
free (tmpNode);
tmpNode = NULL;
}
return 0;
}
void traverseStack (PSTACK pStack)
{
PNODE tmpNode = pStack->pTop; //将栈顶赋给一个临时结点,因为在遍历栈的时候不能销毁栈
//循环遍历栈,直到栈底
while (tmpNode != pStack->pBottom)
{
printf ("%d ", tmpNode->data);
tmpNode = tmpNode->pNext;
}
printf ("\n");
return ;
}
int isEmpty (PSTACK pStack)
{
if (pStack->pTop == pStack->pBottom)
return -1;
else
return 0;
}
void clearStack (PSTACK pStack)
{
//栈为空则推出该函数
if (isEmpty (pStack))
return ;
else
{
PNODE p1 = pStack->pTop;
PNODE p2 = NULL;
//循环释放内存
while (p1 != pStack->pBottom)
{
p2 = p1->pNext;
free (p1);
p1 = p2;
}
//将栈顶和栈底指向同一个指针域为空的结点
pStack->pTop = pStack->pBottom;
}
return ;
}
队列:只允许在序列两端进行操作,一般队列也被称为先进先出的线性结构
循环队列:采用顺序存储结构的队列,需要按队列可能的最大长度分配存储空间,其类型定义如下:
typedef struct
{
Elemtype *base; //存储空间的基址
int front; //队头位标
int rear; //队尾位标,指示队尾元素的下一位置
int maxSize; //maxSize
}SqQueue;
链队列:采用链式存储结构的队列称为链队列,一般需要设置头尾指针:
typedef struct LQNode
{
Elemtype data; //数据域
struct LQNode *next; //指针域
}LQNode, *QueuePtr; //结点和链栈类型
typedef struct
{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LQueue; //栈队列类型
以下为链队列的实现:
/*
The realization of the queue.
*/
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct QNode
{
ElementType Element; //数据域
struct QNode *pNext; //指向下一个元素的指针域
}QNode, *QNodePtr;
typedef struct Node
{
QNodePtr front; //队列头
QNodePtr rear; //队列尾
}Node, *NodePtr;
ElementType isEmpty (NodePtr pNode); /*判断一个队列是否为空*/
NodePtr CreateQueue (void); /*创建一个队列*/
void DisposeQueue (NodePtr pNode); /*销毁队列*/
void MakeEmpty (NodePtr pNode); /*清空队列*/
void Enqueue (ElementType x, NodePtr pNode); /*入队*/
ElementType Front (NodePtr pNode); /*若队列不为空,返回头元素数据域*/
void Dequeue (NodePtr pNode); /*若队列不为空,则删除头元素*/
ElementType FrontAndDequeue (NodePtr pNode); /*返回队列中每个元素的数据域*/
ElementType isEmpty (NodePtr pNode)
{
return pNode->front == pNode->rear;
}
NodePtr CreateQueue (void)
{
NodePtr pNode;
pNode = (NodePtr)malloc (sizeof (Node));
pNode->front = pNode->rear = (QNodePtr)malloc (sizeof (QNode));
if (!pNode->front)
{
printf ("out of space! CreateQueue failed!\n");
return NULL;
}
pNode->front->pNext = NULL;
return pNode;
}
void MakeEmpty (NodePtr pNode)
{
if (NULL == pNode)
{
printf ("Must user CreateQueue first\n");
return ;
}
else
while (!isEmpty (pNode))
Dequeue (pNode);
}
void DisposeQueue (NodePtr pNode)
{
while (pNode->front)
{
pNode->rear = pNode->front->pNext;
free (pNode->front);
pNode->front = pNode->rear;
}
printf ("\nDispose queue completed!!!");
}
void Enqueue (ElementType x, NodePtr pNode)
{
QNodePtr p = (QNodePtr)malloc (sizeof (QNode));
if (!p)
{
printf ("Out of space!\n");
return ;
}
p->Element = x;
p->pNext = NULL;
pNode->rear->pNext = p;
pNode->rear=p;
}
ElementType Front (NodePtr pNode)
{
if (!isEmpty (pNode))
return pNode->front->pNext->Element;
return 0; /*Return value used to avoid warning*/
}
void Dequeue (NodePtr pNode)
{
if (!isEmpty (pNode))
{
QNodePtr p = (QNodePtr)malloc (sizeof (QNode));
if (!pNode)
{
printf ("Out of place!--Dequeue\n");
return ;
}
p = pNode->front->pNext;
pNode->front->pNext = p->pNext;
if (pNode->rear == p)
pNode->rear = pNode->front;
free (p);
}
}
/*
获取队列元素中的数据元素
*/
ElementType FrontAndDequeue (NodePtr pNode)
{
if (!isEmpty (pNode))
{
QNodePtr p = (QNodePtr)malloc (sizeof (QNode));
if (!p)
{
printf ("Out of space!--FrontAndDequeue\n");
return ;
}
p = pNode->front->pNext;
ElementType temp;
temp = p->Element;
pNode->front->pNext = p->pNext;
if (pNode->rear == p)
pNode->rear = pNode->front;
free (p);
return temp;
}
printf ("Empty queue!\n");
return 0; /*Return value used to avoid warning*/
}
int main (void)
{
int n, num, m;
int i;
NodePtr pNode = CreateQueue ();
printf ("initialization complete.\n");
if (isEmpty(pNode))
printf ("queue is empty!\n");
printf ("Please input the number of elements in the queue:\n");
scanf ("%d", &n);
printf ("Please input %d elements put in queue:", n);
for (i = 0; i < n; i++)
{
scanf ("%d", &num);
Enqueue (num, pNode);
}
printf ("Front element:%d.\n", Front(pNode));
printf ("Element is queue:");
while (!isEmpty (pNode))
printf ("%3d", FrontAndDequeue (pNode));
DisposeQueue (pNode);
printf ("\n");
return 0;
}