写在前面的话

基于王道龙哥的代码写的笔记,为了加深印象,同时也为了后期复习。

初级阶段太简单了,中级阶段其实也简单,但以前没有写结构体的习惯,习惯直接把结构体拆成数组写,现在觉得还是挺方便的,主要也是为了这个才做的笔记,把一堆数据结构放在一起便于对比。高级阶段的什么文件、OS那块还没看,如果后期写了笔记还会补。

主要是面向我自己写的,不为教学也不为扩列,所以别指指点点,有哪里写的不对可以说,别在那balabala什么写的太浅,就是复制代码什么的。我完全为了自己,只是觉得发博客就是顺手的事。

主要就是将龙哥的代码模块化(毕竟代码长的话读起来也没有逻辑性,从main开始读还得跳来跳去),更改了一下代码顺序(按照逻辑思维顺序),以及分了一些层次,加了一些注释和解释。再然后就是把很多数据结构放一起好对比。

如果后期做了其他笔记也会发出来,欢迎交流

线性表

定义:n个相同类型元素组成的有序集合

1. 个数有限
2. 数据类型相同
3. 逻辑上有序

线性表的顺序表示

静态分配的数组(SqList)

数据结构

typedef struct{ElemType data[MaxSize];int length;//当前顺序表中有多少个元素
}SqList;

插入元素

​ 传入:结构体变量,插入位置,插入元素

​ 返回:ture or false

//i代表插入的位置(即序数=下标+1),从1开始,e要插入的元素(&为引用)
bool ListInsert(SqList &L,int i,ElemType e)
{if(i<1||i>L.length+1)//判断要插入的位置是否合法return false;if(L.length>=MaxSize)//超出空间了return false;for(int j=L.length;j>=i;j--)//移动顺序表中的元素L.data[j]=L.data[j-1];L.data[i-1]=e;//数组下标从零开始,插入第一个位置,访问的下标为0L.length++;return true;
}

删除元素

​ 传入:结构体变量,删除位置,元素类型变量(通过引用的方式得到输出值)

​ 返回:ture or false

//删除使用元素e的引用的目的是拿出对应的值
bool ListDelete(SqList &L,int i,ElemType &e)
{if(i<1||i>L.length)//如果删除的位置是不合法return false;e=L.data[i-1];//获取顺序表中对应的元素,赋值给efor(int j=i;j<L.length;j++)L.data[j-1]=L.data[j];L.length--;//删除一个元素,顺序表长度减1return true;
}

动态分配的数组(SeqList)

数据结构

typedef struct{ElemType *data;//数组的首地址(地址指针)int capacity;//动态数组的最大容量int length;//当前已存数量
}SeqList;

线性表的链式表示

有头结点的单链表(LNode,*LinkList)

数据结构

typedef struct LNode{ElemType data;struct LNode *next;//指向下一个结点
}LNode,*LinkList;

头插法新建链表

​ 传入:结构体变量(未初始化,所以需要引用)

​ 返回:结构体变量(可有可无)

LinkList CreatList1(LinkList &L)//list_head_insert
{LNode *s;int x;L=(LinkList)malloc(sizeof(LNode));//带头结点的链表L->next=NULL;//L->data里边没放东西scanf("%d",&x);//从标准输入读取数据//3 4 5 6 7 9999while(x!=9999){s=(LNode*)malloc(sizeof(LNode));//申请一个新空间给s,强制类型转换s->data=x;//把读取到的值,给新空间中的data成员s->next=L->next;//让新结点的next指针指向链表的第一个元素(第一个放我们数据的元素)L->next=s;//让s作为第一个元素scanf("%d",&x);//读取标准输入}return L;
}
#mermaid-svg-fxnQr9aiWdhH1H2K {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .error-icon{fill:#552222;}#mermaid-svg-fxnQr9aiWdhH1H2K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fxnQr9aiWdhH1H2K .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-fxnQr9aiWdhH1H2K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fxnQr9aiWdhH1H2K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fxnQr9aiWdhH1H2K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fxnQr9aiWdhH1H2K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fxnQr9aiWdhH1H2K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fxnQr9aiWdhH1H2K .marker.cross{stroke:#333333;}#mermaid-svg-fxnQr9aiWdhH1H2K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fxnQr9aiWdhH1H2K .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .cluster-label text{fill:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .cluster-label span{color:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .label text,#mermaid-svg-fxnQr9aiWdhH1H2K span{fill:#333;color:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .node rect,#mermaid-svg-fxnQr9aiWdhH1H2K .node circle,#mermaid-svg-fxnQr9aiWdhH1H2K .node ellipse,#mermaid-svg-fxnQr9aiWdhH1H2K .node polygon,#mermaid-svg-fxnQr9aiWdhH1H2K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fxnQr9aiWdhH1H2K .node .label{text-align:center;}#mermaid-svg-fxnQr9aiWdhH1H2K .node.clickable{cursor:pointer;}#mermaid-svg-fxnQr9aiWdhH1H2K .arrowheadPath{fill:#333333;}#mermaid-svg-fxnQr9aiWdhH1H2K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fxnQr9aiWdhH1H2K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fxnQr9aiWdhH1H2K .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-fxnQr9aiWdhH1H2K .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-fxnQr9aiWdhH1H2K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fxnQr9aiWdhH1H2K .cluster text{fill:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K .cluster span{color:#333;}#mermaid-svg-fxnQr9aiWdhH1H2K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fxnQr9aiWdhH1H2K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

