我考研时候的数据结构笔记

常用语法:

  1. 申请内存,并转换成Person

Person *p = (Person *) malloc(sizeof(Person));

  1. 定义结构体

typedef struct {

int age;

char *name;

}Person, *PersonP,Persons[20];

如果定义的时候就是类,那么想表示指针就得这么写:Person  *p

如果定义的时候就是指针,那么想表示指针就得这么写: Personp  p

  1. 赋值运算

Person p;

P.age = 12;

PersonP p1 = &p;

p1 -> age = 12;

  1. 定义常量

#define MAX_VALUE 255;

  1. 入参中 &l

 表示对l进行的修改可以拿到外面去

  1. 创建数组

int *p = (int *)malloc(5 * sizeof(int));

 

1 章  绪论

考试内容:

数据结构在程序设计中的作用;数据结构的主要内容;数据结构的基本概念;算法及算法分析。

考试要求:

1. 理解数据结构的基本概念;算法设计;掌握算法的时间和空间复杂度。

2. 掌握数据结构的定义;算法的描述方法。

数据结构包括:逻辑结构、存储结构、数据运算

算法描述方式:自然语言描述、伪代码、程序设计语言、流程图、SD

时间复杂度:语句被执行的次数

空间复杂度:算法消耗的存储空间

 

 

2章  线性表  

考试内容:

线性表的逻辑结构;线性表的顺序存储结构及实现;线性表的链接存储结构及实现;顺序表和链表的比较。

考试要求:

1. 掌握线性表的概念;线性表的顺序存储结构、链式存储结构及其常用算法。

2. 掌握线性表的逻辑结构;线性表的存储结构及操作的实现;链式存储结构及其常用算法;双向循环链表。

常用方法:

initList(&l);

length(l);

locateElem(l,e);

getElem(l,i);

listInsert(&l,i,e);

listDelete(&l,i,&e);

printList(l);

empty(l);

destroyList(&l);

线性表包括:顺序表和链表

#define MAX_SIZE 50

typedef struct{

ElementType data[MAX_SIZE];

int length;

}SqList;

15页的图

typedef struct LNode{

ElementType data;

struct LNode *next;

}LNode,*LinkList;

29页的图

顺序表和链表的比较:

1.顺序表可以顺序存取,也可以随机存取;链表只能顺序存取

2.顺序表的底层是数组,链表的底层是链表。相邻元素顺序表物理存储位置相邻,链表存储位置不相邻;

3.按值查找,顺序表无序时,顺序表和链表都是o(n)

            顺序表有序时,顺序表为o(log2n),链表为o(n)

 按位置查找,顺序表o(1),链表o(n)

  1. 空间分配: 链表多了指针的位置,所以存储密度小

 

双向循环链表

35页的图

3章 栈和队列 

考试内容:   基本运算。

2. 掌握递归的编程实现;循环队列和链队列的基本运算。

 

栈的名字Stack  后进先出

initStack(&s);

stackEmpty(s);

push(&s,x)

pop(&s,&x)

getTop(s,&x)

destroyStack(&s)

 

#define MAX_SIZE 50

typedef struct{

ElementType data[MAX_SIZE];

int top;

}SqStack;

 

top是栈顶的指针,初始值是-1,满栈条件是s.top == MAX_SIZE-1;

入栈:top+1,然后s.data[s.top] = x

出栈:x = s.data[s.top]s.top-1

66页的图

栈的表达式求值: 一棵树的中置表示(人类计算方式)、后置表示(计算机计算方式)

中置表示如何转变成后置表示?

当前是数字就进栈,当前是符号就出栈两个,并进行计算,结果进栈

 

递归可以用栈来转换

 

队列的名字Queue 先进先出

initQueue(&q)

queueEmpty(q)

enQueue(&q)

deQuque(&q)

getHead(q)

 

#define MAX_SIZE 50

typedef struct{

ElementType data[MAX_SIZE];

int front;

int rear;

}SqQueue;

 

初始时frontrear都是0,队尾进元素,队首出元素

入栈,rear位置添加,rear+1

出栈,front位置出值,front+1

78页的图

循环队列:使用取余%实现的

