绪论
数据类型
数据类型是一个值的集合和定义在此集合上一组操作的总称。
(1)原子类型:其值不可再分的数据类型,如int,char,float。
(2)结构类型:其值可以再分解为若干成分的数据类型。
struct Student
{
long no;
char name[10];
float score;
};
(3)抽象数据类型(ADT):是指一个数学模型以及定义在该模型上的一组操作。
class Student
{
private:
long no;
char name[10];
float score;
public:
Student();
void findnumber(long no);
void findname(char name[]);
void Print();
};
数据结构及其三要素
在任何问题中,数据元素都不是孤立存在的,而是在它们之间存在着某种关系,这种数据元素之间的关系称为结构。数据结构是相互之间存在一种或多种特定关系的数据元素的集合。数据结构的三要素:逻辑结构、存储结构(物理结构)和数据的运算。
逻辑结构是指数据元素之间的逻辑关系,即从逻辑关系上描述数据。它与数据的存储无关,是独立于计算机的。数据的逻辑结构分为线性结构和非线性结构,线性表是典型的线性结构;集合、树和图是典型的非线性结构。
存储结构是指数据结构在计算机中的表示,也称物理结构。它包括数据元素的表示和关系。数据的存储结构是逻辑结构用计算机语言的实现。
数据运算是对数据的处理。包括:
(1)算术运算:加减乘除等运算。
(2)逻辑运算:或、且、非等运算。
(3)关系运算:大于、小于、等于、不等于等运算。
(4)数据传输:输入、输出、赋值等运算。
算法的定义
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰的”方法”,算法是用系统的方法描述和解决问题的策略。
五个特性
(1)有穷性(Finiteness):算法的有穷性是指算法必须能在执行有限个步骤之后终止;
(2)确切性(Definiteness):算法的每一步骤必须有确切的定义;
(3)输入项(Input):一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
(4)输出项(Output):一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
(5)可行性(Effectiveness):算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成(也称之为有效性)。
时间复杂度和空间复杂度
同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。
算法的时间复杂度是指执行算法所需要的计算工作量。一般来说,计算机算法是问题规模n 的函数f(n),算法的时间复杂度也因此记做:T(n)=Ο(f(n))。因此,问题的规模n 越大,算法执行的时间的增长率与f(n) 的增长率正相关,称作渐进时间复杂度(Asymptotic Time Complexity)。
算法的空间复杂度是指算法需要消耗的内存空间。其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示。同时间复杂度相比,空间复杂度的分析要简单得多。
简介
了解绪论中的相关概念后,接下来将学习数据结构中的线性表。数据结构中的线性表分为顺序表和链表,本篇博文先总结顺序表的操作和实现,在另一篇博文中再介绍链表的操作和实现。由于在数据结构中,栈和队列可以用顺序表实现,也能用链表实现。所以本篇先介绍顺序表,再介绍栈和队列的概念,最后给出相应的完整代码。由于代码比较多,所以链式结构的代码在下一篇博文中附上。
线性表的定义
线性表是具有相同数据类型的n(n >= 0)个数据元素的有限序列。其中n为表长,当n = 0时,该线性表是一个空表。若用L命名线性表,其一般表示为:L = (a1, a2, ..., ai, ai+1, ..., an)。其中a1是唯一的”第一个”数据元素,an是唯一的”最后一个”数据元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后继。
线性表的特点如下:
(1)表中元素的个数有限。
(2)表中元素具有逻辑上的顺序性,在序列中各元素排序有其先后次序,即为线性结构。
(3)表中元素都是数据元素,每一个元素都是单个元素。
(4)表中元素具有抽象性。即仅讨论元素间的逻辑关系,不考虑元素的内容。
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构。在接触一种新的数据结构类型时,都应该分别从其逻辑结构、存储结构和对数据的操作三方面着手。
线性表的顺序存储称为顺序表。它用的是一组地址连续的存储单元(数组),依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。顺序表的逻辑顺序与其物理顺序相同。顺序表最主要的特点是随机访问。即通过首地址和元素序号可以在O(1)的时间内找到指定的元素。顺序表的存储密度高,每个结点只存储数据元素。因为其元素物理上相邻,所以插入和删除操作需要移动大量元素。其存储类型可描述为:
#define MaxSize 50
typedef struct{
int data[MaxSize]; //顺序表元素
int length; //顺序表当前长度
}SqList;
栈的定义
栈:是只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在一端进行插入和删除操作。它的一个明显的操作特性可以概括为后进先出。
栈的顺序存储称为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时设置了一个指针(top)指向它的当前位置。栈的顺序存储类型可描述为:
#define Maxsize 50
typedef struct{
Elemtype data[Maxsize]; //存放栈中元素
int top; //栈顶指针
}Sqstack;
栈顶指针:S.top,初始化时设置S.top = -1;栈顶元素:S.data[S.top]。
进栈操作:先判断栈是否为满,若不满,栈顶指针先加1,再送值到栈顶元素。
出栈操作:先判断栈是否为空,若不空,先取栈顶元素值,再将栈顶指针减1.
栈空条件:S.top == -1; 栈满条件:S.top == Maxsize - 1; 栈长:S.top + 1。
由于顺序栈的入栈操作受数组上界的约束,当对栈的最大使用空间估计不足时,有可能发生栈上溢,所以要对栈是否为满进行判断。其实栈顶指针也可以设置为S.top = 0,只不过此时的操作也要发生相应的变化。当栈顶指针设置为-1,存放数据时要”先加后放”。而栈顶指针设置为0,存放数据时要”先放后加”。
基本操作:
int Push(Seqstack *S,Stackelemtype e) //入栈
{
if(Isfull(*S))
{
printf("Full!");
}
S->top++;
S->elem[S->top]=e;
return 1;
}
int Pop(Seqstack *S,Stackelemtype *e) //出栈
{
if(Isempty(*S))
{
printf("Empty!");
return 0;
}
*e=S->elem[S->top];
S->top--;
return 1;
}
int Gettop(Seqstack S,Stackelemtype *e) //获取栈顶元素
{
if(Isempty(S))
{
printf("Empty!");
return 0;
}
*e=S.elem[S.top];
return 1;
}
队列的定义
队列:简称为队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在另外一端进行删除。其操作特性是先进先出。
队列的顺序存储
队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针front和rear分别指向队头元素和队尾元素的位置。设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置。队列的顺序存储类型可描述为:
#define Maxsize 50
typedef struct{
Elemtype data[Maxsize]; //存放队列元素
int front,rear; //队头指针和队尾指针
}SqQueue;
这里需要了解循环队列的概念,将顺序队列想象成一个环状的空间,即把存储队列元素的表从逻辑上看成一个环。当队首指针Q.front = Maxsize - 1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现。
初始状态:Q.front = Q.rear = 0;
队首指针加1:Q.front = (Q.front + 1)%Maxsize;
队尾指针加1:Q.rear = (Q.rear + 1)%Maxsize;
为了区分队空和队满,有三种处理方式:
(1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以”队头指针在队尾指针的下一个位置作为队满的标志”。
队满条件为:(Q.rear + 1)%Maxsize == Q.front。
对空条件为:Q.front == Q.rear。
队列中元素的个数:(Q.rear - Q.front + Maxsize)%Maxsize。
(2)类型中增添表示元素个数的数据成员。这样,则队空的条件为Q.size == 0;队满的条件为Q.size == Maxsize。这两种情况都有Q.front == Q.rear。
(3)类型中添加tag数据成员,以区分是队满还是队空。tag等于0的情况下,若因删除导致Q.front == Q.rear则为队空;tag等于1的情况下,若因插入导致Q.front == Q.rear则为队满。
基本操作:
int Enter(SeqQueue *Q,Queueelemtype e) //入队
{
if((Q->rear+1)%MAXSIZE==Q->front)
{
printf("Queue is full!");
return 0;
}
Q->element[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
return 1;
}
int Delete(SeqQueue *Q,Queueelemtype *e) //出队
{
if(Q->front==Q->rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q->element[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return 1;
}
线性表 Linearlist.c
#include <stdio.h>
#define MAXSIZE 100
typedef int Elemtype;
typedef struct Linearlist
{
Elemtype elem[MAXSIZE];
int last;
}Seqlist;
/*这里有一点要注意,若传的参数是Seqlist *L,即传的是地址,就要写成
指针形式,L->last和L->elem[], 若传的是Seqlist L,即一个结构体类型,
就写成L.last和L.elem[]*/
void Initlist(Seqlist *L)
{
L->last=-1;
}
void Destroylist(Seqlist *L)
{
L->last=-1;
}
void Clearlist(Seqlist *L)
{
L->last=-1;
}
int Emptylist(Seqlist L)
{
if(L.last==-1)
{
return 1;
}
else
{
return 0;
}
}
int Lengthlist(Seqlist L)
{
if(L.last==-1)
{
return 0;
}
else
{
return (L.last+1);
}
}
int Locate(Seqlist L,Elemtype e) //传的参数是Seqlist L
{
int i=0;
while(i<=L.last&&L.elem[i]!=e)
{
i++;
}
if(i>L.last)
{
return -1;
}
else
{
return(i+1);
}
}
int Getdata(Seqlist L,int i)
{
if(0<i&&i<=(L.last+1))
{
return L.elem[i-1];
}
else
{
return -1;
}
}
int Insertlist(Seqlist *L,int i,Elemtype e) //传的是指针,写成L->last和L->elem[]
{
int k;
if((i<1)||(i>L->last+2))
{
return 0;
}
for(k=L->last;k>=i-1;k--)
{
L->elem[k+1]=L->elem[k];
}
L->elem[i-1]=e;
L->last++;
return 1;
}
int Deletelist(Seqlist *L,int i,Elemtype *e)
{
int k;
if(i<1||i>L->last+1)
{
return 0;
}
*e=L->elem[i-1];
for(k=i-1;k<L->last;k++)
{
L->elem[k]=L->elem[k+1];
}
L->last--;
return 1;
}
void Traverselist(Seqlist L)
{
int i=0;
if(!Emptylist(L))
{
for(;i<=(L.last);i++)
{
printf("%4d",L.elem[i]);
}
printf("\n");
}
}
void Merge(Seqlist *LC,Seqlist *LA,Seqlist *LB)
{
int i,j,l;
i=0;
j=0;
l=0;
while(i<=LA->last&&j<=LB->last)
{
if(LA->elem[i]<LB->elem[j])
{
LC->elem[l]=LA->elem[i];
l++;
i++;
LC->last++;
}
}
while(i<=LA->last)
{
LC->elem[l]=LA->elem[i];
i++;
l++;
LC->last++;
}
while(j<=LB->last)
{
LC->elem[l]=LB->elem[j];
l++;
j++;
LC->last++;
}
}
int main()
{
int i,e=0;
Seqlist L,LA,LB,LC;
Initlist(&L);
Initlist(&LA);
Initlist(&LB);
Initlist(&LC);
for(i=1;i<=20;i++)
{
Insertlist(&L,i,i);
Insertlist(&LA,i,i+10);
Insertlist(&LB,i,i+30*2);
}
Traverselist(L);
Traverselist(LA);
Traverselist(LB);
Deletelist(&L,3,&e);
printf("Deleted elem is:%4d\n",e);
Traverselist(L);
e=Locate(L,25);
printf("Get the index of elem:%4d\n",e);
e=Getdata(L,13);
printf("Get the figure of elem:%4d\n",e);
Merge(&LC,&LA,&LB);
Traverselist(LC);
return 0;
}
顺序表实现栈 Stack.c
#include <stdio.h>
#define Stacksize 50
typedef int Stackelemtype;
typedef struct Stack
{
Stackelemtype elem[Stacksize];
int top;
}Seqstack;
void Initstack(Seqstack *S)
{
S->top=-1;
}
void Clearstack(Seqstack *S)
{
S->top=-1;
}
int Isempty(Seqstack S)
{
if(S.top==-1)
{
return 1;
}
else
{
return 0;
}
}
int Isfull(Seqstack S)
{
if(S.top==Stacksize-1)
{
return 1;
}
else
{
return 0;
}
}
int Push(Seqstack *S,Stackelemtype e)
{
if(Isfull(*S))
{
printf("Full!");
}
S->top++;
S->elem[S->top]=e;
return 1;
}
int Pop(Seqstack *S,Stackelemtype *e)
{
if(Isempty(*S))
{
printf("Empty!");
return 0;
}
*e=S->elem[S->top];
S->top--;
return 1;
}
int Gettop(Seqstack S,Stackelemtype *e)
{
if(Isempty(S))
{
printf("Empty!");
return 0;
}
*e=S.elem[S.top];
return 1;
}
int main()
{
int i=1;
int c;
//定义栈
Seqstack S;
//初始化栈
Initstack(&S);
//入栈操作
printf("Input:");
while(1)
{
scanf("%d",&c);
if(c==999) break;
Push(&S,c);
}
//获得栈顶元素
Gettop(S,&c);
printf("The top element of stack is:[%d]\n",c);
//出栈操作
printf("Pop element:");
while(Isempty(S)!=1)
{
Pop(&S,&c);
printf("[%d]",c);
}
printf("\n");
return 0;
}
顺序表实现队列 Squeue.c
#include <stdio.h>
#define MAXSIZE 50
typedef int Queueelemtype;
typedef struct Squeue
{
Queueelemtype element[MAXSIZE];
int front;
int rear;
}SeqQueue;
void Init(SeqQueue *Q)
{
Q->front=Q->rear=0;
}
int Enter(SeqQueue *Q,Queueelemtype e)
{
if((Q->rear+1)%MAXSIZE==Q->front)
{
printf("Queue is full!");
return 0;
}
Q->element[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
return 1;
}
int Delete(SeqQueue *Q,Queueelemtype *e)
{
if(Q->front==Q->rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q->element[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return 1;
}
int Isempty(SeqQueue Q)
{
if(Q.front==Q.rear)
return 1;
else
return 0;
}
int Get(SeqQueue Q,Queueelemtype *e)
{
if(Q.front==Q.rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q.element[Q.front];
return 1;
}
int main()
{
int a;
SeqQueue Q;
Init(&Q);
printf("Input:");
while(1)
{
scanf("%d",&a);
if(a==999)
{
break;
}
Enter(&Q,a);
}
Get(Q,&a);
printf("Get the element of queue:");
printf("[%d]\n",a);
printf("Numbers of queue:");
while(Isempty(Q)!=1)
{
Delete(&Q,&a);
printf("[%d]",a);
}
printf("\n");
return 0;
}