数据结构(c语言第2版)-----了解链表,栈,队列,串

时间:2023-03-08 19:55:08
数据结构(c语言第2版)-----了解链表,栈,队列,串

关于链表我觉得这都是最基本的东西,但是不常见,在实际的应用中很少的使用,了解它会用就OK,不需要研究的那么深,除非做那种内存压缩,存储方面工作。

C语言中动态申请空间

  • malloc()
	q=(dlink *)malloc(sizeof(dlink));

在内存空间不足或者栈满的情况下,就需要重新申请内存,此时可以使用malloc动态的申请栈,当无法知道内存具体位置的时候,想要绑定真在的存储空间,就需要用到动态的分配内存。malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。

  • realloc()
         slink* p=(slink*)realloc(sizeof(slink));   

先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

了解双链表

双链表顾名思义就是有两个链,也就是我们之前说的指针,官方说叫前驱和后继,这样就可以前后两个方向都可以指向。也方便我们存储数据。

指针域 数据域 指针域
/*双链表*/
typedef int ElemType;
typedef struct node{
ElemType data;
struct node *next; //后指针
struct node *prior; //前指针
}dlink; //建立双链表
dlink *credlink(int n){
dlink *head,*p,*s;int i;
p=head=(dlink *)malloc(sizeof(dlink)); //创建头节点
for(i=1;i<=n;i++){
s==(dlink)malloc(sizeof(dlink));
scanf("%d",&s->data);
s->prior=p;
p->next=s;
p=s;
}
p->next=head->prior=NULL;
return head;
}
//求表长操作
int genlen(dlink *head){
int i=0;dlink *p;
p=head->next;
while(p!=NULL){
i++;p=p->next;
}
return i;
}
//取元素操作 ----和之前单链表的是一样的。
int getelem(dlink *head,int i,ElemType *e){
int j;dlink *p;
if(i<1)return 0;
p=head->next;j=1;
while(p!=NULL&&j<i){
p=p->next;j++;
}
if(p==NULL)return 0;
*e=p->data;
return 1;
} //定位操作
dlink *locate(dlink *head,ElemType x){
dlink *p;
p=head->next;
while(p!=NULL&&p->data!=x){
p=p->next;
}
return p;
} //删除操作
int deletes(dlink *head,int i,ElemType *e){
dlink *p,*q;int j;
if(i<1)return 0;
p=head;j=0;
while(p->next!=NULL&&j<i-1){
p=p->next;j++;
}
if(p->next==NULL)return 0;
q=p->next;
p->next=q->next;
if(q->next!=NULL)q->next->prior=p;
*e=q->data;
free(q);
return 1;
} //插入操作
int insert(dlink *head,int i,ElemType x){
dlink *p,*q;int j;
if(i<1)return 0;
p=head;j=0;
while(p!=NULL&&j<i-1){
p=p->next;j++;
}
if(p==NULL)return 0;
q=(dlink *)malloc(sizeof(dlink));
q->data=x;
q->next=p->next;
q->prior=p;
if(p->next!=NULL)p->next->prior=q;
p->next=q;
return 1;
} //输出操作
void list(dlink *head){
dlink *p;
p=head;
while(p->next!=NULL){
p=p->next;
printf("%4d",p->data);
}
printf("\n");
while(p!=head){
printf("%4d",p->data);
p=p->prior;
}
printf("\n");
while(p!=head){
printf("%4d",p->data);
p=p->prior;
}
printf("\n");
}

了解栈

栈是一种特殊的线性表,它只能在一段进行插入和删除操作。插入端叫栈顶,另一端叫栈低,栈的特点是先进后出,就和死胡同一样,先进去的车最后才能出来。栈的插入叫做入栈(进栈,压栈),栈的删除操作称为出栈(退栈,弹栈);含有0个元素的栈叫做空栈。

栈是一个线性表,也具有线性表的顺序存储和链式存储两种存储结构,叫做顺序栈和链栈;根据栈的特点主要应用在语言处理和将递归算法改为非递归算法方面使用。

  • 顺序栈