初始:front = rear = 0

添加一个元素 rear = (rear + 1)%MAX_SIZE

取出一个元素 front = (front + 1)%MAX_SIZE

队列长度:(rear-front + MAX_SIZE)%MAX_SIZE

判断队列空还是满?1.牺牲一个队列单元;2.增加元素个数字段;3.增加tag标识上一步是添加元素还是取出元素

79页的图

链队列

typedef struct{

ElementType data;

LNode *next;

}LinkNode;

typedef struct LinkQuquq{

LinkNode *front;

LinkNode *rear;

}LinkQueue;

81页的图

4章 字符串和多维数组

考试内容:

字符串;多维数组;矩阵的压缩存储

考试要求

1. 了解串的逻辑结构,存储结构。

2.掌握串定义和存储方法;串的操作。

 

字符串的模式匹配KMP算法

  1. 根据模求next数组

寻找当前位数前面的字符,首尾匹配,获得匹配数+1

如果前面的字符为空,则填写0

2.用模和被匹配字符串进行匹配,不匹配了就去找next数组对应的值,从这一位继续匹配

 

 

5章 树和二叉树

考试内容   

树的逻辑结构;树的存储结构;二叉树的逻辑结构;二叉树的存储结构及实现; 二叉树遍历的非递归算法;树、森林与二叉树的转换。

考试要求

1.了解树的基本概念;

2.理解二叉树的性质和存储结构;掌握遍历、构造二叉树和线索二叉树;

3.理解树的存储结构和遍历;掌握集合的一种表示方法;

4.掌握哈夫曼树及其应用;

 

树的术语:祖先、子孙、孩子、双亲、堂兄弟、度、叶子结点、终端结点、分支结点、非终端结点、根结点、深度、高度、有序树、无序树

二叉树的形态:空二叉树、只有根结点、只有左子树、只有右子树、左右子树都有

特殊的二叉树:满二叉树、完全二叉树、二叉排序树

计算:n0 = n2 + 1;度为0的结点 = 度为2的结点 + 1

存储结构:顺序存储和链式存储,顺序存储浪费空间,一般为链式存储

typedef struct BiTNode{

ElementType data;

BiTNode *left;

BiTNode *right;

}BiTNode,*BiTree;

二叉树的遍历:前序遍历、中序遍历、后序遍历、层序遍历

遍历方式需要为: 递归和非递归两种

void preOrder(BiTree b){

    if(b){

       visit(b);

       preOrder(b->lchild);

       preOrder(b->rchild);

}

}

前序遍历:构造栈,栈顶就是需要被访问的元素

void preOrder(BiTree b){

  initStack(s);

BiTree p =b;

  while(p || !emptyStack(s)){

  If(p){

 visit(p);

 push(s,p);

 p = p->lchild;

}else{

 pop(s,p);

 p = p->rchild;

}

}

}

中序遍历

void inOrder(BiTree b){

  initStack(s);

  BiTree p = b;

  While(p || !stackEmpty(s)){

     If(p){

        push(s,p);

        p = p->lchild;

}else{

  pop(s,p);

  visit(p);

p = p->rchild;

}

}

}

后续遍历

void postOrder(BiTree b){

  initStack(s);

  BiTree p = b;

  BiTree r = NULL;

  while(p || !emptyStack(s)){

     if(p){

       #有左子树就直接进栈

       push(s,p);

       p = p->lchild;

}else{

getTop(s,p)

  If(p->rchild && r != p->rchild){

    #存在右子树且右子树没有访问过

  p = p->rchild;

  push(p);

  p = p->lchild;

}else{

  #不存在右子树,或者右子树已经被访问过

  pop(s,p);

visit(p);

  r = p;

  p = NULL;

}

}

}

}

 

层序遍历

void levelOrder(BiTree b){

   initQueue(q);

   enQueue(q,b);

   while(!emptyQueue(q)){

      BiTree p = deQueue(q);

      visit(p);

      If(p->lchild){

         enQueue(q,p->lchild);

}

      If(p->rchild){

         enQueue(q,p->rchild)

}

}

}

树和森林和二叉树的转化:左孩子,右兄弟

构造二叉树的方式:

