再不记笔记我就是狗
文章目录
第零章 杂谈
-
这篇笔记的性质
因为某些原因,下学期会请半个月假,为了不耽误课程暑假决定预习,但想学这半个月课程,前面也得学啊,于是需要学一个月的课程。
而笔记以自用为主,记得很多点可能是很多人用不到到的,而笔记该有的概念性解释也没有。原因很简单,自用,如果有参考意义的话可以参考,没有的话拉倒。
-
链表
果然,我对链表是有一定的误解的。链表的头结点确实是特殊的,特殊在不作为元素结点使用。这一句卸载书中很不起眼的角落里,专门坑我这种看得快浅尝辄止的人。至于笔记下面因为这一点出问题的地方也懒得改了,问题不大。
第一章 绪论
略
第二章 基本线性结构
2.1线性表顺序存储
-
顺序表封装实现
typedef struct{ datatype data[MAXSIZE]; int last; }SeqList;
结构体中包括一个数组以及记录数组最后实际有效元素下标,如果实际元素有n个,那么last的值即为n-1。MAXSIZE则是指数组最大容量,用于创建数组。
-
初始化方法实现
SeqList *Init_SepList(){ SeqList *L=(SeqList *)malloc(sizeof(SeqList)); L->last=-1; return L; }
初始化方法包括以下几步:
- 创建顺序表类型指针,开辟大小为顺序表类型的存储空间并将其首地址强制转换为顺序表指针类型赋值给指针;
- 将该顺序表last属性初始化为-1;
- return该顺序表指针。
值得注意的地方是,该方法返回值为一指针,至于出于怎样的考虑暂时还没有涉及到(现在推测是方法传参数方便,可以少写“&”),但很类似面向对象语言的思想。
-
插入方法实现
int Insert_SeqList(SeqList *L,int i,datatype x){ int j; if(L->last+1==MAXSIZE){ printf("顺序表已满\n"); return (-1); } if(i<1||i>L->last+1){ printf("i的值不符合规范\n"); return 0; } for(j=L->last;j>=i-1;j--){ L->data[j+1]=L->data[j]; } L->data[i-1]=x; L->last++; return 1; }
插入方法包括以下几步;
- 根据last的值与MAXSIZE判断顺序表是否已满;
- 根据i与last检查插入位置有效性;
- 将插入位置之后元素从后至前依次后移一个单位;
- 将x赋值给下标为i-1的元素;
- last++。
-
删除方法实现
int Delete_SeqList(SeqList *L,int i){ int j; if(i<1||i>L->last+1){ printf("不存在第i个元素\n"); return 0; } for(j=i-1;j<=L->last;j++){ L->data[j]=L->data[j+1]; } L->last--; return 1; }
删除方法包括以下几步:
- 根据i与last检查删除位置有效性;
- 从第i个元素(下标为i-1)起,依次以后一个元素覆盖;
- last–。
-
按值查找方法实现
int Location_SeqList(SeqList *L,datatype x){ int i=0; while(i<=L->last&&L->data[i]!=x){ i++; } if(i>L->last){ printf("未找到该元素\n"); return 0; }else{ return i; } }
-
应用算法
顺序表划分以及顺序表合并。
2.2线性表的链式存储
-
单链表封装
typedef struct lnode{ datatype data; lnode *next; }LNode,*LinkList;
链表结构体中包含两个变量,分别是链表的值与下一个元素地址。有意思的是定义的方式,之前C语言结构体部分只是知道原理,具体实现没有好好学,typedef在Objective-C学习中也是已不报错为原则,所以也不太懂。直到看这段代码后才理解怎么用,而==*LinkList的原理仍然不是很清楚==,虽然影响不大,但是强迫症有点想搞清楚。
使用LinkList与LNode *创建出来的指针变量有什么区别?好吧,本质没有区别。但约定将指向链表首地址的指针称为LinkList,而指向中间某一元素的指针称为LNode *,增强程序可读性。将LinkList指针作为“链表类型”,将LNode *作为“链表元素指针类型”,不带星号视为普通类型,带星号视为指针类型。
-
单链表的建立实现
单链表建立分为在链表头部插入元素和在尾部插入元素两种方式(分别用来创建栈与队列?顺序遍历先进后出和先进先出?真不好意思,猜对了)。
//从头部插入 LinkList creat_LinkList1(){ LinkList L=NULL; LNode *s; datatype x; scanf("%datatpye",&x); while(x!=flag){ s=(LNode *)malloc(sizeof(LNode)); s->data=x; s->next=L; L=s; scanf("%datatype",&x); } return L; }
//从尾部插入 LinkList creat_LinkList(){ LinkList L=NULL; LNode *s,*r=NULL; datatype x; scanf("%datatpye",&x); while(x!=flag){ s=(LNode *)malloc(sizeof(LNode)); s->data=x; if(L==NULL){ L=s; }else{ r->next=s; } r=s; scanf("%datatype",&x); } if(r!=NULL){ r->next=NULL; } return L; }
创建链表两种方式归结重要步骤有以下几步:
- 创建大小为链表元素的存储空间,并强转为链表元素指针类型;
- 将值赋值给s指向的元素,即新添加的元素;
- 改变相关指针指向;
- 返回链表头指针。
需要注意的是,考虑到程序可读性,链表头指针以“链表类型”LinkList表示,元素指针以“链表元素类型指针”LNode *表示。
-
求表长实现
int Lengh_LinkList(LinkList L){ LNode *p=L; int j=0; while(p->next){ j++; p=p->next; } return j; }
教材里讲的带头结点不带头结点没明白是在指什么(了解了),但大概思路就是这样:
- 定义移动指针p、计数器j;
- p指向头结点;
- 遍历增加计数器;
- 返回计数器值。
-
按序号查找
//按序号查找 LNode *Get_LinkList(LinkList L,int i){ LNode *p=L; int j=0; while(p!=NULL){ p=p->next; if(j++==i){ return p; } } return NULL; }
对教材算法存疑,教材算法如若i=1,返回的会是第二个元素。了解了。
-
按值查找
//按值查找 LNode *Get_LinkList(LinkList L,datatype x){ LNode *p=L->next; while(p!=NULL){ if(p->data==x){ return p; } p=p->next; } return NULL; }
对书中算法存疑。 -
插入方法实现
//后插结点 s->next=p->next; p->next=s;
//前插结点 q=L; while(q->next!=p){ q=q->next; } s->next=q->next; q->next=s;
//插入方法 int Insert_LinkList(LinkList L,int i,datatype x){ LNode *p,*s; p=Get_LinkList(L,i-1); if(p==NULL){ printf("参数i错误\n"); return 0; }else{ s=(LNode *)malloc(sizeof(LNode)); s->data=x; s-next=p->next; p->next=s; return 1; } }
没想到的一点是调用Get_LinkList(),一年多面向对象算是白学了。
-
删除算法
算了,懒得写了。
-
循环链表与双向链表
同上。
-
关于链表的疑问
头结点,到底是怎么回事?里面不存东西的么?经常看到忽略头结点的data域(按值查找和循环链表链接)。
2.3堆栈
- 基本算法
- 栈初始化Init_Stack(S)
- 判栈空Empty_Stack(S)
- 入栈Push_Stack(S,x)
- 出栈Pop_Stack(S)
- 读栈顶元素Top_Stack(S)
2.3.1顺序栈
-
封装
#define MAXSIZE 1024 typedef struct{ datatype data[MAXSIZE]; int top; }SeqStack;
-
栈初始化
SeqStack *Init_Stack(){ SeqStack *s; s=(SeqStack *)malloc(sizeof(SeqStack)); s->top=-1; return s; }
突然想不明白的一点,为什么一定要用malloc,而不是直接创建一个结构体。
-
判栈空
int Empty_SeqStack(S){ if(s->top==-1) return 1; else return 0; }
-
入栈
int Push_SeqStack(SeqStack *s,datatype x){ if(s->top==MAXSIZE-1){ printf("栈满\n"); return 0; }else{ s->top++; s->data[s->top]=x; return 0; } }
-
出栈
int Pop_LinkList(SeqStack *s,datatype *x){ if(Empty_SeqStack(s)){ printf("栈为空\n"); return 0; }else{ *x=s->data[s->top]; s->top--; return 1; } }
-
取栈顶元素
略。
2.3.2链栈
-
封装
typedef struct sonde{ datatype data; struct snode *next; }StackNode,*LinkStack; LinkStack top=NULL;
链栈即单链表,但无需外附头结点,方便将头结点作为栈顶。相应,单纯的单链表外附头结点可以方便运算。或者说,链栈的top指针变量本身起到头结点作用。
-
入栈
LinkStack Push_LinkStack(LinkStack top,datatype x){ StackNode *p=(StackNode *)malloc(sizeof(StackNode)); p->data=x; p->next=top; top=p; return top; }
-
出栈
LinkStack Pop_LinkStack(LinkStack top,datatype *x){ if(top==NULL){ printf("栈为空\n"); return 0; }else{ StackNode *p=(StackNode *)malloc(sizeof(StackNode)); p=top; top=top->next; free(p); return top; } }
2.4队列
- 基本算法
- 队列初始化Init_Queue(Q):构造一个空队列;
- 入队In_Queue(Q,x):在O的队尾插入元素x;
- 出队Out_Queue(Q,x):删除队头元素赋值给x;
- 读队头元素Front_Queue(Q,x):读取Q队头元素赋值给x;
- 判队空Empty_Queue(Q):如空返回1。
2.4.1顺序队列
-
封装
typedef struct{ datatype data[MAXSIZE]; int front,rear; int num; }CSeQueue;
挺有意思的地方是避免假溢出的方法,构建了一个循环队列。当然,在封装定义里是看不出来的,只是多了num属性。
-
队列初始化实现
CSeQueue *Init_CSeQueue(){ CSeQueue *q=(CSeQueue *)malloc(sizeof(CSeQueue)); q->fromt=q->rear=MAXSIZE; q->num=0; return 0; }
-
入队实现
int In_CSeQueue(CSeQueue *q,datatype x){ if(q->num==MAXSIZE){ printf("队列满\n"); return -1; }else{ q->rear=(q->rear+1)%MAXSIZE; q->data[q->rear]=x; q->num++; return 1; } }
“q->rear=(q->rear+1)%MAXSIZE;”一句是实现循环队列的关键。至于先赋值还是先挪动尾指针,感觉影响都不大,但是需要和出队方法统一。
-
出队实现
int Out_CSeQueue(CSeQueue *q,datatype *x){ if(q->num==0){ printf("队列空\n"); return -1; }else{ q->front=(q->front+1)%MAXSIZE; *x=q->data[q->front]; q->num--; return 0; } }
与入队方法类似。
-
判队空及读队头元素
略。
2.4.2链队列
-
封装
typedef struct node{ datatype data; struct node *next; }QNode; typedef struct{ QNode *front,*rear; }LQueue;
-
链队列初始化实现
LQueue *Init_LQueue(){ QNode *p=(QNode *)malloc(sizeof(QNode)); LQueue *q=(LQueue *)malloc(sizeof(LQueue)); p->next=NULL; q->front=p; q->rear=p; return q; }
-
入队实现
void In_LQueue(LQueue *q,datatype x){ QNode *p=(QNode *)malloc(sizeof(QNode)); p->data=x; p->next=NULL; q->rear->next=p; q->rear=p; }
-
判队空
int Empty_LQueue(LQueue *q){ if(q->front==q->rear){ return 1; }else{ return 0; } }
-
出队实现
int Out_LQueue(LQueue *q,datatype *x){ if(Empty_LQueue(q)){ printf("队列空"); return 0; }else{ QNode *p=q->front->next; q->front->next=p->next; *x=p->data; free(p); if(q->front-next==NULL){ q->rear=q->front; } return 1;//教材中该句放在if语句块之中,怀疑是错误。 } }
- 链队列中的链表是带有头结点的。
- 教材中可能出现了些许错误(代码块注释句)。
第三章 线性结构的扩展
3.1字符串
}else{
return 0;
}
}
5. 出队实现
```C
int Out_LQueue(LQueue *q,datatype *x){
if(Empty_LQueue(q)){
printf("队列空");
return 0;
}else{
QNode *p=q->front->next;
q->front->next=p->next;
*x=p->data;
free(p);
if(q->front-next==NULL){
q->rear=q->front;
}
return 1;//教材中该句放在if语句块之中,怀疑是错误。
}
}
- 链队列中的链表是带有头结点的。
- 教材中可能出现了些许错误(代码块注释句)。