/*顺序栈*/
#define INITSIZE 100 //存储空间的初始值
typedef int ElemType;
typedef struct{ //定义的结构体
int top; //表示栈顶指针
ElemType *base; //存放元素的动态数组空间
int stacksize; //当前栈的大小
}sqstack; //初始化顺序栈
void initstack(sqstack *S){
S->base=(ElemType *)malloc(INITSIZE *sizeof(ElemType));
S->top=0;
S->stacksize=INITSIZE;
}
//求栈长操作
int getlen(sqstack *S){
return (S->top); //直接就可以将栈顶的元素放回就OK了
}
//取栈顶元素
int gettop(sqstack *S,ElemType *e){
if(S->top==0)return 0;
*e=S->base[S->top-1];
return 1;
}
//压栈操作
int push(sqstack *S,ElemType x){
if(S->top>=S->stacksize){
S->base=(ElemType *)realloc(S->base,(S->stacksize+1) *sizeof(ElemType));
if(!S->base)return 0;
S->base[S->top++]=x;
return 1;
}
}
//弹栈操作
int pop(sqstack *S,ElemType * e){
if(S->top==0)return 1;
else
return 0;
}
//输出栈操作
void list(sqstack *S){
int i;
for(i=S->top-1;i>=0;i--)
printf("%4d",S->base[i]);
printf("\n");
}
  • 链栈操作

/*
链栈操作
*/
typedef int ElemType;
typedef struct node{
ElemType data;
struct node *next;
}linkstack; //和单链表定义一样。 //初始化操作
linkstack *initstack(void){
linkstack *S;
S=(linkstack *)malloc(sizeof(linkstack)); //申请空间
S->next=NULL;
return S;
}
//取栈顶操作
int gettop(linkstack *S,ElemType *e){
if(S->next==NULL)return 0;
*e=S->next->data;
return 1;
}
//求栈长操作
int getlen(linkstack *S){
linkstack *p;int i;
p=S->next;i=0;
while(p!=NULL){
i++;
p=p->next;
}
return i;
}
//入栈操作
int push(linkstack *S,ElemType x){
linkstack *p;
p=(linkstack *)malloc(sizeof(linkstack));
if(!p)return 0; //分配有问题,就返回0;
p->data=x;
p->next=S->next;
S->next=p;
return 1;
}
//出栈操作
int pop(linkstack *S,ElemType * e){
linkstack *p;
if(S->next==NULL) return 0;
p=S->next;
*e=p->data;
S->next=p->next;
free(p);
return 1;
}
//判断栈空
int emptystack(linkstack *S){
if(S->next==NULL)
return 1;
else
return 0;
}
//输出栈
void list(linkstack *S){
linkstack *p;
p=S->next;
while(p!=NULL){
printf("%4d",p->data);
p=p->next;
}
printf("\n");
}

了解队列

队列也使一种线性表,只容许在一端进行插入(队尾),另一端进行删除(队头),插入节点叫做入队,删除节点叫做出队。含有0个元素节点的队列叫做空队列。队列的特点是先进先出。

  • 循环队列
/*
循环队列
*/
#define MAXCSIZE 100;
typedef int ElemType;
typedef struct{
ElemType *base;
int front;
int rear;
}cqueue;
//初始化操作
void initqueue(cqueue *cq){
cq->base=(ElemType *)malloc(MAXCSIZE *sizeof(ElemType));
cq->front=cq->rear=0;
}
//求队列长操作
int getlen(cqueue *cq){
return ((cq->rear-cq->front+MAXCSIZE)%MAXCSIZE);
}
//取对头元素操作
int getfront(cqueue *cq.ElemType *e){
if(cq->front==cq->rear)return 0;
*e=cq->base[cq->front];
return 1;
}
//入队列操作
int enqueue(cqueue *cq,ElemType x){
if((cq->rear+1)%MAXCSIZE==cq->front)return 0;
cq->base[cq->rear]=x;
cq->rear=(cq->rear+1)%MAXCSIZE;
return 1;
}
//出队列操作
int outqueue(cqueue *cq,ElemType * e){
if(cq->front==cq->rear)return 0;
*e=cq->base[cq->front];
cq->front=(cq->front+1)%MAXCSIZE;
return 1;
}
//判队空操作
int emptyqueue(cqueue *cq){
if(cq->front==cq->rear)return 11;
else
return 0;
}
//输出操作
void list(cqueue *cq){
int i;
i=cq->front;
while(i!=cq->rear){
printf("%4d",cq->base[i]);
i=(i+1)%MAXCSIZE;
} }
  • 链式队列