构造二叉树的意思是说:根据两种遍历的结果,获得二叉树的逻辑结构

  1. 先序遍历+中序遍历
  2. 后序遍历+中序遍历
  3. 层序遍历+中序遍历

查看142的案例

线索二叉树:如果当前结点没有左孩子,左孩子变成直接前驱;如果当前结点没有右孩子,右孩子变成直接后继

ltag=0,lchild表示左孩子

ltag =1,lchild指针表示前驱

rtag=0,rchild指针表示右孩子

rtag=1,rchild指针表示后继

typedef struct ThreadNode{

ElementType data;

int ltag;

int rtag;

struct ThreadNode *lchild;

struct ThreadNode *rchild;

}ThreadNode,*ThreadTree;

前序线索二叉树、中序线索二叉树、后序线索二叉树

如何画出中序线索二叉树?

  1. 中序排序
  2. 寻找空指针,指向直接前驱或者直接后继

代码实现中序线索二叉树(1.线索化;2.遍历)

 

void inThread(ThreadTree &p,ThreadTree &pre){

if(p!=NULL){

inThread(p->lchild,pre);

if(p->lchild == NULL){

    #第一个的左结点也被处理了

p->ltag = 1;

p->lchild = pre;

}

if(pre!=NULL && pre->rchild == NULL){

pre->rtag = 1;

pre->rchild = p;

}

pre = p;

inThread(p->rchild,pre);

}

}

//然后最后一个结点进行特殊处理

void createInThread(ThreadThree t){

ThreadTree pre = NULL;

If(t!=NULL){

inThread(T,pre);

pre->rchild = NULL;

pre->rtag = 1;

}

}

//中序线索二叉树的遍历

求中序遍历的第一个结点

ThreadNode * firstNode(ThreadNode *p){

while(p->ltag == 0){

p = p->lchild;

}

return p;

}

ThreadNode *nextNode(ThreadNode *p){

if(p->rtag == 0){

return firstNode(p->rchild)

}else{

return p->rchild;

}

}

 

void inOrder(ThreadNode *t){

for(ThreadNode *p = firstNode(t) ; p!=NULL ; p=nextNode(p)){

visit(p);

}

}

 

 

哈夫曼树:带权路径长度最短的树

  1. 将结点和权排在一起
  2. 选择权值最小的两个结点,连接起来,生成一个新的结点和权值
  3. 重复上面的步骤

查看190页的图

哈夫曼编码:用哈夫曼树的编码(可变长度)

01

查看190页的图

 

集合:并查集

S表示森林,Root1表示以Root1为根的树,Root2表示以Roo2为根的树

并:将Root1Root2合并变为Root1Root2作为Root1的孩子

查:找到元素x的根是谁

初始化:让S中所有的元素各自为一个子集

void init(int s[]){

for(int i=0;i<max_size;i++){

s[i] = -1;

}

}

 

void find(int s[],int x){

while(s[x] >=0){

x = s[x];

}

return x;

}

 

void union(int s[],int root1,int root2){

s[root2] = root[1]

}

 

 

6章 图    

考试内容

图的逻辑结构;图的存储结构及实现;最小生成树;最短路径;有向无环图及其应用。

考试要求

1.理解图的基本概念;图的存储结构;

2.掌握图的遍历及应用{最小生成树最短路径等};拓扑排序和关键路径。

 

G=(V,E);V表示顶点,E表示边

V={1,2,3}表示有三个顶点

E={<1,2>,<2,1>}表示有1221的有向边

E={(1,2)}表示12的无向边

连通、连通图、连通分量的概念:

顶点A和顶点B之间有路径存在,则称AB连通;

图中所有的顶点都是连通的,则称图为连通图;

一个图中的极大连通子图,叫做连通分量。

图的存储结构:临接矩阵法、临接表法、十字链表、临接多重表

邻接矩阵

typedef struct {

vertexType vex[MAX_SIZE];

edgeType edge[MAX_SIZE][MAX_SIZE];

int vexnum;

int arcnum;

}MGraph;

 

临接表法

边结点

typedef struct ArcNode{

int adjvex;

struct ArcNode *next;

}ArcNode;