L-malloc
NULL
L-malloc
NULL
s->data=x
L-malloc
s->data=x
NULL

尾插法新建链表

​ 传入:结构体变量(未初始化,所以需要引用)

​ 返回:结构体变量(可有可无)

LinkList CreatList2(LinkList &L)//list_tail_insert
{int x;L=(LinkList)malloc(sizeof(LNode));//带头节点的链表LNode* s, * r = L;//LinkList s,r=L;也可以,r代表链表表尾结点,指向链表尾部//3 4 5 6 7 9999scanf("%d",&x);while(x!=9999){s=(LNode*)malloc(sizeof(LNode));s->data=x;r->next=s;//让尾部结点指向新结点r=s;//r指向新的表尾结点scanf("%d",&x);}r->next=NULL;//尾结点的next指针赋值为NULLreturn L;
}
#mermaid-svg-mqU1xMX0jV8xIVBS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .error-icon{fill:#552222;}#mermaid-svg-mqU1xMX0jV8xIVBS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mqU1xMX0jV8xIVBS .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-mqU1xMX0jV8xIVBS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mqU1xMX0jV8xIVBS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mqU1xMX0jV8xIVBS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mqU1xMX0jV8xIVBS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mqU1xMX0jV8xIVBS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mqU1xMX0jV8xIVBS .marker.cross{stroke:#333333;}#mermaid-svg-mqU1xMX0jV8xIVBS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mqU1xMX0jV8xIVBS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .cluster-label text{fill:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .cluster-label span{color:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .label text,#mermaid-svg-mqU1xMX0jV8xIVBS span{fill:#333;color:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .node rect,#mermaid-svg-mqU1xMX0jV8xIVBS .node circle,#mermaid-svg-mqU1xMX0jV8xIVBS .node ellipse,#mermaid-svg-mqU1xMX0jV8xIVBS .node polygon,#mermaid-svg-mqU1xMX0jV8xIVBS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mqU1xMX0jV8xIVBS .node .label{text-align:center;}#mermaid-svg-mqU1xMX0jV8xIVBS .node.clickable{cursor:pointer;}#mermaid-svg-mqU1xMX0jV8xIVBS .arrowheadPath{fill:#333333;}#mermaid-svg-mqU1xMX0jV8xIVBS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mqU1xMX0jV8xIVBS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mqU1xMX0jV8xIVBS .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-mqU1xMX0jV8xIVBS .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-mqU1xMX0jV8xIVBS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mqU1xMX0jV8xIVBS .cluster text{fill:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS .cluster span{color:#333;}#mermaid-svg-mqU1xMX0jV8xIVBS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mqU1xMX0jV8xIVBS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

L-malloc,r
未初始化
L-malloc,r,r指向新节点
s->data=x
未初始化
L-malloc
s->data=x,r,r变为新节点
未初始化

双链表(DNode,*DLinkList)

数据结构

typedef struct DNode{ElemType data;struct DNode *prior,*next;//前驱,后继
}DNode,*DLinkList;

双向链表头插法(Dlist_head_insert)

DLinkList Dlist_head_insert(DLinkList &DL)
{DNode *s;int x;DL=(DLinkList)malloc(sizeof(DNode));//带头结点的链表,DL就是头结点DL->next=NULL;//前驱指针和后继指针都填写为NULLDL->prior=NULL;scanf("%d",&x);//3 4 5 6 7 9999while(x!=9999){s=(DLinkList)malloc(sizeof(DNode));s->data=x;s->next=DL->next;//新节点的后继 是 头结点的后继                 (1)离DL最远的指针(向外的)if(DL->next!=NULL)//第一个节点不需要(第一个节点时头结点没有后继)   {DL->next->prior=s;//头结点的后继的前驱 是 新节点             (2)离DL最远的往回的指针}s->prior=DL;//新节点的前驱 是 头结点                         (3)挨着DL的指针DL->next=s;//头结点的后继 是 新节点                           (4)挨着DL的指针scanf("%d",&x);//读取标准输入}return DL;
}

双向链表尾插法(Dlist_tail_insert)

DLinkList Dlist_tail_insert(DLinkList &DL)
{int x;DL=(DLinkList)malloc(sizeof(DNode));//带头节点的链表DNode *s,*r=DL;//r代表尾指针DL->prior=NULL;//初始化前驱,DL的前驱为空,后继会被赋值所以无所谓//3 4 5 6 7 9999scanf("%d",&x);while(x!=9999){s=(DNode*)malloc(sizeof(DNode));s->data=x;r->next=s;             //连接原尾巴和新节点s->prior=r;              //连接原尾巴和新节点r=s;//r指向新的表尾结点scanf("%d",&x);}r->next=NULL;//尾结点的next指针赋值为NULLreturn DL;
}