/*
链队列
*/
typedef int ElemType;
typedef struct node{
ElemType data;
struct node *next;
}qlink;
typedef struct{
qlink *front;
qlink *rear;
}linkqueue;
//初始化操作
void initqueue(linkqueue *LQ){
LQ->front=LQ->rear=(qlink *)malloc(sizeof(qlink));
LQ->front->Next=NULL;
}
//求队列长度
int getlen(linkqueue *LQ){
int i;qlink *p;
p=LQ->front->Next;
i=0;
while(p!=NULL){
i++;p=p->Next;
}
return i;
}
//判队空操作
int emptyqueue(linkqueue *LQ){
if(LQ->front==LQ->rear)return 1;
else return 0;
}
//取对头元素操作
int getfront(linkqueue *LQ,ElemType *e){
if(LQ->front==LQ->rear)return 0;
*e=LQ->front->Next->data;
return 1;
}
//入队列操作
void enqueue(linkqueue *LQ,ElemType x){
qlink *p;
p=(qlink *)malloc(sizeof(qlink));
p->data=x;
p->Next=NULL;
LQ->rear->Next=P; //插入到队尾
LQ->rear=P;
}
//出队列
int outqueue(linkqueue *LQ,ElemType *e){
qlink *p;
if(LQ->front==LQ->rear)return 0;
p=LQ->front->Next;
*e=p->data;
LQ->front->Next=p->Next;
if(LQ->rear==p)
LQ->rear=LQ->front;
free(p);
return 1;
}
//输出队列操作
void list(linkqueue *LQ){
qlink *p;
p=LQ->front->Next;
while(p!=NULL){
printf("%4d",p->data);
p=p->Next;
}
}

了解串

串就是我们的字符串,是一种特殊的线性表,每个表中的元素是一个字符串,即串由零个或多个字符组成的有限序列,其中双引号不是里面的内容,主要是里面包含的值。串中任意连续的字符组成的子序列称为该串的子串包含子串的串叫做主串,字符在串中的位序称为该字符在串的位置。子串在主串中的位置是以子串的第1个字符在主串中的位置来表示的。若是两个串的长度相同,且在各个位置上的字符都相同,则称这个两个串相等。

顺序串是串的顺序存储结构,既串中的字符被依次存放在一组连续的存储单元中,一般来说一个字符占用一个字节的存储空间,因此,一个存储单元可以存储多个字符。

串的链式存储结构称为链串,链串中的一个结点可以存储一个字符,也可以存储多个字符。链串中的每个结点所存储的字符个数称为结点大小。链串的结点大小越大,则存储密度越大,但给一些操作(插入,删除,替换等)带来了不便,可能引起大量的字符移动。结点大小越小,操作处理越方便。但存储密度会下降。<串结点大小为1>

串的模式匹配:就是子串在主串中的定位操作。既在主串s中,从第pos个字符开始查找与子串t第1次相等的位置。若查找成功,则返回子串t的第1个字符在主串的位序。失败则返回0.

  • Brute-Force算法

基本思想:就是拿着子串在主串中挨着找,从子串的第一个开始,若和主串第一个相同则开始用子串的第二个比较主串的第二个,不同则拿着子串的第一个和主串的第二个,以此类推,主要的缺点是一些比较过的重复比较了,效率底下,目标串指针回溯消耗了大量的时间。时间复杂度:最好情况--->O(n+m),最坏情况--->O(mn);

  • KMP

思想:其实和之前的思想是一样的,只是在基础之上添加了过滤,就是没有了回溯,让比较过的不再重复比较。时间复杂度O(n+m);