顶点

Typedef struct VNode{

VertexType data;

ArcNode *first;

}VNode,AdjList[MAX_SIZE];

Typedef struct {

AdjList vertexList;

int vexNum;

int arcNum;

}AlGraph;

 

十字链表:(有方向图)

VNodedatainout

ArcNodeinDataoutDatainout

 

临接多重表:(无方向图)

VNodedatanext

ArcNodeinDatainoutDataout

 

图的基本操作:

getEdgeValue(G,x,y);

setEdgeValue(G,x,y);

insertVertex(G,x);

deleteVertex(G,x);

addEdge(G,x,y);

removeEdge(G,x,y);

neighbors(G,x);

firstNeighbor(G,x)

nextNeighbor(G,x,y);

adjacent(G,x,y);

 

图的遍历:广度优先和深度优先

下面是广度优先的算法,需要一个visited[]数组和一个queue来做辅助

bool visited[MAX_NUM] = false;

void bfsMain(Graph g){

for(int i=0;i<g.vertexNum;i++){

visited[i] = false;

}

for(i=0;i<g.vertexNum;i++){

if(!visited[i]){

bfs(g,i)

}

}

}

void bfs(Graph g,int v){

initQueue(q);

visit(v);

visited[v]=true;

enqueue(v);

while(!emptyQueue(q)){

dequeue(q,v);

for(int i = firstNeighbor(g,v);i>0;i=nextNeighbor(g,v,i)){

if(!visited(i)){

visit(i);

visited[i]=true;

enQueue(i);

}

}

}

}

广度优先算法分析:

时间复杂度是O(V+E)或者O(V平方)

空间复杂度是O(V)

 

深度优先:

bool visited[MAX_SIZE];

void dfsMain(Graph g){

for(int i=0;i<g.vertexNum;i++){

visited[i] = false;

for(i=0;i<g.vertexNum;i++){

dfs(g,i);

}

}

}

void dfs(Graph g,int v){

visit(v);

visited[v]=true;

for(int w = firstNeighbor(g,v);w>0;w=nextNeighbor(g,v,w)){

if(!visited[w]){

dfs(g,v);

}

}

}

深度优先算法分析:

时间复杂度是O(V+E)或者O(V平方)

空间复杂度是O(V)

 

最小生成树的算法:普里姆和克鲁斯卡尔

普里姆:1.任选一点;2.寻找当前图形的权最短的边的未连接的点,加入进来;无限循环

克鲁斯卡尔:每次都找最小权值的边,查看当前的边的两端点是否在树内,不在则加入进来

看书239

 

最短路径的算法:迪杰斯特拉和弗洛伊德

迪杰斯特拉P241

求顶点1到其余顶点的距离:

做表,纵坐标是顶点2345;集合

横左边是第一轮,第二轮,第三轮。。。

每次把上一轮确定进入集合的点加入到已知路径的中间;求得最短路径并加入集合

弗洛伊德看总结的案例

迪杰斯特拉和弗洛伊德的算法都是贪心算法,需要先求出当前的路径中经过的点,再把下一个点加入进来,需要整合计算

 

拓扑结构

给一个有向的树形结构,依次写出没有指向它的顶点。

 

关键路径

有四个量:事件最早发生时间、事件最迟发生时间、活动最早开始时间、活动最迟结束时间。

  1. 画图,圆圈和方块
  2. 圆圈画出事件的最早发生时间和最迟发生时间
  3. 方块画出活动的最早开始时间和最迟结束时间
  4. 做表

 

7章 查找技术   

考试内容

查找的基本概念、查找算法的性能;线性表的查找技术;树表的查找技术;散列表的查找技术

考试要求

1掌握顺序查找、折半查找和索引查找的方法

2.掌握二叉排序树的构造方法和二叉平衡树的建立方法

3.掌握哈希表的构造方法,哈希表在查找不成功时的平均查找长度的计算方法

 

顺序查找: 一般线性表的顺序查找、有序表的顺序查找、有序表的二分查找

typedef struct SSTable{

ElementType *data;

int length;

}SSTable;

 