删除节点

bool DListDelete(DLinkList DL,int i)
{DLinkList p=GetElem(DL,i-1);if(NULL==p){return false;}DLinkList q;q=p->next;if(q==NULL)return false;p->next=q->next;//断链的操作if(q->next!=NULL)//q->next为NULL删除的是最后一个结点{q->next->prior=p;//断链的操作(只有删除的节点不是尾节点才执行)}free(q);return true;
}

循环双链表

静态链表

线性栈(SqStack)

数据结构

typedef struct {ElemType data[MaxSize];//数组int top;
}SqStack;
void InitStack(SqStack& S)
{S.top = -1;//代表栈为空
}

出入栈

//入栈
bool Push(SqStack& S, ElemType x)
{if (S.top == MaxSize - 1)//数组的大小不能改变,避免访问越界{return false;}S.data[++S.top] = x;return true;
}
//出栈
bool Pop(SqStack& S, ElemType& x)
{if (-1 == S.top)return false;x = S.data[S.top--];//后减减,x=S.data[S.top];S.top=S.top-1;return true;
}

数组实现的循环队列(SqQueue)

数据结构

typedef struct{ElemType data[MaxSize];//数组,存储MaxSize-1个元素int front,rear;//队列头 队列尾(队列头指向队列的第一个元素,队列尾指向可以装入的第一个位置)
}SqQueue;void InitQueue(SqQueue &Q)
{Q.rear=Q.front=0;
}

入队(EnQueue)

bool EnQueue(SqQueue &Q,ElemType x)
{if((Q.rear+1)%MaxSize==Q.front) //判断是否队满return false;Q.data[Q.rear]=x;//3 4 5 6Q.rear=(Q.rear+1)%MaxSize;return true;
}

出队(DeQueue)

bool DeQueue(SqQueue &Q,ElemType &x)
{if(Q.rear==Q.front)return false;x=Q.data[Q.front];//先进先出Q.front=(Q.front+1)%MaxSize;return true;
}

链表实现的循环队列(LinkQueue嵌套LinkNode)

数据结构

typedef struct LinkNode{ElemType data;struct LinkNode *next;
}LinkNode;
typedef struct{LinkNode *front,*rear;//链表头 链表尾
}LinkQueue;//先进先出void InitQueue(LinkQueue &Q)
{Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));//头和尾指向同一个结点Q.front->next=NULL;//头结点的next指针为NULL
}

判空

bool IsEmpty(LinkQueue Q)
{if(Q.front==Q.rear)return true;elsereturn false;
}

入队

//入队,尾部插入法
void EnQueue(LinkQueue &Q,ElemType x)
{LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));s->data=x;s->next=NULL;Q.rear->next=s;//rear始终指向尾部Q.rear=s;
}

出队

bool DeQueue(LinkQueue &Q,ElemType &x)
{if(Q.front==Q.rear) return false;//队列为空LinkNode *p=Q.front->next;//头结点什么都没存,所以头结点的下一个节点才有数据x=p->data;Q.front->next=p->next;//断链if(Q.rear==p)//删除的是最后一个元素(即判断要不要更新rear)Q.rear=Q.front;//队列置为空free(p);return true;
}

树的链式存储

二叉树建立和遍历

数据结构

一个数据结构(BiTNode,*BiTree)存树节点

一个数据结构(tag,*ptag_t)指向应当生孩子的叶子结点(用于建立完全二叉树)

typedef struct BiTNode{BiElemType c;//c就是书籍上的datastruct BiTNode *lchild;struct BiTNode *rchild;
}BiTNode,*BiTree;typedef struct tag{BiTree p;//树的某一个结点的地址值struct tag *pnext;
}tag_t,*ptag_t;

建立二叉树

     pnew=(BiTree)calloc(1,sizeof(BiTNode));//calloc申请空间并对空间进行初始化,赋值为0pnew->c=c;//数据放进去listpnew=(ptag_t)calloc(1,sizeof(tag_t));//给队列结点申请空间listpnew->p=pnew;//有多少个树节点,list就有多长,一指一if(NULL==tree)//建立第一个节点(根节点){tree=pnew;//树的根phead=listpnew;//队列头ptail=listpnew;//队列尾pcur=listpnew;//指向当前应当生孩子的节点continue;}else{ptail->pnext=listpnew;//新结点放入链表,通过尾插法ptail=listpnew;//ptail指向队列尾部}//pcur始终指向要插入的结点的位置if(NULL==pcur->p->lchild)//如果父节点的左子节点为空{pcur->p->lchild=pnew;//把新结点放到要插入结点的左边}else if(NULL==pcur->p->rchild)//如果父节点的右子节点为空{pcur->p->rchild=pnew;//把新结点放到要插入结点的右边pcur=pcur->pnext;//左右都放了结点后,pcur指向队列的下一个}

遍历二叉树