int search(SSTable ss,ElementType key){

ss.data[0] = key;

for(int i = ss.length;ss.data[i]!=key;i--){

//哨兵 + 从后往前找

}

return i;

}

二分查找

int binarySearch(SeqList l,ElementType key){

int low = 0;int high = l.length-1;

int middle;

while(high >= low){

middle = ( low + high ) / 2;

if(l[middle] == key){

return middle;

}

if(l[middle] > key){

high = middle -1;

}else{

low = middle + 1;

}

}

return -1;

}

索引查找又叫做分块查找

 

散列表:包括hash函数和处理冲突的方式

常用的散列函数:直接地址法、除留余数法、数字分析法、平方取中法

处理冲突的方法:开放地址法,拉链法

散列表的性能比较:ASL和α

ASL1.先写出具体的数组;2.写出某个数查询的次数;3.ASL等于平均查找次数

α装填因子 = 表中记录数/数组的长度

 

二叉排序树和二叉平衡树

二叉排序树:中序排序,左子树均小于父结点,右子树均大于父结点

int search(BiTree t,ElementType key){

while(t!=null && t->data!=key){

if(t->data < key){

t = t->rchild;

}else{

t = t->rchild;

}

}

return t;

 

}

int insertBSTNode(BiTree &t,ElementType key){

if(t == NULL){

t = (BiTree *)malloc(sizeof(BiTree));

t->data = key;

t>lchild = NULL;

t->rchild = NULL;

return 1;

}

if(key == t->data){

return 0;

}else if(key > t->data){

return insertBSTNode(t->rchild,key);

}else if(key < t->data){

return insertBSTNode(t->lchild,key);

}

}

二叉排序树的删除操作:

1.如果删除叶子结点,则直接删除

2.如果删除的结点只有1个左孩子或者右孩子,则删除该结点后孩子顶替

3.如果删除的结点有左孩子和右孩子,则将直接前驱结点或者直接后继结点顶替

 

平衡二叉树

概念:结点的左子树和右子树高度差不超过1

插入:LL   LR  RL  RR四种情况

P186页  RLLR都是以RLLR为父结点,LLRR是以LR为父节点

 

 

8章 排序技术    

考试内容

排序的基本概念、排序算法的性能;插入排序;交换排序;选择排序;归并排序; 分配排序;各种排序方法的比较。

考试要求

1.掌握各类排序的原理和特征;

2. 掌握排序的各种算法实现和应用

 

排序的口诀:

选择插入归交基(排序的种类分为选择、插入、交换、归并、基数)

插入里有直半希(插入排序包括:直接插入、折半、希尔)

交换冒泡快排好(交换排序包括:冒泡、快排)

选出一堆简单栗(选择排序包括:简单选择、堆排)

 

稳定:

稳稳的幸福,鸡毛插归壳(插入、归并)

 

额外的存储:

有序插,无序快

 

O(nlog2n)

快以nlog2n归堆(快排,归并、堆排)

 

插入排序:将数分成三部分:已经排好序的、正在排序的、未排序的

 

快排:在一堆成绩里面第一次排序就可以得到张硕士的成绩排名,需要ijpivot三个指针,i在最左边,pivot在最左边,j在最有边。从j开始运动,ji的方向运动,ij的方向运动。当j碰到比pivot小的数就停止运动,且将该值放到i上,i碰到比pivot大的数就停止运动,且将该值放到j上。一直运动直到ij位置相等,此时将pivot赋值到ij位置上。Pivot的排名得以确定。

空间复杂度O(log2n)

 

堆排序:

堆排序分为大根堆和小根堆,大根堆就是父节点比子节点大

1.建堆

只处理分支结点的逆层序排序 O(n)

2输出堆结点,然后将最底层的结点放到根节点,然后建堆

3新增结点放到最底层,然后逆序排序

 

归并排序:

1先把两两一组排序

2.再把四个一组排序

3.再把八个一组排序

空间复杂度O(n)

 

基数排序:面向数字进行排序

比如n个三位数字的排序:

需要三次分配三次收集

每次分配需要n次,每次收集需要d10)次

posted @ 2022-09-12 21:19  纯丿乱  阅读(92)  评论(0编辑  收藏  举报