//abdhiejcfg  前序遍历,前序遍历就是深度优先遍历
void preOrder(BiTree p)
{if(p!=NULL){putchar(p->c);//等价于visit函数preOrder(p->lchild);preOrder(p->rchild);}
}
//中序遍历  hdibjeafcg
void InOrder(BiTree p)
{if(p!=NULL){InOrder(p->lchild);putchar(p->c);InOrder(p->rchild);}
}
//hidjebfgca  后序遍历
void PostOrder(BiTree p)
{if(p!=NULL){PostOrder(p->lchild);PostOrder(p->rchild);putchar(p->c);}
}
//中序遍历非递归,非递归执行效率更高,考的概率很低
void InOrder2(BiTree T)
{SqStack S;InitStack(S);BiTree p=T;while(p||!StackEmpty(S))//逻辑或||{if(p){//当一个结点不为空,压栈,并取左孩子Push(S,p);p=p->lchild;}else{//弹出栈中元素并打印,获取打印元素的右结点Pop(S,p);putchar(p->c);p=p->rchild;}}
}
//层次遍历,层序遍历,广度优先遍历
void LevelOrder(BiTree T)
{LinkQueue Q;//辅助队列InitQueue(Q);//初始化队列BiTree p;EnQueue(Q,T);//树根入队while(!IsEmpty(Q)){DeQueue(Q,p);//出队当前结点并打印putchar(p->c);if(p->lchild!=NULL) //入队左孩子EnQueue(Q,p->lchild);if(p->rchild!=NULL)  //入队右孩子EnQueue(Q,p->rchild);}
}

线索二叉树

不让子节点的指针空着,没有左孩子的时候左指针指向前驱,没有右孩子的时候右指针指向后继。

数据结构(ThreadNode)

typedef struct ThreadNode{ElemType data;struct ThreadNode *lchild,*rchild;int ltag,rtag;//ltag表示lchild是否指向前驱(or表示左孩子)//rtag表示rchild是否指向后继(or表示右孩子)
}ThreadNode,*ThreadTree;

构建线索二叉树(在已经构建好普通二叉树的基础上更新ltag、rtag、lchild、rchild)

中序遍历过程中,p为当前指针,pre为刚刚访问的指针(即p上一时刻的值)。

当p的左指针为空则指向pre,当pre的右指针为空就指向p。

void InThread(ThreadTree &p,ThreadTree &pre)
{if(p!=NULL){//若p为空直接返回//中序遍历,先遍历左节点,再判断p,再遍历右节点InThread(p->lchild,pre);//递归找树的左孩子if(p->lchild==NULL){//左边为NULL,填写当前结点的前驱p->lchild=pre;p->ltag=1;}if(pre!=NULL&&pre->rchild==NULL){//pre节点右孩子为NULL,就让其指向后继节点,而后继结点刚好就是ppre->rchild=p;pre->rtag=1;}pre=p;InThread(p->rchild,pre);}
}
void CreateInThread(ThreadTree T)
{ThreadTree pre=NULL;//使用辅助指针preif(T!=NULL){InThread(T,pre);//遍历完之后最后一个pre的后继节点为NULLpre->rchild=NULL;pre->rtag=1;}
}

二叉排序树

数据结构(BSTNode,*BiTree)

typedef struct BSTNode{KeyType key;struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;

插入节点(BST_Insert)

int BST_Insert(BiTree &T,KeyType k)//&T一方面可以直接更改根节点的地址(而不需要在函数外面申请),另一方面可以与return联系起来直接构建父子关系
{if(NULL==T){ //为新节点申请空间,第一个结点作为树根T=(BiTree)malloc(sizeof(BSTNode));T->key=k;T->lchild=T->rchild=NULL;return 1;//代表插入成功}else if(k==T->key)return 0;//发现相同元素,就不插入else if(k<T->key)//如果要插入的结点,小于当前结点return BST_Insert(T->lchild,k);//函数调用结束后,左孩子和原来的父亲会关联起来elsereturn BST_Insert(T->rchild,k);
}

搜索节点(BST_Search)

返回搜索到的指针(p无用)

BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{p=NULL;//存储要找的结点的父亲while(T!=NULL&&key!=T->key){p=T;if(key<T->key) T=T->lchild;//比当前节点小,就左边找else T=T->rchild;//比当前节点大,右边去}return T;
}

删除节点(DeleteNode)

void DeleteNode(BiTree &root,KeyType x){if(root == NULL){return;}if(root->key>x){//在左子节点下找DeleteNode(root->lchild,x);}else if(root->key<x){//在右子节点下找DeleteNode(root->rchild,x);}else{ //查找到了删除节点if(root->lchild == NULL){ //左子树为空,右子树直接顶上去BiTree tempNode = root;//用临时的存着的目的是一会要freeroot = root->rchild;free(tempNode);}else if(root->rchild == NULL){ //右子树为空,左子树直接顶上去BiTree tempNode = root;//临时指针root = root->lchild;free(tempNode);}else{  //左右子树都不为空//一般的删除策略是左子树的最大数据 或 右子树的最小数据 代替要删除的节点(这里采用查找左子树最大数据来代替)//BiTree tempNode = root->lchild;BiTree father = root;while(tempNode->rchild!=NULL){//这里是while,上课讲的原理没问题tempNode = tempNode->rchild;father = tempNode;}root->key = tempNode->key;//传值给删除的节点,然后把下面的节点删掉//DeleteNode(root->lchild,tempNode->key);DeleteNode(father,tempNode->key);//直接从father(tempNode的父节点)开始删除temp(不直接删除tempNode是因为需要置fathe->child=NULL)}}
}

查找

顺序查找(线性查找)

顺序表和链表都适用

for(i=ST.TableLen-1;ST.elem[i]!=key;--i);

​ 循环条件是没有查找到目标元素,且从后往前找:多申请的[0]号位置保存目标key值,防止内存越界,若返回0代表没有找到。

折半查找(二分查找)

仅适用于有序的顺序表

哈希查找(散列查找)

散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr

散列表:根据关键字而直接进行访问的数据结构

KMP字符串匹配

原理:首先找出目标字符串的“最长公共前后缀”,在当前字符串匹配失败时只需要移动使得前后缀匹配,而无须将标记回退。

排序

数据结构(动态申请数组)

typedef struct{ElemType *elem;int TableLen;
}SSTable;

交换排序

冒泡排序(BubbleSort)

for(i=0;i<n-1;i++)//i最多访问到8{flag=false;for(j=n-1;j>i;j--)//把最小值就放在最前面{if(A[j-1]>A[j]){swap(A[j-1],A[j]);flag=true;}}if(false==flag)return;}

快速排序

分治的思想

QuickSort

void QuickSort(ElemType A[],int low,int high)
{if(low<high){int pivotpos=Partition(A,low,high);//分割点左边的元素都比分割点要小,右边的比分割点大QuickSort(A,low,pivotpos-1);QuickSort(A,pivotpos+1,high);}
}

Partiton(挖坑法)——以左边为分隔值

int Partition(ElemType A[],int low,int high)
{ElemType pivot=A[low];//把最左边的值暂存起来while(low<high){while(low<high&&A[high]>=pivot)//让high从最右边找,找到比分割值小,循环结束--high;A[low]=A[high];//先从右边找(最开始的坑在左边,先填上)while(low<high&&A[low]<=pivot)//让low从最左边开始找,找到比分割值大,就结束++low;A[high]=A[low];}A[low]=pivot;return low;
}

Partition(交换法)——以右边为分隔值

int Partition(int* arr, int left, int right)
{int k, i;//k记录要放入比分割值小的数据的位置for (i = left, k = left; i < right; i++){if (arr[i] < arr[right])//i找到比分隔值小的值都会给k{swap(arr[k], arr[i]);//k遍历过的地方都是比分隔值小的值k++;}}swap(arr[k], arr[right]);//最后k的坐标就是分隔值return k;
}

插入排序

直接插入排序(n^2)

在已经排好序的序列中从后往前遍历,寻找插入位置([0]位置可以存放哨兵——插入值,故循环条件是便利值大于或小于key,且一边比较一边移动元素)

for(i=2;i<=n;i++)//第零个元素是哨兵,从第二个元素开始拿,往前面插入{if(A[i]<A[i-1]){A[0]=A[i];//放到暂存位置,A[0]即是暂存,也是哨兵for(j=i-1;A[0]<A[j];--j)//移动元素,内层循环控制有序序列中的每一个元素和要插入的元素比较A[j+1]=A[j];A[j+1]=A[0];//把暂存元素插入到对应位置}}

折半插入排序(n^2)

减少了比较次数,但没有减少数据移动次数。时间复杂度量级不变

for(i=2;i<=n;i++){A[0]=A[i];low=1;high=i-1;//low有序序列的开始,high有序序列的最后while(low<=high)//先通过二分查找找到待插入位置{mid=(low+high)/2;if(A[mid]>A[0])high=mid-1;elselow=mid+1;}for(j=i-1;j>=high+1;--j)A[j+1]=A[j];A[high+1]=A[0];}

希尔排序

第一趟排序完成步长为n/2的插入排序,第二趟完成步长为n/4的插入排序,直到完成步长为1的插入排序

for(dk=n/2;dk>=1;dk=dk/2)//步长变化,步长变化{for(i=dk+1;i<=n;++i)//以dk为步长进行插入排序{if(A[i]<A[i-dk]){A[0]=A[i];for(j=i-dk;j>0&&A[0]<A[j];j=j-dk)A[j+dk]=A[j];A[j+dk]=A[0];}}}

选择排序

简单选择排序

每一趟找到最小的交换即可,不需要每次都交换

for(i=0;i<n-1;i++)//最多可以为8{min=i;for(j=i+1;j<n;j++)//j最多可以为9{if(A[j]<A[min])min=j;}if(min!=i){swap(A[i],A[min]);}}

堆排序

0号元素不参与排序(根节点下标为1,左节点为2i,右节点为2i+1)

HeapSort——排序
void HeapSort(ElemType A[],int len)
{int i;BuildMaxHeap(A,len);//建立大顶堆for(i=len;i>1;i--){swap(A[i],A[1]);//依次将大根堆的根(最大元素)放到末尾AdjustDown(A,1,i-1);//重新调整根,使得仍然是大根堆}
}
BuildMaxHeap——建立大根堆
void BuildMaxHeap(ElemType A[],int len)
{for(int i=len/2;i>0;i--)//自下而上调整成大根堆(大根堆的调整需要子节点是大根堆),len/2是最后一个父节点{AdjustDown(A,i,len);}
}
AdjustDown——调整子树(for实现)
void AdjustDown(ElemType A[],int k,int len)//调整A[k],此时除了A[k]其他节点(除了末尾已经排好序外的)均满足大根堆
{int i;A[0]=A[k];//A[0]只是中转(根节点下标为1)for(i=2*k;i<=len;i*=2)//取左子节点{if(i<len&&A[i]<A[i+1])//左子节点与右子节点比较大小(取子节点中较大的那个)i++;if(A[0]>=A[i])//A[0]存的是A[k],如果当前节点已经比A[k]小,说明之后节点均比A[k]小,无需再调整break;else{A[k]=A[i];//否则:给父节点赋值成子节点中较大的那个k=i;//将子节点当做父节点继续考虑}}A[k]=A[0];//将原来的根节点赋值给最后确定的节点
}

所有元素参与排序(根节点下标为0,左节点为2i+1,右节点为2i+2)

HeapSort1——排序
void HeapSort1(ElemType A[], int len)
{int i;//建立大顶堆for (i = len / 2; i >= 0; i--)//这里不需要从len/2开始遍历,最大的父节点应该是(len-1)/2{AdjustDown1(A, i, len);}//开始排序swap(A[0], A[len]);//交换顶部和数组最后一个元素for (i = len - 1; i > 0; i--){AdjustDown1(A, 0, i);//剩下元素调整为大根堆swap(A[0], A[i]);}
}
AdjustDown1——调整子树(while实现)
void AdjustDown1(ElemType A[], int k, int len)
{int dad = k;int son = 2 * dad + 1; //左孩子下标while (son<=len){if (son + 1 <= len && A[son] < A[son + 1])//看下有没有右孩子,比较左右孩子选大的{son++;}if (A[son] > A[dad])//比较孩子和父亲{swap(A[son], A[dad]);dad = son;son = 2 * dad + 1;}else {break;}}
}

归并排序

先递归分隔——MergeSort

void MergeSort(ElemType A[],int low,int high)//递归分割
{if(low<high){int mid=(low+high)/2;MergeSort(A,low,mid);MergeSort(A,mid+1,high);Merge(A,low,mid,high);}
}

再归并排序——Merge

void Merge(ElemType A[],int low,int mid,int high)
{ElemType B[N];int i,j,k;for(k=low;k<=high;k++)//复制元素到B中B[k]=A[k];for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)//合并两个有序数组{if(B[i]<=B[j])A[k]=B[i++];elseA[k]=B[j++];}while(i<=mid)//如果有剩余元素,接着放入即可A[k++]=B[i++];while(j<=high)A[k++]=B[j++];
}

图的遍历——邻接表

##数据结构及宏定义

三层结构体:第一层是图,保存了边的数目和点的数目,以及N个节点的信息指针;第二层是节点,保存了节点的名字,以及其指向的所有节点(中的第一个节点的地址);第三层即链表中的节点。

#define MAX 100
#define isLetter(a)  ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)  (sizeof(a)/sizeof(a[0]))// 邻接表中表对应的链表的顶点
typedef struct _ENode
{int ivex;                   // 该边所指向的顶点的位置,是数组的下标struct _ENode *next_edge;   // 指向下一条弧的指针
}ENode, *PENode;// 邻接表中表的顶点
typedef struct _VNode
{char data;              // 顶点信息(顶点名字)ENode *first_edge;      // 指向第一条依附该顶点的弧
}VNode;// 邻接表
typedef struct _LGraph
{int vexnum;             // 图的顶点的数目int edgnum;             // 图的边的数目VNode vexs[MAX];
}LGraph;

主函数main()

void main()
{LGraph* pG;// 无向图自定义"图"(自己输入数据,输入的方法可以参考create_example_lgraph初始化好的数据)pG = create_lgraph(); 无向图的创建,采用已有的"图"pG = create_example_lgraph();//有向图的创建pG = create_example_lgraph_directed();// 打印图print_lgraph(*pG);//打印邻接表图DFSTraverse(*pG);//深度优先遍历BFS(*pG);//广度优先遍历system("pause");
}

一些工具函数

get_position()——返回ch在matrix矩阵中的位置

用于给定顶点的名字(如"a"),返回顶点对应的下标(如1),因为输入是面向显示的,内部运算是面向机器的,需要有name到数据的转变。

不过一般的题目中名字就是下标,不会考察转换。

static int get_position(LGraph g, char ch)
{int i;for(i=0; i<g.vexnum; i++)//去顶点结构体数组中遍历每个顶点if(g.vexs[i].data==ch)return i;//返回的是对应顶点的下标return -1;
}

read_char()——读取一个输入字符

是考虑到输入时都是字符和空格,挨个跳过太麻烦,所以循环跳过空格,只读字母。

static char read_char()
{char ch;do {ch = getchar();} while(!isLetter(ch));return ch;
}

link_last()——将node链接到list的末尾

static void link_last(ENode *list, ENode *node)
{ENode *p = list;while(p->next_edge)p = p->next_edge;p->next_edge = node;
}

create_lgraph()——根据输入数据创建图

返回值即为该图的地址

基本步骤:

①给pG清零(将所有顶点的指针指向NULL,虽然均会被覆盖)

②输入顶点数和边数,存入pG

③循环读入顶点信息(名字,如下标1的节点名字为a),并给每个节点的first_edge置为NULL(因为还没有读入边,此时默认图里无边)

④循环读入边的信息,将顶点名字转化为下标(大多数题目中下标即名字,这部分可以忽略)。申请两个node:(1)清零(置NULL)。(2)指向刚输入的节点下标。(3)分情况插入(考虑是不是插入的第一个节点)

LGraph* create_lgraph()
{char c1, c2;int v, e;int i, p1, p2;ENode *node1, *node2;LGraph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i].data = read_char();pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点printf("edge(%d): ", i);c1 = read_char();c2 = read_char();//输入的是名字,所以要根据名字找下标p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)calloc(1,sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)calloc(1,sizeof(ENode));node2->ivex = p1;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;
}

create_example_lgraph()——用内置的数据创建无向图

LGraph* create_example_lgraph()
{char c1, c2;char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'C'}, {'A', 'D'}, {'A', 'F'}, {'B', 'C'}, {'C', 'D'}, {'E', 'G'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);//上面类似一个邻接矩阵存储int i, p1, p2;ENode *node1, *node2;LGraph* pG;//pG表示图if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));//就是把申请的空间内初始化为零// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){pG->vexs[i].data = vexs[i];pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点c1 = edges[i][0];c2 = edges[i][1];p1 = get_position(*pG, c1);//p1对应起始顶点下标位置p2 = get_position(*pG, c2);//p1对应结束顶点下标位置// 初始化node1node1 = (ENode*)calloc(1,sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)calloc(1,sizeof(ENode));node2->ivex = p1;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;
}

create_example_lgraph_directed()——用内置的数据创建有向图

LGraph* create_example_lgraph_directed()
{char c1, c2;char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};char edges[][2] = {{'A', 'B'}, {'B', 'C'}, {'B', 'E'}, {'B', 'F'}, {'C', 'E'}, {'D', 'C'}, {'E', 'B'}, {'E', 'D'}, {'F', 'G'}}; int vlen = LENGTH(vexs);int elen = LENGTH(edges);int i, p1, p2;ENode *node1;LGraph* pG;if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){pG->vexs[i].data = vexs[i];pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点和结束顶点c1 = edges[i][0];c2 = edges[i][1];p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)calloc(1,sizeof(ENode));node1->ivex = p2;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);}return pG;
}

print_lgraph()——打印邻接表图

void print_lgraph(LGraph G)
{int i;ENode *node;printf("List Graph:\n");for (i = 0; i < G.vexnum; i++)//遍历所有的顶点{printf("%d(%c): ", i, G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL)//把每个顶点周围的结点都输出一下{printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);node = node->next_edge;}printf("\n");}
}

DFSTraverse()——深度优先搜索遍历图

无脑DFS

DFSTraverse()相当于是一个起点函数,定义标记数组然后初始化,接着直接循环DFS嵌套就行

static void DFS(LGraph G, int i, int *visited)
{ENode *node;visited[i] = 1;//要访问当前结点了,所以打印printf("%c ", G.vexs[i].data);node = G.vexs[i].first_edge;//拿当前顶点的后面一个顶点while (node != NULL){if (!visited[node->ivex])//只要对应顶点没有访问过,深入到下一个顶点访问DFS(G, node->ivex, visited);node = node->next_edge;//某个顶点的下一条边,例如B结点的下一条边}
}/** 深度优先搜索遍历图*/
void DFSTraverse(LGraph G)
{int i;int visited[MAX];       // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");//从A开始深度优先遍历for (i = 0; i < G.vexnum; i++){if (!visited[i])DFS(G, i, visited);}printf("\n");
}

广度优先搜索(类似于树的层次遍历)

开个队列就完了

void BFS(LGraph G)
{int head = 0;int rear = 0;int queue[MAX];     // 辅组队列int visited[MAX];   // 顶点访问标记int i, j, k;ENode *node;//每个顶点未被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;//从零号顶点开始遍历printf("BFS: ");for (i = 0; i < G.vexnum; i++)//对每个连同分量均调用一次BFS{if (!visited[i])//如果没访问过,就打印,同时入队,最初是A{visited[i] = 1;//标记已经访问过printf("%c ", G.vexs[i].data);queue[rear++] = i;  // 入队列}while (head != rear) //第一个进来的是A,遍历A的每一条边{j = queue[head++];  // 出队列node = G.vexs[j].first_edge;while (node != NULL){k = node->ivex;if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k].data);queue[rear++] = k;//类似于树的层次遍历,遍历到的同时入队}node = node->next_edge;}}}printf("\n");
}

考研笔记——王道C语言相关推荐

  1. 计算机语言学考研科目,语言学考研笔记整理(共16页)

    语言学考研笔记整理 一.语言和语言学 1.语言的本质 (1)自然属性:语言从本质上来说是一套符号系统. (2)社会属性:是人类最重要的交际工具. (3)心理属性:是人类进行思维的工具. 语言的自然属性 ...

  2. 计算机组成考研笔记(持续更新)

    书籍:计组王道考研复习书 CK: 基本每周更新一次,同时更新==计算机网络==的考研笔记 6.28日首发,第一章笔记+第二章大部分笔记 7.2日补充,第二章剩余笔记 7.9日第三次更新,第三章存储器层 ...

  3. 中传计算机学院考研笔记,纯干货|中传学霸笔记show。◕‿◕。

    原标题:纯干货|中传学霸笔记show。◕‿◕。 在"海底捞"大学 流传着一句话 "不会录音的摄像不是能策划的好导演" 中传学子可谓是卧虎藏龙 这背后与他们的苦练 ...

  4. 23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter

    23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter 2016-07-22 (www.cnblogs.com/icmzn) 模式理解 1. Adapter 定义 ...

  5. 数据结构源码笔记(C语言描述)汇总

    数据结构源码笔记(C语言):英文单词按字典序排序的基数排序 数据结构源码笔记(C语言):直接插入排序 数据结构源码笔记(C语言):直接选择排序 数据结构源码笔记(C语言):置换-选择算法 数据结构源码 ...

  6. 数据结构源码笔记(C语言):英文单词按字典序排序的基数排序

    //实现英文单词按字典序排序的基数排序算法#include<stdio.h> #include<malloc.h> #include<string.h>#defin ...

  7. 数据结构源码笔记(C语言):索引文件建立和查找

    //实现索引文件建立和查找算法#include<stdio.h> #include<malloc.h> #include<string.h> #include< ...

  8. 数据结构源码笔记(C语言):快速排序

    //实现快速排序算法 #include<stdio.h> #include<malloc.h> #define MAXE 20typedef int KeyType; type ...

  9. 数据结构源码笔记(C语言):冒泡排序

    //冒泡排序算法实现 #include<stdio.h> #include<malloc.h> #define MAXE 20typedef int KeyType; type ...

  10. 数据结构源码笔记(C语言):希尔插入排序

    //实现希尔插入排序算法 #include<stdio.h> #include<malloc.h> #define MAXE 20typedef int KeyType; ty ...

最新文章

  1. 人工智能三大驱动力背后的CMOS传感器
  2. 第一个django项目-通过命令行和pycharm两种方式
  3. ajax 载入html后不能执行其中的js解决方法
  4. 电压越低采集的ad值反而变大_80多条关于AD转换设计的经验总结
  5. 阿里巴巴 连接池 druid 的使用、maven依赖
  6. 会说话的狗狗本电脑版_一看就会用!Fotor 电脑版 图片后期处理详解系列教程 第一节...
  7. 路径规划-人工势场法(Artificial Potential Field)
  8. 3D结构光摄像头深度算法介绍
  9. 环信webIM即时通讯学习笔记
  10. Firebird学习(02):数据库的中文参考资料
  11. Appium+python自动化(七)- 初识琵琶女Appium(千呼万唤始出来,犹抱琵琶半遮面)- 上(超详解)...
  12. 目前常见的web网络安全漏洞
  13. 【LG-P4449】于神之怒加强版
  14. HTML5+CSS3小实例:篮球弹跳动画
  15. cmd bat 执行多条
  16. Windows 任务计划程序
  17. 嵌入式软件工程师就业方向有哪些呢?
  18. JavaSE学习(1)
  19. Goroutine并发调度模型深度解析之手撸一个协程池
  20. Linux操作系统安全加固指导

热门文章

  1. linux+qq+输入法下载官网,续:Linux下安装输入法和QQ软件
  2. BZOJ.3097 Hash Killer 1(卡掉自然溢出法)
  3. 【报表福利大放送】100余套报表模板免费下
  4. 计算机二级office查分数,3月份office计算机二级内网查分90,写下一点经验给后来人...
  5. 闰年c语言循环计算方法,C语言计算有多少闰年(答案原创)
  6. Alien Skin ExposureX8专业图像编辑器全新版本
  7. 同比和环比真正的区别
  8. 代码女神利用Python网络爬虫爬取淘宝评论区数据(用小本本记下来)
  9. Python简单示例-词频统计(分词)
  10. 2.1Mysql 基础--基本介绍